Time

@ARGS: --lib tests/fixtures/libs

The time library represents a point in time as a plain integer (i64) holding milliseconds since the Unix epoch — the same unit now() returns. All calendar math is UTC and uses the proleptic Gregorian calendar, so results are identical on the interpreter, --native, and WebAssembly. Import it with use time; and call its free functions with the time:: prefix.

Timezone model: field accessors and formatters are UTC. For local-day bucketing pass a fixed UTC offset in minutes (to_local / local_day / today). There is no timezone database and no daylight-saving handling — a fixed offset only.

use time;

Parsing and field access

parse reads an ISO date "YYYY-MM-DD", optionally followed by a time. The accessors return UTC calendar fields; weekday is 0=Mon..6=Sun.

fn show_fields() {
  d = time::parse("2026-05-25");
  assert(time::year(d) == 2026, "year");
  assert(time::month(d) == 5, "month");
  assert(time::day(d) == 25, "day");
  assert(time::weekday(d) == 0, "2026-05-25 is a Monday");
  assert(time::weekday_name(d) == "Mon", "weekday name");
  assert(time::month_name(d) == "May", "month name");
  t = time::parse("2026-05-25 14:30:07");
  assert(time::hour(t) == 14, "hour");
  assert(time::minute(t) == 30, "minute");
  assert(time::second(t) == 7, "second");

The "T...Z" ISO form parses to the same instant as the space form.

  assert(time::parse("2026-05-25T14:30:07Z") == t, "ISO T/Z form");

Malformed input parses to null — test with !.

  bad = time::parse("not-a-date");
  assert(!bad, "garbage is null");
}

Formatting

fn show_formatting() {
  t = time::parse("2026-05-25 14:30:07");
  assert(time::format_date(t) == "2026-05-25", "format_date");
  assert(time::format_time(t) == "14:30", "format_time");
  assert(time::format_seconds(t) == "14:30:07", "format_seconds");
  assert(time::format_datetime(t) == "2026-05-25 14:30", "format_datetime");
  assert(time::format_iso(t) == "2026-05-25T14:30:07Z", "format_iso");
}

Arithmetic and differences

Stepping never desynchronises across leap years — the math is exact.

fn show_arithmetic() {
  jan1 = time::parse("2026-01-01");
  assert(time::format_date(time::add_weeks(jan1, 1)) == "2026-01-08", "add_weeks");
  assert(time::format_date(time::add_days(jan1, 365)) == "2027-01-01", "add_days");

Leap-day boundary.

  feb28 = time::parse("2024-02-28");
  assert(time::format_date(time::add_days(feb28, 1)) == "2024-02-29", "leap day");
  dec31 = time::parse("2026-12-31");
  assert(time::days_between(jan1, dec31) == 364, "days_between");
  hourly = time::seconds_between(time::parse("2026-01-01 00:00:00"),
                                 time::parse("2026-01-01 01:00:00"));
  assert(hourly == 3600, "seconds_between");
}

Week boundaries and ISO week numbering

fn show_weeks() {

start_of_week snaps back to Monday 00:00.

  wed = time::parse("2026-05-27");
  assert(time::format_date(time::start_of_week(wed)) == "2026-05-25", "Monday of week");

2026-01-01 is a Thursday -> ISO week 1 of 2026.

  assert(time::iso_year(time::parse("2026-01-01")) == 2026, "iso_year");
  assert(time::iso_week(time::parse("2026-01-01")) == 1, "iso_week");

2021-01-01 is a Friday -> still ISO week 53 of 2020.

  jan1_2021 = time::parse("2021-01-01");
  assert(time::iso_year(jan1_2021) == 2020, "iso_year rollback");
  assert(time::iso_week(jan1_2021) == 53, "iso_week 53");
}

Local day at a fixed offset

23:30 UTC at +120 minutes is 01:30 the next local day.

fn show_local() {
  t = time::parse("2026-05-25 23:30:00");
  assert(time::format_datetime(time::to_local(t, 120)) == "2026-05-26 01:30", "to_local");
  assert(time::format_date(time::local_day(t, 120)) == "2026-05-26", "local_day");
}
fn main() {
  show_fields();
  show_formatting();
  show_arithmetic();
  show_weeks();
  show_local();
  println("time library: all checks passed");
}