module time
module lib/time.mu
import "time"
time provides helpers for time-related operations.
Imports
Functions
date_from_days#
date_from_days returns [year, month, day] for days since 1970-01-01.
Source lib/time.mu:166
fn date_from_days(days) {
return _date_from_days(days)
}days_in_month#
days_in_month reports the number of days for the given year/month (handles leap years).
Source lib/time.mu:171
fn days_in_month(year, month) {
return _days_in_month(year, month)
}format#
format returns an ISO‑8601 timestamp (UTC) for the provided Unix epoch seconds.
Source lib/time.mu:150
fn format(epoch) {
dm := math.div_mod(epoch, 86400)
days := dm[0]
secs_of_day := dm[1]
date := _date_from_days(days)
year := date[0]
month := date[1]
day := date[2]
hour := secs_of_day / 3600
minute := (secs_of_day % 3600) / 60
second := secs_of_day % 60
return text.pad_number(year, 4) + "-" + text.pad_number(month, 2) + "-" + text.pad_number(day, 2) +
"T" + text.pad_number(hour, 2) + ":" + text.pad_number(minute, 2) + ":" + text.pad_number(second, 2)
}is_leap_year#
is_leap_year reports whether the provided year is a leap year per the Gregorian rule.
Source lib/time.mu:176
fn is_leap_year(year) {
return _is_leap_year(year)
}monotonic_ns#
monotonic_ns returns a monotonic clock reading in nanoseconds: it moves only forward and survives wall-clock adjustments, so it is the right clock for measuring elapsed time. Linux only -- darwin has no monotonic clock reachable as a syscall, and answering wall time here would be a lie a benchmark cannot detect, so it answers an error there instead.
Source lib/time.mu:65
fn monotonic_ns() {
platformKey := platform()
if platformKey == "darwin/amd64" || platformKey == "darwin/arm64" {
return error("time.monotonic_ns: no monotonic clock syscall on " + platformKey)
}
b := _make_zero_buffer(16)
result := syscall("clock_gettime", 1, b)
if is_error(result) {
return result
}
return _time_word(b, 0, 8) * 1000000000 + _time_word(b, 8, 8)
}now_ms#
now_ms returns the current Unix time in milliseconds.
Source lib/time.mu:52
fn now_ms() {
ns := now_ns()
if is_error(ns) {
return ns
}
return ns / 1000000
}now_ns#
now_ns returns the current Unix time in NANOSECONDS. This is the first clock in the language finer than a second: before it, an elapsed interval under 1s measured as 0 and every process started within the same second seeded rand identically.
clock_gettime(CLOCK_REALTIME) on linux -- the asm-generic ABI has no gettimeofday with a writable timeval in its modern form and clock_gettime is the one clock every linux has -- and gettimeofday on darwin, whose kernel has no clock_gettime syscall (libc fakes one in userspace), so the resolution there is the timeval's microseconds.
Source lib/time.mu:32
fn now_ns() {
platformKey := platform()
b := _make_zero_buffer(16)
if platformKey == "darwin/amd64" || platformKey == "darwin/arm64" {
result := syscall("gettimeofday", b, 0)
if is_error(result) {
return result
}
// struct timeval: tv_sec int64 at 0, tv_usec int32 at 8.
return _time_word(b, 0, 8) * 1000000000 + _time_word(b, 8, 4) * 1000
}
result := syscall("clock_gettime", 0, b)
if is_error(result) {
return result
}
// struct timespec: tv_sec at 0, tv_nsec at 8.
return _time_word(b, 0, 8) * 1000000000 + _time_word(b, 8, 8)
}sleep#
sleep pauses execution for the requested number of seconds, using platform syscalls plus a fallback busy-wait.
The duration may be an INTEGER or a Decimal: both syscalls underneath take a seconds/sub-seconds pair, so sleep(0.15) is representable and sleeps for 150ms. It used to reject anything non-INTEGER with an ERROR value, which nobody checks on a sleep, so a fractional sleep silently did nothing at all.
Source lib/time.mu:119
fn sleep(seconds) {
if !typing.is_int(seconds) && type(seconds) != "Decimal" {
return error("time.sleep expects a number of seconds, got " + type(seconds))
}
if seconds < 0 {
return error("time.sleep expects a non-negative duration")
}
platformKey := platform()
if platformKey == "linux/amd64" || platformKey == "linux/arm64" ||
platformKey == "linux/riscv64" {
parts := split_duration(seconds, 1000000000)
req := _make_timespec(parts[0], parts[1])
syscall("nanosleep", req, 0)
return nil
}
if platformKey == "darwin/amd64" || platformKey == "darwin/arm64" {
parts := split_duration(seconds, 1000000)
req := _make_timeval(parts[0], parts[1])
syscall("select", 0, 0, 0, 0, req)
return nil
}
// Fallback for a platform with neither syscall. time() has one-second
// resolution, so a fractional duration is only honoured to the extent the
// clock can see it.
target := time() + seconds
while time() < target {
}
return nil
}split_duration#
split_duration splits a duration in seconds into whole seconds plus the sub-second remainder expressed in unitths of a second (10^9 for a timespec's nanoseconds, 10^6 for a timeval's microseconds).
An INTEGER passes straight through with a zero remainder, so sleep(0) stays exactly zero -- §4.4 makes a zero-duration sleep a pure yield, and a stray nanosecond would turn it into a real one. A Decimal is truncated toward zero: a sub-unit tail is dropped rather than rounded up, matching what the kernel would do with the digits it cannot represent.
The Decimal round-trips through .str() because int() does not accept a Decimal directly, and the syscall staging buffers take INTEGERs.
Source lib/time.mu:103
fn split_duration(seconds, unit) {
if typing.is_int(seconds) {
return [seconds, 0]
}
whole := seconds.trunc(0)
frac := ((seconds - whole) * unit).trunc(0)
return [int(whole.str()), int(frac.str())]
}time#
time returns the current Unix time in seconds using the available host syscall.
Source lib/time.mu:8
fn time() {
platformKey := platform()
if platformKey == "darwin/amd64" || platformKey == "darwin/arm64" ||
platformKey == "linux/arm64" || platformKey == "linux/riscv64" {
buf := _make_zero_buffer(16)
syscall("gettimeofday", buf, 0)
return _unixtime_from_timeval(buf)
}
if platformKey == "linux/amd64" {
return syscall("time", 0)
}
return error("time.time: unsupported platform " + platformKey)
}Internal helpers
Underscore-prefixed names are implementation detail. They are listed so the module's source reads without surprises, not as API — they may change at any time.
| _date_from_days(days) | — |
| _days_in_month(year, month) | — |
| _is_leap_year(year) | — |
| _make_timespec(seconds, nanos) | — |
| _make_timeval(seconds, micros) | — |
| _make_zero_buffer(length) | — |
| _put_uint64_le(b, off, value) | put_uint64_le writes value into b as eight little-endian bytes at off. |
| _time_word(b, off, width) | _time_word reads a little-endian unsigned word of width bytes at off. |
| _unixtime_from_timeval(b) | unixtime_from_timeval reads the seconds field from a 16-byte timeval BUFFER. |