Skip to main content

Time Library (Time)

Every timestamp in this library is a plain ms value (nanoseconds for the _ns variants) — which means normal unit arithmetic just works on them. Time.diff(start) as seconds reads naturally instead of needing a manual division.

Current Time & Elapsed

now() -> ms

The current Unix timestamp, in milliseconds.

now.stof
#[main]
fn main() {
  pln(Time.now());
}
Output

now_ns() -> ns

Same idea, in nanoseconds — for when millisecond resolution isn't fine enough.

now-ns.stof
#[main]
fn main() {
  pln(Time.now_ns());
}
Output

diff(prev: float) -> ms

Shorthand for Time.now() - prev — how much time has passed since a timestamp you captured earlier.

diff.stof
#[main]
fn main() {
  const ts = Time.now();
  sleep(50ms);
  pln(Time.diff(ts) >= 50);
}
Output

diff_ns(prev: float) -> ns

The nanosecond version of diff.

diff-ns.stof
#[main]
fn main() {
  const ts = Time.now_ns();
  sleep(50ms);
  pln(Time.diff_ns(ts) >= 49ms);
}
Output

sleep(time: float = 1000ms) -> void

An alias for Std.sleep — identical behavior, just callable through Time for anyone who'd rather keep every time-related call under one namespace.

time-sleep.stof
#[main]
fn main() {
  const ts = Time.now();
  Time.sleep(50ms);
  pln(Time.diff(ts) >= 50);
}
Output

Formatting

RFC 3339 is the ISO-8601-flavored format most APIs use today; RFC 2822 is the older, email-header-style format — both convert cleanly to and from a plain ms timestamp.

now_rfc3339() -> str

now-rfc3339.stof
#[main]
fn main() {
  pln(Time.now_rfc3339());
}
Output

now_rfc2822() -> str

now-rfc2822.stof
#[main]
fn main() {
  pln(Time.now_rfc2822());
}
Output

to_rfc3339(time: float) -> str

to-rfc3339.stof
#[main]
fn main() {
  pln(Time.to_rfc3339(Time.now()));
}
Output

to_rfc2822(time: float) -> str

to-rfc2822.stof
#[main]
fn main() {
  pln(Time.to_rfc2822(Time.now()));
}
Output

from_rfc3339(time: str) -> ms

from-rfc3339.stof
#[main]
fn main() {
  const ts = Time.from_rfc3339("2025-08-13T16:22:43.028375200+00:00");
  pln(ts < Time.now());
}
Output

from_rfc2822(time: str) -> ms

from-rfc2822.stof
#[main]
fn main() {
  const ts = Time.from_rfc2822("Wed, 13 Aug 2025 16:24:12 +0000");
  pln(ts < Time.now());
}
Output

Reading Components

All UTC — none of these read a local timezone.

year(ts: ms) -> int

year.stof
#[main]
fn main() {
  pln(Time.year(Time.now()));
}
Output

month(ts: ms) -> int

1–12.

month.stof
#[main]
fn main() {
  pln(Time.month(Time.now()));
}
Output

day_of_month(ts: ms) -> int

1–31.

day-of-month.stof
#[main]
fn main() {
  pln(Time.day_of_month(Time.now()));
}
Output

day_of_week(ts: ms) -> int

ISO convention — 0 is Monday, 6 is Sunday.

day-of-week.stof
#[main]
fn main() {
  pln(Time.day_of_week(Time.now()));
}
Output

hour(ts: ms) -> int

0–23.

hour.stof
#[main]
fn main() {
  pln(Time.hour(Time.now()));
}
Output

minute(ts: ms) -> int

minute.stof
#[main]
fn main() {
  pln(Time.minute(Time.now()));
}
Output

second(ts: ms) -> int

second.stof
#[main]
fn main() {
  pln(Time.second(Time.now()));
}
Output

days_in_month(ts: ms) -> int

28–31, leap years handled correctly for February.

days-in-month.stof
#[main]
fn main() {
  pln(Time.days_in_month(Time.now()));
}
Output

Calendar Arithmetic

add_days(ts: ms, n: int) -> ms

n can be negative.

add-days.stof
#[main]
fn main() {
  const tomorrow = Time.add_days(Time.now(), 1);
  pln(tomorrow > Time.now());
}
Output

add_months(ts: ms, n: int) -> ms

If the day of month doesn't exist in the target month, it clamps to the last valid day — Jan 31 plus one month lands on Feb 28 (or 29).

add-months.stof
#[main]
fn main() {
  const next = Time.add_months(Time.now(), 1);
  pln(next > Time.now());
}
Output

Period Boundaries

Every start_of_* function returns midnight UTC on the relevant boundary — useful for anything that resets on a schedule.

start_of_day(ts: ms) -> ms

start-of-day.stof
#[main]
fn main() {
  pln(Time.start_of_day(Time.now()) <= Time.now());
}
Output

start_of_week(ts: ms, start_day: int = 0) -> ms

start_day follows the same ISO convention as day_of_week0 is Monday by default, but any day can be the week's start.

start-of-week.stof
#[main]
fn main() {
  const mon_start = Time.start_of_week(Time.now());
  const sun_start = Time.start_of_week(Time.now(), 6);
  pln(mon_start <= Time.now(), sun_start <= Time.now());
}
Output

start_of_month(ts: ms) -> ms

start-of-month.stof
#[main]
fn main() {
  const som = Time.start_of_month(Time.now());
  pln(Time.day_of_month(som));
}
Output

start_of_year(ts: ms) -> ms

start-of-year.stof
#[main]
fn main() {
  const soy = Time.start_of_year(Time.now());
  pln(Time.month(soy), Time.day_of_month(soy));
}
Output

start_of_period(ts: ms, schedule: str) -> ms

The most flexible of the five — a schedule expression instead of a fixed unit. Returns null for an invalid schedule string:

  • "monthly:N" — the Nth day of every month (clamped to the month's length)
  • "monthly:last" — the last day of every month
  • "weekly:mon" — a given weekday, every week (mon/tue/wed/thu/fri/sat/sun)
  • "nth_weekday:N:mon" — the Nth occurrence of a weekday in the month (N is 1–4)
  • "yearly:M-D" — a fixed month and day every year
  • "quarterly:D" — a given day of the first month of each quarter (Jan/Apr/Jul/Oct)
start-of-period.stof
#[main]
fn main() {
  pln(Time.start_of_period(Time.now(), 'monthly:1') <= Time.now());
  pln(Time.start_of_period(Time.now(), 'weekly:mon') <= Time.now());
  pln(Time.start_of_period(Time.now(), 'nth_weekday:1:tue') <= Time.now());
  pln(Time.start_of_period(Time.now(), 'yearly:1-1') <= Time.now());
  pln(Time.start_of_period(Time.now(), 'quarterly:1') <= Time.now());
}
Output