Docs/Data & text

Dates & time

Dates, time zones, formatting, and measuring time.

On this page

valk.time has DateTime for calendar dates, Duration for lengths of time, time zones, clocks and sleeping.

Create a date#

Valk
use valk.time

fn main() {
    let date = time.DateTime.new(2026, 1, 15, 9, 30) ! {
        println("Invalid date")
        return
    }
    println(date)                   // 2026-01-15T09:30:00Z
    println(date.year())            // 2026
    println(date.day_of_week())     // 4

    let midnight = time.DateTime.new(2026, 1, 1) ! return
    let now = time.DateTime.now()
    println(now > midnight)         // true
}

DateTime is UTC unless it has a zone. Arguments are year, month, day, hour, minute, second and microsecond; the ones left out default to their minimum. An invalid date throws. Dates compare with ==, < and >.

Read Returns
year() month() day() Calendar parts
hour() minute() second() microsecond() Time parts
day_of_week() 1 (Monday) to 7 (Sunday)
day_of_year(), is_leap_year() Position in the year
unix_seconds(), unix_us() Unix timestamp

DateTime.from_unix_seconds(ts) and from_unix_us(ts) go the other way.

Change a date#

Valk
use valk.time

fn main() {
    let date = time.DateTime.new(2026, 1, 31) ! return
    let later = date.add_months(1) ! return
    println(later.format("Y-m-d"))  // 2026-02-28
    let first = date.with_day(1) ! return
    println(first.format("Y-m-d"))  // 2026-01-01

    date.modify_add_days(1) ! return
    println(date.format("Y-m-d"))   // 2026-02-01
}
  • add_years, add_months, add_days, add_hours, add_minutes, add_seconds, add_microseconds return a new date; amounts may be negative.
  • with_year, with_month, with_day, with_hour, ... return a copy with one part replaced.
  • modify_add_* and modify_* change the date itself.

Format and parse#

Valk
use valk.time

fn main() {
    let date = time.DateTime.from_format("Y-m-d H:i", "2026-03-01 14:30") ! {
        println("Invalid date text")
        return
    }
    println(date.format("d/m/Y H:i"))    // 01/03/2026 14:30
    println(date.format("l j F Y, g A")) // Sunday 1 March 2026, 2 PM
    println(date.to_iso8601())           // 2026-03-01T14:30:00Z

    let parsed = time.DateTime.from_iso8601("2026-03-01T14:30:00+02:00") ! return
    println(parsed.in_utc() !? parsed)   // 2026-03-01T12:30:00Z
}

from_format reads text with the same tokens format writes, and throws when the text does not match. Put a \ before a letter to write it as is: "Y-m-d\\TH:i".

Token Meaning Token Meaning
Y / y Year, 2026 / 26 H / G Hour 00-23 / 0-23
m / n Month 01 / 1 h / g Hour 01-12 / 1-12
M / F Jan / January A / a AM / am
d / j Day 05 / 5 i / s Minute / second
D / l Mon / Monday v / u Milliseconds / microseconds
N Weekday 1-7 O / P Offset +0100 / +01:00
U Unix seconds T Zone abbreviation

Durations#

Valk
use valk.time

fn main() {
    let start = time.DateTime.new(2026, 5, 1, 9) ! return
    let end = time.DateTime.new(2026, 5, 1, 10, 30) ! return
    let took = end - start
    println(took.to_string())                   // 1h30m
    println(took.minutes())                     // 90
    println(took > time.Duration.of_minutes(45)) // true

    let deadline = start.add(time.Duration.of_hours(2)) ! return
    println(deadline)                           // 2026-05-01T11:00:00Z
}

Subtracting two dates gives a Duration. Make one with Duration.of_days, of_hours, of_minutes, of_seconds, of_ms or of_us, and read it back with days(), hours(), ... ms() or seconds_float(). Durations add, subtract and compare with the usual operators.

Time zones#

Valk
use valk.time

fn main() {
    let amsterdam = time.zone("Europe/Amsterdam") ! {
        println("Unknown zone")
        return
    }
    let meeting = time.DateTime.new_in(amsterdam, 2026, 3, 30, 9) ! return
    println(meeting)                       // 2026-03-30T09:00:00+02:00
    println(meeting.zone_abbreviation())   // CEST
    println(meeting.in_utc() !? meeting)   // 2026-03-30T07:00:00Z

    let new_york = time.zone("America/New_York") ! return
    println(meeting.in_zone(new_york) !? meeting) // 2026-03-30T03:00:00-04:00
}

time.zone takes an IANA zone name. The same instant compares equal in any zone. add_days, add_months and with_* keep the wall-clock time across daylight saving changes; add_hours and smaller units count elapsed time.

  • DateTime.now_in(zone) and from_format_in(zone, pattern, text) work in a zone.
  • time.local_zone() is the zone of the machine (the TZ variable, then the system settings).
  • time.utc() and time.fixed_zone(3600) give zones without daylight saving.
  • utc_offset() and is_dst() describe a date in its zone.

Sleep#

Valk
use valk.time

fn main() {
    time.sleep_ms(10)
    time.sleep(time.Duration.of_ms(10))
    println("done")
}

Sleeping pauses only the current coroutine; other coroutines keep running. sleep_ns takes nanoseconds.

Measure elapsed time#

Valk
use valk.time

fn main() {
    let start = time.mono_ms()
    time.sleep_ms(20)
    let elapsed = time.mono_ms() - start
    println(elapsed >= 20) // true
}

mono_ms, mono_us and mono_ns read a monotonic clock: it only moves forward, even when the system clock changes, so use it for durations. unix_ms, unix_us and unix_ns return wall-clock time since 1970.