time
stdlib/extra/time.hk: actor-shaped time.
A pure-Hanki face over the sys native seam, like io/fs: sleep! plus the clock reads now_ms!/monotonic_ms!, with the [time] effect bubbling up through each delegating call. Timer patterns (send-after, backoff, periodic work) are no primitives at all, by design: they are plain actor code over sleep!, an actor that sleeps and then sends.
No hermetic test block belongs here: this module has no logic of its own, each function being a thin effectful delegation to the sys native seam, and exercising it needs real, non-deterministic OS state (a moving clock) that cannot be asserted in the pure stdlib test pack. Coverage is end-to-end through the CLI integration tests and example programs.
sleep!
def sleep!(ms: i32) -> () [time]
Block the calling actor for ms milliseconds; other actors keep running. Negative ms is treated as 0. Under --deterministic the sleep is virtual: it costs no real time and replays deterministically. @no-doctest: blocks on the clock, with no return value to assert
sleep_ns!
def sleep_ns!(ns: i64) -> () [time]
sleep! at the clock's full tick: block for ns nanoseconds, which makes a sub-millisecond pace expressible (a 16.667ms frame is sleep_ns!(16666667)). Negative ns is treated as 0; the same --deterministic virtualization applies, at nanosecond resolution. @no-doctest: blocks on the clock, with no return value to assert
sleep_until!
def sleep_until!(deadline_ns: i64) -> () [time]
Block until monotonic_ns! reads at least deadline_ns: the fixed-timestep idiom done once. An absolute deadline composes without drift, the next frame's deadline being deadline + frame and never now + frame, which follows a slow frame with a short sleep in place of pushing every later frame back. A past deadline returns immediately. The 60fps skeleton:
frame = 16666667 var deadline = time.monotonicns!() + frame loop ...simulate and render... time.sleepuntil!(deadline) deadline += frame end
Under --deterministic the sleep is virtual like the rest. @no-doctest: blocks on the clock, with no return value to assert
now_ms!
def now_ms!() -> i64 [time]
Wall-clock milliseconds since the Unix epoch. Under --deterministic this reads the gate's virtual clock, and a time-printing program still replays the same bytes for the same seed. @no-doctest: reads the live clock; the result is host-dependent
monotonic_ms!
def monotonic_ms!() -> i64 [time]
Monotonic milliseconds since an unspecified fixed origin. It never goes backward, which makes it the clock for measuring durations; the absolute value has no meaning. Same [time] gate and --deterministic virtualization. @no-doctest: reads a clock whose absolute value has no meaning
monotonic_ns!
def monotonic_ns!() -> i64 [time]
monotonic_ms! at the clock's full tick: nanoseconds since the same origin, for benchmarks and frame budgets a millisecond read cannot resolve (a 2ms cold start has zero significant digits in ms). i64 nanoseconds hold ~292 years of process uptime. @no-doctest: reads a clock whose absolute value has no meaning
localoffsetseconds_at!
def local_offset_seconds_at!(epoch_second: i64) -> Option<i32> [time]
Seconds east of UTC that the host's local time runs at epoch_second (7200 on a machine set to +02:00). Per instant because a zone observing daylight saving answers differently either side of a transition; None when the host cannot place that second on its local calendar. datetime.local_offset_at! is the typed face over this. Under --deterministic it answers UTC, like the clock reads. @no-doctest: reads the host's timezone; the result is host-dependent