std.time
Getting the current time
now() // milliseconds since Unix epoch
timestamp() // seconds since Unix epoch
date() // {year, month, day, hour, minute, second, weekday, yearday}
format(ms[, fmt]) // default: %Y-%m-%d %H:%M:%S
parse(str, fmt) // date string -> milliseconds (C++ chrono specifiers)
elapsed(start_ms) // ms elapsed since start_ms
var t = std.time.now()
print(std.time.format(t)) // 2026-08-31 12:00:00
var d = std.time.date()
print(d.year, d.month, d.day)
Unit conversion
nanoseconds(n) microseconds(n) milliseconds(n)
seconds(n) minutes(n) hours(n) days(n)
to_seconds(ms) to_minutes(ms) to_hours(ms) to_days(ms)
The * (n) helpers convert a number of that unit to milliseconds; the
to_* (ms) helpers convert milliseconds to that unit (integer division —
results truncate toward zero).
std.time.seconds(90) // 90000 (ms)
std.time.to_minutes(120000) // 2
Calendar
calendar([year][, month]) returns an object describing a month for planning
and display:
var cal = std.time.calendar(2026, 3)
The object has these fields:
| Field | Meaning |
|---|---|
year, month | The requested year and 1-based month |
month_name | e.g. "March" |
days_in_month | Number of days (handles leap years) |
first_weekday | Weekday of the 1st, Monday = 1 … Sunday = 7 |
today | Day-of-month when the requested month is the current one, else nil |
weeks | Rows of day numbers, nil-padded to seven slots |
print(cal.month_name, cal.year) // March 2026
print(cal.first_weekday) // 7 (1st is a Sunday)
print(cal.weeks[0]) // [nil, nil, nil, nil, nil, nil, 1]
weeks lays out the month as Monday-first rows, padding with nil before the
1st and after the last day:
// March 2026, first_weekday = 7
[
[nil, nil, nil, nil, nil, nil, 1],
[2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15],
[16, 17, 18, 19, 20, 21, 22],
[23, 24, 25, 26, 27, 28, 29],
[30, 31, nil, nil, nil, nil, nil],
]
Render it into a terminal grid by mapping each week's nil padding to blanks:
var cal = std.time.calendar(2026, 3)
print(cal.month_name, cal.year)
print("Mo Tu We Th Fr Sa Su")
for (week : cal.weeks) {
var line = ""
for (d : week) {
if (d == nil) {
line += " "
} else {
line += (d < 10) ? " " + d + " " : d + " "
}
}
print(line)
}
This prints:
March 2026
Mo Tu We Th Fr Sa Su
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
No arguments means the current month, and today is then set to today's
day-of-month (so you can highlight it in the grid).
Timers
timer schedules a callback through the async scheduler:
var t = std.time.timer({
callback: fn() { print("tick") },
duration: 1000, // ms
repeat: true,
})
- Non-repeating timers return a future —
awaitit for the result. - Repeating timers return a handle with
cancel()/start()/last_error().
Getting the last error
Repeating-timer callbacks run on background threads with no caller to receive
an exception, so each timer records the most recent callback failure on its
own handle and exposes it through handle.last_error() (returns null
when there is none). It is echoed to stderr as well, so it is visible even if
you never query it.
var t = std.time.timer({
callback: fn() { does_not_exist },
duration: 20,
repeat: true,
})
std.async.sleep(80) // let the tick fire once
var err = t.last_error() // "Unknown variable: does_not_exist"
if (err != null) {
print("timer failed:", err)
}
Which surface an error takes depends on the timer kind:
| Timer kind | Where the error goes |
|---|---|
| Non-repeating | The returned future — re-thrown when you await() it (as Async error: <msg>) |
| Repeating | Recorded on that timer's handle via handle.last_error(); each tick keeps running |
// Non-repeating: the error surfaces on await, not last_error()
var f = std.time.timer({
callback: fn() { does_not_exist },
duration: 10,
})
std.async.await(f) // RUNTIME_ERROR: Async error: Unknown variable: does_not_exist
last_error() is not cleared when read and holds only the most recent
message, so a repeating timer's latest failure overwrites its previous one.
Each timer keeps its own slot — one timer failing does not mask another's error.