(std datetime)

Reference

Overview

Dates, times, instants, zones and durations, with the clock as an argument.

(import (std datetime))

(defun (main)
  (def (Some stockholm) (timezone "Europe/Stockholm") :leave-with 1)
  (def (Some noon) (datetime 2026 10 24 #:hours 12) :leave-with 1)
  (def meeting (datetime->moment noon stockholm (resolver GapPush OverlapEarlier)))
  (println (+days meeting 1))     ;; 2026-10-25T12:00:00+01:00[Europe/Stockholm]
  (println (+hours meeting 24))   ;; 2026-10-25T11:00:00+01:00[Europe/Stockholm]
  (println (now))                 ;; 2026-09-28T10:59:20.3067129Z
  0)

In the example the clocks go back on the night between, so a calendar day later and 24 hours later are two different times.

One model for time, the one java.time, NodaTime and Racket’s Gregor share, plus two ideas from OCaml’s Ptime: the clock is an argument (with a default that a program can rebind), and the types refuse operations that mean nothing, such as adding a calendar day to an instant.

The types.

TypeWhat it isBuilt on
Datea calendar date, no zoneDateOnly
Timea time of day, no zoneTimeOnly
DateTimea date and a time, no zoneDateTime, Kind always Unspecified
Instanta point on the global timelinea long of UTC ticks
TimeZonean IANA zone, such as Europe/StockholmTimeZoneInfo
Momenta local datetime, its UTC offset, and its zonea struct of the three
Durationa fixed length of time: hours, not calendar daysTimeSpan
DatePeriodcalendar amounts: years, months, weeks, daysa struct of four int
Perioda date period and a duration: 1 day and 3 hoursa struct of the two
Clockwhere now comes from, and its zonethe system clock, a fixed or a manual one

Each value type is a struct with one field (a Moment has three), so a Date costs what a DateOnly costs. No .NET time type appears in a public signature.

Traits. Each trait has one method, and everything else is a generic function over the traits. A new function, such as a ->quarter, never needs a new instance.

TraitMethodDateTimeDateTimeMomentInstantDuration
HasDate(->date x)✓✓✓
HasTime(->time x)✓✓✓
HasInstant(->instant x)✓✓
DateArith(+date-period x p)✓✓✓
DateDiff(->date-in-zone-of b a)✓✓✓
TimeArith(+duration x d)✓✓✓✓✓

Every type has = and ->str; all but TimeZone, the periods and the resolvers also have compare. A TimeZone is equal to another by its id.

Construction. Bad input is None: (date 2026 2 30) is None. Nanoseconds are cut to the 100 ns a tick holds: (time 1 2 #:nanoseconds 150) holds 100 ns.

Moments. A local time can be missing from a zone (a gap, when the clocks go forward) or happen twice (an overlap, when they go back), so datetime->moment takes a Resolver saying what to do with each, and datetime->moment/opt answers None for both.

Date arithmetic on a moment works on the local time and then finds the offset again, since a trait method cannot take a resolver. Through an overlap it keeps the moment’s own offset if that still fits, and takes the earlier one if not. Through a gap it pushes forward. So 2026-10-24 02:30+02:00 plus a day is 2026-10-25 02:30+02:00, and 2026-10-26 02:30+01:00 minus a day is 2026-10-25 02:30+01:00. +date-period/resolver and -date-period/resolver take a resolver of their own.

Duration arithmetic on a moment is exact: through the instant, and back into the same zone.

The clock. Only now, now/moment, today and clock-timezone read the time. Each takes #:clock, which defaults to the value of the parameter current-clock at that call. A clock carries its own zone, which is where today is, and there is no separate current zone. Everything else in the module is pure.

(now)                                      ;; the current clock
(now #:clock some-clock)                   ;; this one
(today #:tz tokyo)                         ;; the date in Tokyo
(parameterize ((current-clock test-clock))
  (handle-request req))                    ;; every (now) inside reads test-clock

This is the wall clock, and it can step backwards when the machine’s time is corrected. To measure how long something takes, use the prelude’s (monotonic-ms) or (std stopwatch) instead, whose clock only moves forward.

Arithmetic. Calendar units (+years, +months, +weeks, +days and their - twins) take an int and work on anything DateArith; clock units (+hours, +minutes, +seconds, +milliseconds and their twins) take a long and work on anything TimeArith.

Month arithmetic clamps to the end of the month: 2026-01-31 plus a month is 2026-02-28, and 2024-01-31 plus a month is 2024-02-29. A period is applied largest unit first: years, then months, then weeks and days, then the duration. Time-of-day arithmetic wraps at midnight, modulo 24 hours, as TimeOnly and java.time’s LocalTime do. 23:00 plus two hours is 01:00, 01:00 minus three hours is 22:00, and whole days drop out: 12:00 plus (duration #:days 3 #:hours 2) is 14:00, as is 12:00 plus 50 hours. Nothing is refused, so to know how many midnights were crossed, add to a DateTime instead.

Weeks. ISO 8601 weeks, as most of Europe numbers them: a week runs Monday to Sunday, and week 1 is the week with the year’s first Thursday. A week belongs to the year its Thursday is in, so up to three days at the start of January can be in the last week of the year before, and up to three at the end of December in week 1 of the next: 2027-01-01 is in week 53 of 2026, and 2025-12-29 in week 1 of 2026. That year is ->week-year, and it is the one to print beside ->week.

Differences. Whole units, toward zero, from the first argument to the second. days-between, weeks-between, months-between and years-between read dates only, of two values of one type. duration-between and the hours-between family measure the time that passed, between two things with an instant.

Text. ->str writes ISO 8601 for every type, and a reader for each reads it back as an Option:

iso8601->date2026-10-25
iso8601->time02:30, 02:30:00, 02:30:00.5, 02:30:00,5
iso8601->datetime2026-10-25T02:30:00
iso8601->instant2026-10-25T01:30:00Z, or any offset, converted to UTC
iso8601->moment2026-10-25T02:30:00+01:00[Europe/Stockholm]
iso8601->durationPT26H30M, -PT1.5S
iso8601->date-periodP1Y2M3W4D, P1M-3D
iso8601->periodP1DT3H

Only the extended format is read (2026-10-25, not 20261025), with an upper case T and Z. Week dates, 2026-W43-7, and ordinal dates, 2026-298, are not read. A fraction of a second has up to seven digits written, and more are read and dropped.

Text in a format of your own is written by date->text and its neighbours and read by text->date and its neighbours. The digits are written and read by this module, not by .NET, so no culture is involved and nothing depends on the machine’s locale.

Range and errors. Every value lies between 0001-01-01T00:00 and 9999-12-31T23:59:59.9999999. A function that returns the value itself raises a System.OverflowException when the result would be outside; a constructor, a reader or an /opt function answers None.

(-days (date 1 1 1) 1)        ;; raises
(date 0 12 31)                ;; None

Nothing is clamped or wrapped silently, apart from the month-end rule and the wrap at midnight above. The checks come before the arithmetic, since C# arithmetic wraps where it overflows: (+hours i n) with an n above about 256 million would multiply past a long and could wrap back into range, so n is checked first.

A Duration holds ±(263 − 1) ticks, about ±29 227 years, from -PT256204778H48M5.4775807S to PT256204778H48M5.4775807S. A TimeSpan goes one tick further on the negative side, to a value with no negation; a sum that would reach it raises instead, so every duration can be negated, printed and read back.

Near the two ends of the range, a local time and its instant cannot both fit: 0001-01-01T00:00 in Tokyo is an instant on the day before, and the instant 0001-01-01T00:00Z is a local time on the day before in New York. There, datetime->moment and instant->moment raise and datetime->moment/opt is None. This is not a gap, so no resolver moves it.

Limits.

How a local time finds its offset. An offset o fits a local time l when the zone has offset o at the instant l - o. The candidates are the offsets a day before and a day after l, read as instants. No offset exceeds 16 hours, so those two cover every offset in reach, as long as the zone changes its offset at most once in 48 hours. Nothing in the tz database comes close: its two closest changes, in Freetown in 1939, are 95 hours apart. When a lookup answers with a third offset, that becomes a candidate too, which catches most layouts with two changes in 48 hours, though not every one.

How far a change jumps does not matter, only that every offset is under a day, so the two lookups land on either side of it. Samoa skipped 2011-12-30 entirely, going from −10:00 to +14:00, and that day is a gap here; Alaska had 1867-10-18 twice, and that day is an overlap. Both are in the tests.

.NET’s own IsInvalidTime and IsAmbiguousTime are not used. They miss the gaps and overlaps that a change of standard time leaves: Brussels went from +00:00 to +01:00 on 1914-11-08, and .NET calls 00:30 that night an ordinary time. Here it is a gap.

Why it is built this way. Each of these rules something out, and the example is what it rules out. Values are written as ISO text here for short.

Also exported. date+date-period, datetime+date-period, moment+date-period, datetime+duration, instant+duration, moment+duration and duration+duration are the trait methods at one type. An importing module copies the trait methods’ bodies into its own code, and those bodies call these, so the compiler exports them too.

See also: Moment, datetime->moment, now, current-clock, date->text, text->date

Reference

Types

Functions

Values

Types

Date

record

A calendar date, with no time and no zone.

Built on DateOnly, and costs what one costs. Opaque: every date comes from a function that checked it.

See also: date, iso8601->date, HasDate

Time

record

A time of day, with no date and no zone.

Built on TimeOnly, at a precision of 100 ns. Arithmetic on a time wraps at midnight.

See also: time, iso8601->time, HasTime

DateTime

record

A date and a time of day, with no zone.

Built on a System.DateTime whose Kind is always Unspecified. It has no instant, so it is not HasInstant: put it in a zone with datetime->moment first. The name is this module’s; code at the edge to .NET writes System.DateTime.

See also: datetime, date+time->datetime, datetime->moment

Instant

record

A point on the global timeline, with no zone.

A long of UTC ticks rather than a UTC DateTime, whose equality ignores the Kind. ->str writes it in UTC, with a Z.

See also: now, instant->moment, HasInstant

TimeZone

record

An IANA time zone, such as Europe/Stockholm.

Built on TimeZoneInfo. Two zones are equal when their ids are, and ->str writes the id. A zone has no compare.

See also: timezone, utc, timezone-id

Moment

record

A local date and time, its UTC offset, and its zone.

Opaque, so its offset is always one its zone has at that local time: nobody can build 2026-07-01T12:00+01:00[Europe/Stockholm] (July is +02:00).

= compares every field, so 12:00+02:00[Europe/Stockholm] and 10:00+00:00[UTC] are one instant and two values. compare orders by instant, then local time, then zone id, so that it is 0 exactly when = holds. For the same instant, compare (->instant a) and (->instant b).

See also: datetime->moment, instant->moment, iso8601->moment

Duration

record

A fixed length of time: hours, minutes and seconds, not calendar days.

Built on TimeSpan, and holds ±(263 − 1) ticks, about ±29 227 years. (duration #:days 1) is exactly 24 hours; a calendar day is a DatePeriod.

See also: duration, iso8601->duration, duration-between

Clock

union

Where now comes from, and the zone today is in.

The system clock, a fixed clock or a manual one. A clock carries its own zone, and there is no separate current zone.

See also: current-clock, system-clock, fixed-clock, manual-clock

DatePeriod

record

Calendar amounts: years, months, weeks and days.

years
Years.
months
Months.
weeks
Weeks.
days
Days.

Transparent, since any mix of numbers is a valid period: 1 month and 40 days is fine, as months differ in length. Equality is field by field, so (= (date-period #:weeks 1) (date-period #:days 7)) is #f. It is applied largest unit first: years, then months, then weeks and days together.

See also: date-period, +date-period, iso8601->date-period

Period

record

A date period and a duration: 1 day and 3 hours.

calendar
The calendar part, a DatePeriod, applied first.
exact
The exact part, a Duration, applied after the calendar part.

See also: period, +period, iso8601->period

Gap

union

What datetime->moment does with a local time the zone skips.

GapPush
Forward by the length of the gap: 02:30 in a one-hour gap is 03:30.
GapNextValid
The first time after the gap: 03:00.

In Stockholm, 2026-03-29 02:30 never happens: GapPush gives 03:30+02:00 and GapNextValid 03:00+02:00.

See also: resolver, Overlap

Overlap

union

What datetime->moment does with a local time the zone has twice.

OverlapEarlier
The first of the two, at the larger offset.
OverlapLater
The second.

In Stockholm, 2026-10-25 02:30 happens twice: OverlapEarlier gives +02:00 (00:30Z) and OverlapLater +01:00 (01:30Z).

See also: resolver, Gap

Resolver

record

What to do with a local time in a gap and with one in an overlap.

gap
What to do in a gap.
overlap
What to do in an overlap.

A closed set: a resolver of your own, such as going back to the last valid time on a gap, would need a new case here. In return, no resolver can produce a moment at a local time or in a zone that was not asked for, and datetime->moment/opt covers refusing.

See also: resolver, datetime->moment

HasDate

trait

Has a calendar date: Date, DateTime and Moment.

%a
The type with a date.

See also: ->year, ->month, ->day, ->weekday, ->week

HasTime

trait

Has a time of day: Time, DateTime and Moment.

%a
The type with a time of day.

See also: ->hours, ->minutes, ->seconds, ->nanoseconds

HasInstant

trait

Is a point on the timeline: Instant and Moment.

%a
The type with an instant.

DateTime is not one, since (->instant some-datetime) would have to guess a zone.

See also: duration-between, ->unix

DateArith

trait

Takes calendar arithmetic: Date, DateTime and Moment.

%a
The type that takes a DatePeriod.

See also: +days, +months, -date-period, +period

DateDiff

trait

Takes calendar differences: Date, DateTime and Moment.

%a
The type whose dates are compared.

See also: days-between, months-between

TimeArith

trait

Takes a Duration: Time, DateTime, Moment, Instant and Duration.

%a
The type that takes a Duration.

See also: +hours, -duration, +period

DateNumber

union

The numbers of a date, as a format names them: year, month, day and the others.

NumYear
year, the year.
NumMonth
month, 1 to 12.
NumDay
day, the day of the month.
NumWeekday
weekday, 1 for Monday to 7 for Sunday.
NumYday
yday, the day of the year.
NumWeek
week, the ISO week.
NumWeekYear
week-year, the year the ISO week is in.

Each is written by its tag, alone or with a width: year and (year) are the same, and (year 4) fills with zeros to four digits.

See also: DatePart, date->text

RomanNumber

union

What (roman …) writes in a format: year, month or day.

RomanYear
(roman year).
RomanMonth
(roman month).
RomanDay
(roman day).

No width, as a numeral has no zeros to fill with: (roman (year 4)) is a compile error.

See also: DatePart

DatePart

union

A part of a date’s format, for date->text and text->date.

DateNum
A number: year, month, day, weekday, yday, week or week-year.
DateRoman
(roman year), (roman month) or (roman day).
MonthName
month-name, from the names table.
MonthAbbr
month-abbr, from the names table.
WeekdayName
weekday-name, from the names table.
WeekdayAbbr
weekday-abbr, from the names table.
DateText
A string, written as it stands.
DateChar
A character, written as it stands.

See also: date->text, text->date, DateNumber, RomanNumber

TimePart

union

A part of a time’s format, for time->text and text->time.

TimeHours
hours, 0 to 23.
TimeHours12
hours12, 1 to 12: 12 for midnight's and noon's hour.
TimeMinutes
minutes.
TimeSeconds
seconds.
TimeFraction
fraction: bare, ISO 8601's, point included; (fraction n), n digits.
TimeAmPm
am-pm, the names table's word for the morning or the afternoon.
TimeText
A string, written as it stands.
TimeChar
A character, written as it stands.

See also: time->text, text->time

DateTimePart

union

A part of a datetime’s format: the fields of a date’s and of a time’s.

DateTimeDate
A field of a DatePart.
DateTimeTime
A field of a TimePart.
DateTimeText
A string, written as it stands.
DateTimeChar
A character, written as it stands.

See also: datetime->text, text->datetime

MomentPart

union

A part of a moment’s format: a datetime’s fields, offset, zone, unix and unix-ms.

MomentDateTime
A field of a DateTimePart, of the local time.
MomentOffset
offset, as ISO 8601 writes it: +01:00.
MomentZone
zone, the zone's id: Europe/Stockholm.
MomentUnix
unix, the Unix time in seconds, rounded toward the past.
MomentUnixMs
unix-ms, the Unix time in milliseconds, rounded toward the past.
MomentText
A string, written as it stands.
MomentChar
A character, written as it stands.

See also: moment->text, text->moment

InstantPart

union

A part of an instant’s format: a datetime’s fields in UTC, offset, unix and unix-ms.

InstantDateTime
A field of a DateTimePart, in UTC.
InstantOffset
offset, which for an instant is Z.
InstantUnix
unix, the Unix time in seconds, rounded toward the past.
InstantUnixMs
unix-ms, the Unix time in milliseconds, rounded toward the past.
InstantText
A string, written as it stands.
InstantChar
A character, written as it stands.

An instant has no zone to write.

See also: instant->text, text->instant

DateNames

record

The names a format writes and reads: months, weekdays, and the words of am-pm.

months
Twelve month names, from January.
months-short
Twelve abbreviated month names, from January.
weekdays
Seven weekday names, from Monday.
weekdays-short
Seven abbreviated weekday names, from Monday.
am
What am-pm writes before noon.
pm
What am-pm writes from noon.

A table of your own for another language is a DateNames with these six fields. One with the wrong number of names raises an ArgumentException when a name is taken from it, in writing or in reading.

See also: english-names, swedish-names, current-date-names

Functions

->date

function

(: ->date (-> %a Date))

The date of a value; a moment’s local date.

x
A date, a datetime or a moment.
returns
Its date.

->time

function

(: ->time (-> %a Time))

The time of day of a value; a moment’s local time.

x
A time, a datetime or a moment.
returns
Its time of day.

->instant

function

(: ->instant (-> %a Instant))

The instant of a value.

x
An instant or a moment.
returns
Its instant; a moment's is its local time less its offset.

+date-period

function

(: +date-period (-> %a DatePeriod %a))

A value moved by calendar amounts.

x
A date, a datetime or a moment.
p
The period, applied years first, then months, then weeks and days.
returns
x moved by p, of x's type.
raises OverflowException
When the result is outside the calendar.

Month arithmetic clamps to the end of the month: 2026-01-31 plus a month is 2026-02-28. On a moment the local time moves and the offset is found again: through an overlap it keeps the moment’s own offset if that still fits, and takes the earlier one if not; through a gap it pushes forward.

See also: -date-period, +date-period/resolver

->date-in-zone-of

function

(: ->date-in-zone-of (-> %a %a Date))

The date of b where a is, which calendar differences compare with a’s own date.

b
The value whose date is wanted.
a
The value whose zone it is read in.
returns
b's date; for two moments, b's date in a's zone.
raises OverflowException
Near either end of the calendar, when b moved into a's zone is outside it.

A moment b is first moved into a’s zone, so two moments of one instant are the same day wherever they are. A date or a datetime is its own date.

+duration

function

(: +duration (-> %a Duration %a))

A value moved by an exact length of time.

x
A time, a datetime, a moment, an instant or a duration.
d
The duration.
returns
x moved by d, of x's type.
raises OverflowException
When the result is outside the calendar, or a duration outside ±29 227 years.

A time wraps at midnight and never raises. A moment moves through its instant and is put back in the same zone, so it is exact. A duration plus a duration is their sum: (+hours (duration #:minutes 30) 1) is PT1H30M.

See also: -duration

date

function

(: date (-> int int int (Option Date)))
(date y m d)

A date from a year, a month and a day, or None if there is no such date.

y
The year, 1 to 9999.
m
The month, 1 to 12.
d
The day of the month.
returns
Some date, or None: (date 2026 2 30) is None.

See also: week-date, iso8601->date

time

function

(: time (-> int int (#:seconds int) (#:nanoseconds int) (Option Time)))
(time h m #:seconds 0 #:nanoseconds 0)

A time of day, or None if there is no such time.

h
The hour, 0 to 23.
m
The minute, 0 to 59.
#:seconds
The second, 0 to 59.
#:nanoseconds
The fraction of the second, 0 to 999 999 999, cut to the 100 ns a tick holds.
returns
Some time, or None.
(time 13 5)                     ;; (Some 13:05:00)
(time 1 2 #:nanoseconds 150)    ;; holds 100 ns

See also: datetime, iso8601->time

datetime

function

(: datetime (-> int int int (#:hours int) (#:minutes int) (#:seconds int) (#:nanoseconds int) (Option DateTime)))
(datetime y mo d #:hours 0 #:minutes 0 #:seconds 0 #:nanoseconds 0)

A date and a time of day, or None if there is no such datetime.

y
The year, 1 to 9999.
mo
The month, 1 to 12.
d
The day of the month.
#:hours
The hour, 0 to 23.
#:minutes
The minute, 0 to 59.
#:seconds
The second, 0 to 59.
#:nanoseconds
The fraction of the second, cut to the 100 ns a tick holds.
returns
Some datetime, or None.
(datetime 2026 10 24 #:hours 12)   ;; (Some 2026-10-24T12:00:00)

See also: date+time->datetime, iso8601->datetime

date+time->datetime

function

(: date+time->datetime (-> Date Time DateTime))
(date+time->datetime d t)

A datetime from a date and a time.

d
The date.
t
The time of day.
returns
The datetime.

See also: ->datetime

->datetime

function

(: ->datetime (-> %a DateTime))
(->datetime x)

The date and time of anything with both, as a DateTime.

x
A datetime or a moment.
returns
Its date and time; a moment's local ones.

How a moment is written with a datetime’s format: (datetime->text (->datetime some-moment) ...).

See also: date+time->datetime, datetime->text

duration

function

(: duration (-> (#:days long) (#:hours long) (#:minutes long) (#:seconds long) (#:milliseconds long) (#:nanoseconds long) Duration))
(duration #:days 0L #:hours 0L #:minutes 0L #:seconds 0L #:milliseconds 0L
                 #:nanoseconds 0L)

A fixed length of time, the sum of the units given.

#:days
Days of exactly 24 hours.
#:hours
Hours.
#:minutes
Minutes.
#:seconds
Seconds.
#:milliseconds
Milliseconds.
#:nanoseconds
Nanoseconds, cut to the 100 ns a tick holds.
returns
The duration.
raises OverflowException
When a unit or the sum is outside ±29 227 years.
(duration #:hours 26 #:minutes 30)   ;; PT26H30M

A day here is exactly 24 hours. For calendar days, which can be 23 or 25 hours long across a change of offset, use a DatePeriod.

See also: date-period, period

date-period

function

(: date-period (-> (#:years int) (#:months int) (#:weeks int) (#:days int) DatePeriod))
(date-period #:years 0 #:months 0 #:weeks 0 #:days 0)

Calendar amounts: years, months, weeks and days.

#:years
Years.
#:months
Months.
#:weeks
Weeks.
#:days
Days.
returns
The period. Any mix of amounts, negative ones included, is a period.
(date-period #:months 1 #:days -3)   ;; P1M-3D

See also: period, duration, +date-period

period

function

(: period (-> (#:years int) (#:months int) (#:weeks int) (#:days int) (#:hours long) (#:minutes long) (#:seconds long) (#:milliseconds long) (#:nanoseconds long) Period))
(period #:years 0 #:months 0 #:weeks 0 #:days 0
               #:hours 0L #:minutes 0L #:seconds 0L #:milliseconds 0L #:nanoseconds 0L)

A date period and a duration together: 1 day and 3 hours.

#:years
Years.
#:months
Months.
#:weeks
Weeks.
#:days
Calendar days.
#:hours
Hours.
#:minutes
Minutes.
#:seconds
Seconds.
#:milliseconds
Milliseconds.
#:nanoseconds
Nanoseconds, cut to the 100 ns a tick holds.
returns
The period.
raises OverflowException
When the clock units are outside ±29 227 years.
(period #:days 1 #:hours 3)   ;; P1DT3H

#:days is a calendar day here, in the date part; the clock units make the duration.

See also: +period, date-period, duration

timezone

function

(: timezone (-> string (Option TimeZone)))
(timezone id)

The IANA zone with this id, or None.

id
An IANA id, such as Europe/Stockholm.
returns
Some zone, or None for an id the machine does not know.
(def (Some stockholm) (timezone "Europe/Stockholm") :leave-with 1)

IANA ids only, so a Windows id such as "W. Europe Standard Time" is None, and so is every id on a machine without zone data (see the module’s limits). Stop there rather than fall back to utc: (option-value (timezone "Europe/Stockholm") utc) would put every date and time in UTC without a word, on exactly the machines that lack the data.

See also: utc, timezone-id

timezone-id

function

(: timezone-id (-> TimeZone string))
(timezone-id z)

A zone’s IANA id.

z
The zone.
returns
Its id, such as Europe/Stockholm; what ->str writes.

timezone-offset-at

function

(: timezone-offset-at (-> TimeZone Instant Duration))
(timezone-offset-at z i)

A zone’s UTC offset at an instant.

z
The zone.
i
The instant.
returns
The offset, as a Duration: PT2H in Stockholm in summer.

See also: ->utc-offset, instant->moment

resolver

function

(: resolver (-> Gap Overlap Resolver))
(resolver g o)

A resolver from what to do in a gap and in an overlap.

g
GapPush or GapNextValid.
o
OverlapEarlier or OverlapLater.
returns
The resolver.
(resolver GapPush OverlapEarlier)
GapPushforward by the length of the gap: 02:30 in a one-hour gap is 03:30
GapNextValidthe first time after the gap: 03:00
OverlapEarlierthe first of the two, at the larger offset
OverlapLaterthe second

In Stockholm, 2026-10-25 02:30 happens twice: OverlapEarlier gives +02:00 (00:30Z) and OverlapLater +01:00 (01:30Z). 2026-03-29 02:30 never happens: GapPush gives 03:30+02:00 and GapNextValid 03:00+02:00.

See also: datetime->moment, Gap, Overlap

datetime->moment

function

(: datetime->moment (-> DateTime TimeZone Resolver Moment))
(datetime->moment dt z r)

A local time in a zone, with a resolver for a gap or an overlap.

dt
The local time.
z
The zone.
r
What to do when dt is in a gap or an overlap.
returns
The moment. Always one, since the resolver decides.
raises OverflowException
Near either end of the calendar, when the local time and its instant cannot both fit.
(datetime->moment noon stockholm (resolver GapPush OverlapEarlier))

A local time can be missing from a zone (a gap, when the clocks go forward) or happen twice (an overlap, when they go back); the resolver says what to do with each. 0001-01-01T00:00 in Tokyo is an instant on the day before: that is not a gap, so no resolver moves it, and this raises.

See also: datetime->moment/opt, resolver, instant->moment

datetime->moment/opt

function

(: datetime->moment/opt (-> DateTime TimeZone (Option Moment)))
(datetime->moment/opt dt z)

A local time in a zone, or None in a gap or an overlap.

dt
The local time.
z
The zone.
returns
Some moment when the local time happens exactly once in z, or None. None too when its instant is outside the calendar.

See also: datetime->moment

instant->moment

function

(: instant->moment (-> Instant TimeZone Moment))
(instant->moment i z)

The moment an instant is in a zone.

i
The instant.
z
The zone.
returns
The moment. Always one answer.
raises OverflowException
Near either end of the calendar, when the local time is outside it: the instant 0001-01-01T00:00Z in New York.

See also: adjust-timezone, datetime->moment

+date-period/resolver

function

(: +date-period/resolver (-> Moment DatePeriod Resolver Moment))
(+date-period/resolver m p r)

A moment moved by calendar amounts, with a resolver for where it lands.

m
The moment.
p
The period.
r
What to do when the new local time is in a gap or an overlap.
returns
The moment in m's zone.
raises OverflowException
When the result is outside the calendar.

+date-period on a moment keeps its offset through an overlap if that still fits, and pushes forward through a gap; this says otherwise.

See also: -date-period/resolver, +date-period

-date-period/resolver

function

(: -date-period/resolver (-> Moment DatePeriod Resolver Moment))
(-date-period/resolver m p r)

A moment moved back by calendar amounts, with a resolver for where it lands.

m
The moment.
p
The period, subtracted.
r
What to do when the new local time is in a gap or an overlap.
returns
The moment in m's zone.
raises OverflowException
When the result is outside the calendar.

See also: +date-period/resolver, -date-period

->utc-offset

function

(: ->utc-offset (-> Moment Duration))
(->utc-offset m)

A moment’s UTC offset.

m
The moment.
returns
Its offset, as a Duration.

See also: timezone-offset-at

->timezone

function

(: ->timezone (-> Moment TimeZone))
(->timezone m)

A moment’s zone.

m
The moment.
returns
Its zone.

adjust-timezone

function

(: adjust-timezone (-> Moment TimeZone Moment))
(adjust-timezone m z)

The same instant in another zone.

m
The moment.
z
The zone to put it in.
returns
A moment at m's instant in z.
raises OverflowException
Near either end of the calendar, when the local time in z is outside it.

See also: instant->moment

instant->utc-datetime

function

(: instant->utc-datetime (-> Instant DateTime))
(instant->utc-datetime i)

An instant’s date and time in UTC.

i
The instant.
returns
Its UTC date and time, as a DateTime.

See also: utc-datetime->instant

utc-datetime->instant

function

(: utc-datetime->instant (-> DateTime Instant))
(utc-datetime->instant dt)

The instant of a date and time read as UTC.

dt
A date and time in UTC.
returns
The instant.

It takes any DateTime, so a local time goes in and is read as UTC without a word: put a local time in its zone with datetime->moment. Read on purpose, it spells out a wall-clock difference: (hours-between (utc-datetime->instant a) (utc-datetime->instant b)).

See also: instant->utc-datetime, datetime->moment

instant->unix-seconds

function

(: instant->unix-seconds (-> Instant long))
(instant->unix-seconds i)

An instant’s Unix time in seconds, rounded toward the past.

i
The instant.
returns
Seconds since 1970-01-01T00:00Z. Half a second before 1970 is -1.

See also: unix-seconds->instant, ->unix

unix-seconds->instant

function

(: unix-seconds->instant (-> long (Option Instant)))
(unix-seconds->instant s)

The instant of a Unix time in seconds.

s
Seconds since 1970-01-01T00:00Z.
returns
Some instant, or None outside the years 1 to 9999.

See also: instant->unix-seconds, unix-milliseconds->instant

instant->unix-milliseconds

function

(: instant->unix-milliseconds (-> Instant long))
(instant->unix-milliseconds i)

An instant’s Unix time in milliseconds, rounded toward the past.

i
The instant.
returns
Milliseconds since 1970-01-01T00:00Z. Half a millisecond before 1970 is -1.

See also: unix-milliseconds->instant

unix-milliseconds->instant

function

(: unix-milliseconds->instant (-> long (Option Instant)))
(unix-milliseconds->instant ms)

The instant of a Unix time in milliseconds.

ms
Milliseconds since 1970-01-01T00:00Z.
returns
Some instant, or None outside the years 1 to 9999.

See also: instant->unix-milliseconds, unix-seconds->instant

->unix

function

(: ->unix (-> %a long))
(->unix x)

The Unix time in seconds of an instant or a moment.

x
An instant or a moment.
returns
Its instant's Unix time, rounded toward the past, whatever a moment's zone.

See also: instant->unix-seconds

system-clock-in

function

(: system-clock-in (-> TimeZone Clock))
(system-clock-in z)

The machine’s clock, in a zone of your choosing.

z
The zone today is to be in.
returns
A clock reading the system time, with z as its zone.
(defun (main)
  (def (Some stockholm) (timezone "Europe/Stockholm") :leave-with 1)
  (parameterize ((current-clock (system-clock-in stockholm)))
    (serve)))

A machine set to UTC, as most containers are, would otherwise make (today) UTC’s date, which in Stockholm is still yesterday until 01:00, or 02:00 in summer. A program binds it once, around everything it runs, and the fibers it starts inherit it.

See also: system-clock, current-clock

fixed-clock

function

(: fixed-clock (-> Instant TimeZone Clock))
(fixed-clock i z)

A clock that always answers the same instant.

i
The instant it answers.
z
Its zone.
returns
The clock.

See also: manual-clock, current-clock

manual-clock

function

(: manual-clock (-> Instant TimeZone Clock))
(manual-clock i z)

A clock that stands still until clock-advance! moves it.

i
The instant it starts at.
z
Its zone.
returns
The clock.

The clock takes a lock to advance and to answer now, so fibers on several threads may share one: every advance counts, and the next now sees it.

See also: clock-advance!, fixed-clock

clock-advance!

function

(: clock-advance! (-> Clock Duration void))
(clock-advance! c d)

Moves a manual clock.

c
A clock from manual-clock.
d
How far. A negative duration steps it back, as a corrected wall clock does.
raises ArgumentException
When the clock is the system clock or a fixed one.
raises OverflowException
When the new instant is outside the calendar.

A manual clock is a Clock, not a type of its own, so this on another clock is an error when it runs rather than when it compiles.

See also: manual-clock

clock-timezone

function

(: clock-timezone (-> (#:clock Clock) TimeZone))
(clock-timezone #:clock (parameter-ref current-clock))

A clock’s zone.

#:clock
The clock.
returns
Its zone: the machine's for system-clock.

See also: today

now

function

(: now (-> (#:clock Clock) Instant))
(now #:clock (parameter-ref current-clock))

The current instant.

#:clock
The clock to read.
returns
What the clock says now.
(now)                     ;; the current clock
(now #:clock some-clock)  ;; this one

See also: now/moment, today

now/moment

function

(: now/moment (-> (#:clock Clock) (#:tz TimeZone) Moment))
(now/moment #:clock (parameter-ref current-clock) #:tz (clock-timezone #:clock clock))

The current moment, in the clock’s zone or another.

#:clock
The clock to read.
#:tz
The zone to put it in.
returns
What the clock says now, as a moment.

A Moment rather than an Instant, since an instant has no zone and so now in Stockholm cannot be one.

See also: now, today

today

function

(: today (-> (#:clock Clock) (#:tz TimeZone) Date))
(today #:clock (parameter-ref current-clock) #:tz (clock-timezone #:clock clock))

Today’s date, in the clock’s zone or another.

#:clock
The clock to read.
#:tz
The zone whose date it is.
returns
The date there now.
(today #:tz tokyo)   ;; the date in Tokyo

See also: now/moment, system-clock-in

->year

function

(: ->year (-> %a int))
(->year x)

The year of anything with a date.

x
A date, a datetime or a moment; a moment's local date.
returns
The year, 1 to 9999.

See also: ->week-year

->month

function

(: ->month (-> %a int))
(->month x)

The month of anything with a date.

x
A date, a datetime or a moment; a moment's local date.
returns
The month, 1 to 12.

->day

function

(: ->day (-> %a int))
(->day x)

The day of the month of anything with a date.

x
A date, a datetime or a moment; a moment's local date.
returns
The day, 1 to 31.

->weekday

function

(: ->weekday (-> %a int))
(->weekday x)

The day of the week, Monday 1 to Sunday 7, as ISO 8601 counts.

x
A date, a datetime or a moment; a moment's local date.
returns
1 for Monday up to 7 for Sunday.

It is not called ->wday, since C’s tm_wday, Ruby’s wday and Racket Gregor’s ->wday count Sunday as 0. For Sunday as 0, write (% (->weekday d) 7).

->yday

function

(: ->yday (-> %a int))
(->yday x)

The day of the year of anything with a date.

x
A date, a datetime or a moment; a moment's local date.
returns
The day, 1 to 366.

->week

function

(: ->week (-> %a int))
(->week x)

The ISO 8601 week number of anything with a date.

x
A date, a datetime or a moment; a moment's local date.
returns
The week, 1 to 53, of the year ->week-year answers.

A week runs Monday to Sunday and belongs to the year its Thursday is in, so 2027-01-01 is in week 53 of 2026. Print it beside ->week-year, not ->year.

See also: ->week-year, weeks-in-year, week-date

->week-year

function

(: ->week-year (-> %a int))
(->week-year x)

The year ->week counts in.

x
A date, a datetime or a moment; a moment's local date.
returns
The year of the date's ISO week: 2026 for 2027-01-01, and 2026 for 2025-12-29.

It differs from ->year in at most three days at either end of a year. The two are separate functions, since asking which week it is usually wants the number only.

See also: ->week, ->year

weeks-in-year

function

(: weeks-in-year (-> int int))
(weeks-in-year y)

How many ISO weeks a week-year has.

y
The week-year.
returns
52 or 53.
raises OverflowException
When y is outside 1 to 9999.

See also: week-date, ->week

week-date

function

(: week-date (-> int int int (Option Date)))
(week-date y w d)

The date of a day of an ISO week, or None.

y
The week-year.
w
The week, 1 to 53.
d
The day of the week, 1 for Monday to 7 for Sunday.
returns
Some date, or None for a week the year does not have, such as week 53 of 2027.
(week-date 2026 43 1)   ;; (Some 2026-10-19)

See also: ->week, ->week-year, weeks-in-year

->hours

function

(: ->hours (-> %a int))
(->hours x)

The hour of anything with a time of day.

x
A time, a datetime or a moment; a moment's local time.
returns
The hour, 0 to 23.

->minutes

function

(: ->minutes (-> %a int))
(->minutes x)

The minute of anything with a time of day.

x
A time, a datetime or a moment; a moment's local time.
returns
The minute, 0 to 59.

->seconds

function

(: ->seconds (-> %a int))
(->seconds x)

The second of anything with a time of day.

x
A time, a datetime or a moment; a moment's local time.
returns
The second, 0 to 59.

->milliseconds

function

(: ->milliseconds (-> %a int))
(->milliseconds x)

The millisecond of anything with a time of day.

x
A time, a datetime or a moment; a moment's local time.
returns
The whole milliseconds of the second, 0 to 999.

->nanoseconds

function

(: ->nanoseconds (-> %a int))
(->nanoseconds x)

The whole fraction of the second, in nanoseconds.

x
A time, a datetime or a moment; a moment's local time.
returns
0 to 999 999 900, always a multiple of 100.

+years

function

(: +years (-> %a int %a))
(+years x n)

Years later.

x
A date, a datetime or a moment.
n
How many years.
returns
x moved by n years; February 29 becomes February 28 in a year without one.
raises OverflowException
When the result is outside the calendar.

See also: -years, +date-period

+months

function

(: +months (-> %a int %a))
(+months x n)

Months later, clamped to the end of the month.

x
A date, a datetime or a moment.
n
How many months.
returns
x moved by n months: 2026-01-31 plus a month is 2026-02-28, and 2024-01-31 plus a month 2024-02-29.
raises OverflowException
When the result is outside the calendar.

See also: -months, months-between

+weeks

function

(: +weeks (-> %a int %a))
(+weeks x n)

Weeks later.

x
A date, a datetime or a moment.
n
How many weeks.
returns
x moved by 7 × n calendar days.
raises OverflowException
When the result is outside the calendar.

See also: -weeks

+days

function

(: +days (-> %a int %a))
(+days x n)

Calendar days later.

x
A date, a datetime or a moment.
n
How many days.
returns
x moved by n days; a moment keeps its local time, which across a change of offset is not 24 hours a day.
raises OverflowException
When the result is outside the calendar.
(+days meeting 1)    ;; 2026-10-25T12:00:00+01:00[Europe/Stockholm]
(+hours meeting 24)  ;; 2026-10-25T11:00:00+01:00[Europe/Stockholm]

See also: -days, +hours

-years

function

(: -years (-> %a int %a))
(-years x n)

Years earlier.

x
A date, a datetime or a moment.
n
How many years.
returns
x moved back by n years.
raises OverflowException
When the result is outside the calendar.

See also: +years

-months

function

(: -months (-> %a int %a))
(-months x n)

Months earlier, clamped to the end of the month.

x
A date, a datetime or a moment.
n
How many months.
returns
x moved back by n months.
raises OverflowException
When the result is outside the calendar.

See also: +months

-weeks

function

(: -weeks (-> %a int %a))
(-weeks x n)

Weeks earlier.

x
A date, a datetime or a moment.
n
How many weeks.
returns
x moved back by 7 × n calendar days.
raises OverflowException
When the result is outside the calendar.

See also: +weeks

-days

function

(: -days (-> %a int %a))
(-days x n)

Calendar days earlier.

x
A date, a datetime or a moment.
n
How many days.
returns
x moved back by n days.
raises OverflowException
When the result is outside the calendar: (-days (date 1 1 1) 1) raises.

See also: +days

-date-period

function

(: -date-period (-> %a DatePeriod %a))
(-date-period x p)

A value moved back by calendar amounts.

x
A date, a datetime or a moment.
p
The period, every amount negated and then applied as +date-period applies it.
returns
x moved back by p.
raises OverflowException
When the result is outside the calendar.

See also: +date-period, -date-period/resolver

+hours

function

(: +hours (-> %a long %a))
(+hours x n)

Hours later.

x
A time, a datetime, a moment, an instant or a duration.
n
How many hours.
returns
x moved by n hours; a time wraps at midnight.
raises OverflowException
When n hours is outside ±29 227 years, or the result is outside the calendar.

See also: -hours, +duration, +days

+minutes

function

(: +minutes (-> %a long %a))
(+minutes x n)

Minutes later.

x
A time, a datetime, a moment, an instant or a duration.
n
How many minutes.
returns
x moved by n minutes; a time wraps at midnight.
raises OverflowException
When n minutes is outside ±29 227 years, or the result is outside the calendar.

See also: -minutes, +duration

+seconds

function

(: +seconds (-> %a long %a))
(+seconds x n)

Seconds later.

x
A time, a datetime, a moment, an instant or a duration.
n
How many seconds.
returns
x moved by n seconds; a time wraps at midnight.
raises OverflowException
When n seconds is outside ±29 227 years, or the result is outside the calendar.

See also: -seconds, +duration

+milliseconds

function

(: +milliseconds (-> %a long %a))
(+milliseconds x n)

Milliseconds later.

x
A time, a datetime, a moment, an instant or a duration.
n
How many milliseconds.
returns
x moved by n milliseconds; a time wraps at midnight.
raises OverflowException
When n milliseconds is outside ±29 227 years, or the result is outside the calendar.

See also: -milliseconds, +duration

-hours

function

(: -hours (-> %a long %a))
(-hours x n)

Hours earlier.

x
A time, a datetime, a moment, an instant or a duration.
n
How many hours.
returns
x moved back by n hours: 01:00 minus three hours is 22:00.
raises OverflowException
When n hours is outside ±29 227 years, or the result is outside the calendar.

See also: +hours, -duration

-minutes

function

(: -minutes (-> %a long %a))
(-minutes x n)

Minutes earlier.

x
A time, a datetime, a moment, an instant or a duration.
n
How many minutes.
returns
x moved back by n minutes.
raises OverflowException
When n minutes is outside ±29 227 years, or the result is outside the calendar.

See also: +minutes

-seconds

function

(: -seconds (-> %a long %a))
(-seconds x n)

Seconds earlier.

x
A time, a datetime, a moment, an instant or a duration.
n
How many seconds.
returns
x moved back by n seconds.
raises OverflowException
When n seconds is outside ±29 227 years, or the result is outside the calendar.

See also: +seconds

-milliseconds

function

(: -milliseconds (-> %a long %a))
(-milliseconds x n)

Milliseconds earlier.

x
A time, a datetime, a moment, an instant or a duration.
n
How many milliseconds.
returns
x moved back by n milliseconds.
raises OverflowException
When n milliseconds is outside ±29 227 years, or the result is outside the calendar.

See also: +milliseconds

-duration

function

(: -duration (-> %a Duration %a))
(-duration x d)

A value moved back by an exact length of time.

x
A time, a datetime, a moment, an instant or a duration.
d
The duration.
returns
x moved back by d, of x's type.
raises OverflowException
When the result is outside the calendar, or a duration outside ±29 227 years.

See also: +duration, duration-negate

+period

function

(: +period (-> %a Period %a))
(+period x p)

A value moved by a period: the calendar part, then the duration.

x
A datetime or a moment.
p
The period.
returns
x moved by p's calendar amounts and then by its duration.
raises OverflowException
When the result is outside the calendar.

See also: -period, period

-period

function

(: -period (-> %a Period %a))
(-period x p)

A value moved back by a period: the calendar part, then the duration.

x
A datetime or a moment.
p
The period.
returns
x moved back by p's calendar amounts and then by its duration.
raises OverflowException
When the result is outside the calendar.

See also: +period

duration->hours

function

(: duration->hours (-> Duration long))
(duration->hours d)

A duration in whole hours, toward zero.

d
The duration.
returns
The whole hours in it.

duration->minutes

function

(: duration->minutes (-> Duration long))
(duration->minutes d)

A duration in whole minutes, toward zero.

d
The duration.
returns
The whole minutes in it.

duration->seconds

function

(: duration->seconds (-> Duration long))
(duration->seconds d)

A duration in whole seconds, toward zero.

d
The duration.
returns
The whole seconds in it.

duration->milliseconds

function

(: duration->milliseconds (-> Duration long))
(duration->milliseconds d)

A duration in whole milliseconds, toward zero.

d
The duration.
returns
The whole milliseconds in it.

duration-negate

function

(: duration-negate (-> Duration Duration))
(duration-negate d)

A duration with its sign turned.

d
The duration.
returns
-d. It never raises, as every duration has a negation.

years-between

function

(: years-between (-> %a %a int))
(years-between a b)

Whole years from a to b, by their dates.

a
The start.
b
The end, of a's type.
returns
Whole months between them divided by 12, toward zero.
raises OverflowException
Near either end of the calendar, as ->date-in-zone-of raises.

See also: months-between

months-between

function

(: months-between (-> %a %a int))
(months-between a b)

Whole months from a to b, by their dates, as java.time counts them.

a
The start.
b
The end, of a's type.
returns
The whole months, toward zero.
raises OverflowException
Near either end of the calendar, as ->date-in-zone-of raises.

A month counts once the day of the month is reached, so (months-between 2026-01-31 2026-02-28) is 0, although (+months 2026-01-31 1) is 2026-02-28. In return, swapping the two dates only flips the sign.

See also: days-between, years-between

weeks-between

function

(: weeks-between (-> %a %a int))
(weeks-between a b)

Whole weeks from a to b, by their dates.

a
The start.
b
The end, of a's type.
returns
The days between them divided by 7, toward zero.
raises OverflowException
Near either end of the calendar, as ->date-in-zone-of raises.

See also: days-between

days-between

function

(: days-between (-> %a %a int))
(days-between a b)

Calendar days from a to b, by their dates only.

a
The start.
b
The end, of a's type.
returns
The days between the two dates: 23:00 to 01:00 the next day is 1.
raises OverflowException
Near either end of the calendar, as ->date-in-zone-of raises.
(days-between some-date (->date some-moment))   ;; to mix types, say which date

The calendar differences read the two dates only, of two values of one type (DateDiff). Between two moments in different zones, the second is read in the first one’s zone, as java.time does: 2026-10-25T20:00Z is the 25th in New York and the 26th in Tokyo, and those two moments are 0 days apart. So across zones, swapping the arguments also swaps the zone the dates are read in. For the time that passed, use hours-between.

See also: hours-between, months-between, ->date-in-zone-of

duration-between

function

(: duration-between (-> %a %b Duration))
(duration-between a b)

The time that passed from a to b.

a
The start: an instant or a moment.
b
The end: an instant or a moment, not necessarily of a's type.
returns
The duration between their instants, negative when b is before a.

Between two things with an instant (HasInstant): moments, instants, or one of each. 2026-10-25 00:00 to 2026-10-26 00:00 is 25 hours as Stockholm moments, as the clocks went back that night.

Two DateTime values have no instant and do not compile. Their wall-clock difference, which is not the time that passed across a change of offset, is spelled out by reading both as UTC: (hours-between (utc-datetime->instant a) (utc-datetime->instant b)) is 24.

See also: hours-between, days-between

hours-between

function

(: hours-between (-> %a %b long))
(hours-between a b)

Whole hours that passed from a to b, toward zero.

a
The start: an instant or a moment.
b
The end: an instant or a moment.
returns
The whole hours of duration-between.

See also: duration-between, days-between

minutes-between

function

(: minutes-between (-> %a %b long))
(minutes-between a b)

Whole minutes that passed from a to b, toward zero.

a
The start: an instant or a moment.
b
The end: an instant or a moment.
returns
The whole minutes of duration-between.

See also: duration-between

seconds-between

function

(: seconds-between (-> %a %b long))
(seconds-between a b)

Whole seconds that passed from a to b, toward zero.

a
The start: an instant or a moment.
b
The end: an instant or a moment.
returns
The whole seconds of duration-between.

See also: duration-between

milliseconds-between

function

(: milliseconds-between (-> %a %b long))
(milliseconds-between a b)

Whole milliseconds that passed from a to b, toward zero.

a
The start: an instant or a moment.
b
The end: an instant or a moment.
returns
The whole milliseconds of duration-between.

See also: duration-between

iso8601->date

function

(: iso8601->date (-> string (Option Date)))
(iso8601->date s)

Reads an ISO 8601 date, 2026-10-25.

s
The text.
returns
Some date, or None when s is not a whole, valid date in the extended format.

See also: text->date

iso8601->time

function

(: iso8601->time (-> string (Option Time)))
(iso8601->time s)

Reads an ISO 8601 time of day: 02:30, 02:30:00, 02:30:00.5 or 02:30:00,5.

s
The text.
returns
Some time, or None. A fraction has up to seven digits kept; more are read and dropped.

See also: text->time

iso8601->datetime

function

(: iso8601->datetime (-> string (Option DateTime)))
(iso8601->datetime s)

Reads an ISO 8601 date and time, 2026-10-25T02:30:00.

s
The text, with an upper case T.
returns
Some datetime, or None.

See also: text->datetime

iso8601->instant

function

(: iso8601->instant (-> string (Option Instant)))
(iso8601->instant s)

Reads an ISO 8601 date and time with an offset, converted to UTC.

s
The text: 2026-10-25T01:30:00Z, or any offset, such as +01:00.
returns
Some instant, or None, also when the instant is outside the calendar.

See also: text->instant

iso8601->moment

function

(: iso8601->moment (-> string (Option Moment)))
(iso8601->moment s)

Reads an ISO 8601 date and time with an offset and a zone in brackets.

s
The text: 2026-10-25T02:30:00+01:00[Europe/Stockholm].
returns
Some moment, or None.

The offset has to be one its zone has at that local time, so 2026-07-01T12:00:00+01:00[Europe/Stockholm] is None (July is +02:00), and so is any local time in a gap. A local time in an overlap reads at either of its offsets. An id the machine does not know is None.

See also: text->moment

iso8601->duration

function

(: iso8601->duration (-> string (Option Duration)))
(iso8601->duration s)

Reads an ISO 8601 duration: PT26H30M, -PT1.5S.

s
The text.
returns
Some duration, or None.

A duration has no days, since a day is a calendar amount: P1D is a DatePeriod, and None here. Only seconds take a fraction.

See also: iso8601->period

iso8601->date-period

function

(: iso8601->date-period (-> string (Option DatePeriod)))
(iso8601->date-period s)

Reads an ISO 8601 date period: P1Y2M3W4D, P1M-3D.

s
The text.
returns
Some period, or None. Each amount may have a sign of its own.

See also: iso8601->period

iso8601->period

function

(: iso8601->period (-> string (Option Period)))
(iso8601->period s)

Reads an ISO 8601 period with date and time parts: P1DT3H.

s
The text.
returns
Some period, or None. A T needs a time part after it.

See also: iso8601->date-period, iso8601->duration

instant->http-date

function

(: instant->http-date (-> Instant string))
(instant->http-date i)

Writes an instant as an HTTP date: Sun, 25 Oct 2026 01:30:00 GMT.

i
The instant.
returns
Its IMF-fixdate (RFC 9110). The fraction of a second is dropped.

See also: http-date->instant

http-date->instant

function

(: http-date->instant (-> string (Option Instant)))
(http-date->instant s)

Reads an HTTP date: Sun, 25 Oct 2026 01:30:00 GMT.

s
An IMF-fixdate (RFC 9110).
returns
Some instant, or None.

The day name has to be the date’s own. The two obsolete forms, RFC 850 and asctime, are not read.

See also: instant->http-date

date->text

function

(: date->text (-> Date (List DatePart) (#:names DateNames) string))
(date->text d parts #:names (parameter-ref current-date-names))

Writes a date in a format of your own.

d
The date.
parts
The format, a quoted list: '((year 4) "-" (month 2) "-" (day 2)).
#:names
The names month-name and the others write.
returns
The text.
raises ArgumentException
When a part is given more than one width, as (year 4 2) is, or a name is taken from a table without twelve months or seven weekdays.
(date->text d '((year 4) "-" (month 2) "-" (day 2)))            ;; 2026-10-25
(date->text d '(weekday-name ", " day " " month-name " " year))  ;; Sunday, 25 October 2026
(date->text d '(day "." (roman month) "." (roman year)))         ;; 25.X.MMXXVI

A format is a quoted list: a field’s name, a string or a character, each written as it stands. A number after a field’s name is a width, filled with zeros: (month 2) is 03. Without one, a number takes the digits it needs, and day and (day) are the same.

There is one format union and one writer per type, so the writers and their formats are:

WriterFormatFields
date->textDatePartyear, month, day, weekday (1 is Monday), yday, week, week-year; (roman year), (roman month), (roman day); month-name, month-abbr, weekday-name, weekday-abbr
time->textTimeParthours, hours12 (12 for midnight’s and noon’s hour), minutes, seconds, fraction, am-pm
datetime->textDateTimePartboth of those
moment->textMomentPartboth, offset (+01:00), zone (Europe/Stockholm), and unix and unix-ms
instant->textInstantPartthe fields of a DateTimePart, offset, unix and unix-ms

The mistakes are compile errors, with the list of the fields there are:

(date->text d '((year 4) (hours 2)))        ;; `hours` is not a tag of the union DatePart …
(datetime->text dt '((hours 2) offset))     ;; `offset` is not a tag of the union DateTimePart …
(date->text d '((roman (year 4))))          ;; RomanYear takes no argument …
(datetime->text some-moment '(year))        ;; a type error: write (->datetime some-moment)

A moment’s date is written with (date->text (->date m) ...), which says whose date it is. Writing has none of the limits reading has: a format that cannot read can still write. Beside year, week is written without a word, so '((year 4) "-W" (week 2)) writes 2027-01-01 as 2027-W53, a week 2027 does not have; write week-year beside week.

See also: text->date, time->text, datetime->text, moment->text, instant->text, DatePart

time->text

function

(: time->text (-> Time (List TimePart) (#:names DateNames) string))
(time->text t parts #:names (parameter-ref current-date-names))

Writes a time of day in a format of your own.

t
The time.
parts
The format, a list of TimePart.
#:names
The words am-pm writes.
returns
The text.
raises ArgumentException
When a part is given more than one width.
(time->text t '(hours12 ":" (minutes 2) " " am-pm))   ;; 1:05 PM

The fields are listed under date->text.

See also: text->time, date->text, TimePart

datetime->text

function

(: datetime->text (-> DateTime (List DateTimePart) (#:names DateNames) string))
(datetime->text dt parts #:names (parameter-ref current-date-names))

Writes a date and time in a format of your own.

dt
The datetime.
parts
The format, a list of DateTimePart: a date's fields and a time's.
#:names
The names the name fields and am-pm write.
returns
The text.
raises ArgumentException
When a part is given more than one width, or a name is taken from a table without twelve months or seven weekdays.

See also: text->datetime, date->text, ->datetime

moment->text

function

(: moment->text (-> Moment (List MomentPart) (#:names DateNames) string))
(moment->text m parts #:names (parameter-ref current-date-names))

Writes a moment in a format of your own.

m
The moment.
parts
The format, a list of MomentPart: its local date and time, offset, zone, unix and unix-ms.
#:names
The names the name fields and am-pm write.
returns
The text.
raises ArgumentException
When a part is given more than one width, or a name is taken from a table without twelve months or seven weekdays.
(moment->text m '((year 4) "-" (month 2) "-" (day 2) "T" (hours 2) ":" (minutes 2)
                  ":" (seconds 2) offset))   ;; 2026-10-25T02:30:00+01:00

A moment’s unix and unix-ms are its instant’s.

See also: text->moment, date->text

instant->text

function

(: instant->text (-> Instant (List InstantPart) (#:names DateNames) string))
(instant->text i parts #:names (parameter-ref current-date-names))

Writes an instant in a format of your own, in UTC.

i
The instant.
parts
The format, a list of InstantPart: a datetime's fields, offset, unix and unix-ms.
#:names
The names the name fields and am-pm write.
returns
The text.
raises ArgumentException
When a part is given more than one width, or a name is taken from a table without twelve months or seven weekdays.

An instant is written in UTC, and its offset is Z, so '((year 4) "-" (month 2) "-" (day 2) "T" (hours 2) ":" (minutes 2) ":" (seconds 2) offset) writes what ->str writes for a whole second: 2026-10-25T01:30:00Z.

See also: text->instant, date->text

text->date

function

(: text->date (-> string (List DatePart) (#:names DateNames) (Option Date)))
(text->date s parts #:names (parameter-ref current-date-names))

Reads a date in a format of your own, the one that wrote it.

s
The text.
parts
The format, as date->text takes it.
#:names
The names month-name and the others read.
returns
Some date, or None when the text does not fit the format or names no date.
raises ArgumentException
When the format cannot be read, whatever the text: see below.
(text->date "2026-10-25" '((year 4) "-" (month 2) "-" (day 2)))   ;; 2026-10-25
(text->date "Sunday, 25 October 2026"
            '(weekday-name ", " day " " month-name " " year))    ;; 2026-10-25
(text->date "25.X.MMXXVI" '(day "." (roman month) "." (roman year)))
(text->date "2026-10" '((year 4) "-" (month 2)))                  ;; 2026-10-01
(text->date "2026-W43" '((week-year 4) "-W" (week 2)))            ;; 2026-10-19

Each writer has a reader, which reads a text with the format that wrote it and answers an Option: text->date, text->time, text->datetime, text->instant, text->moment and text->moment/resolver. Each takes #:names as the writers do. What follows holds for all of them.

The parts are read in order, and the whole text has to be used. Text that does not fit is None, never an exception.

The date. The fields read make the date:

The time.

Formats that cannot be read. Each raises an ArgumentException when it is used to read, whatever the text, with a message that names the part:

Writing has none of these limits: a format that cannot read can still write.

Round trips. What a writer writes with a format that can be read, the reader of the same type reads back with that format. It gives back the value, or what the format keeps of it: without seconds, the minute; without a day, the 1st; with (fraction 3), the millisecond; with unix, the second, rounded toward the past. The tests write and read thousands of values of every type so, across the whole range and in zones with offsets such as Kathmandu’s +05:45 and Chatham’s +12:45.

Cost. A reader allocates nothing but a moment’s zone id. It walks the format part by part and checks it on every read, so it costs about ten times what the iso8601-> readers do, which stay the fast path for ISO 8601 text.

See also: date->text, text->time, text->datetime, text->instant, text->moment, iso8601->date

text->time

function

(: text->time (-> string (List TimePart) (#:names DateNames) (Option Time)))
(text->time s parts #:names (parameter-ref current-date-names))

Reads a time of day in a format of your own.

s
The text.
parts
The format, as time->text takes it.
#:names
The words am-pm reads.
returns
Some time, or None.
raises ArgumentException
When the format cannot be read: without hours, or with hours12 and no am-pm.
(text->time "1:05 PM" '(hours12 ":" (minutes 2) " " am-pm))   ;; 13:05:00

The rules are under text->date.

See also: time->text, text->date

text->datetime

function

(: text->datetime (-> string (List DateTimePart) (#:names DateNames) (Option DateTime)))
(text->datetime s parts #:names (parameter-ref current-date-names))

Reads a date and time in a format of your own.

s
The text.
parts
The format, as datetime->text takes it.
#:names
The names the name fields and am-pm read.
returns
Some datetime, or None. Without any time field it is midnight.
raises ArgumentException
When the format cannot be read, as text->date and text->time say.
(text->datetime "2026-10-25 02:30"
                '((year 4) "-" (month 2) "-" (day 2) " " (hours 2) ":" (minutes 2)))

See also: datetime->text, text->date

text->instant

function

(: text->instant (-> string (List InstantPart) (#:names DateNames) (Option Instant)))
(text->instant s parts #:names (parameter-ref current-date-names))

Reads an instant in a format of your own: UTC’s fields, unless the format has an offset.

s
The text.
parts
The format, as instant->text takes it.
#:names
The names the name fields and am-pm read.
returns
Some instant, or None.
raises ArgumentException
When the format cannot be read, as text->date says.
(text->instant "2026-10-25T02:30:00+01:00"
               '((year 4) "-" (month 2) "-" (day 2) "T" (hours 2) ":" (minutes 2) ":"
                 (seconds 2) offset))                             ;; 2026-10-25T01:30:00Z
(text->instant "@1792891800" '("@" unix))                         ;; 2026-10-25T01:30:00Z

An instant is its Unix time, or a date and a time. Those are UTC’s, as instant->text writes them, unless the format has an offset, and then they are the local time at that offset: the inverse of the writer, and no guess at a zone.

A text with an offset and no zone, as RFC 3339 writes, is read with text->instant and put in a zone with instant->moment: two steps, and no zone is guessed.

See also: instant->text, text->moment, instant->moment

text->moment

function

(: text->moment (-> string (List MomentPart) (#:names DateNames) (Option Moment)))
(text->moment s parts #:names (parameter-ref current-date-names))

Reads a moment in a format of your own, which names its zone.

s
The text.
parts
The format, as moment->text takes it. It needs zone.
#:names
The names the name fields and am-pm read.
returns
Some moment, or None.
raises ArgumentException
When the format has no zone, or cannot be read as text->date says.
(text->moment "2026-10-25 12:30 Europe/Stockholm"
              '((year 4) "-" (month 2) "-" (day 2) " " (hours 2) ":" (minutes 2) " " zone))

A moment is in the zone it reads, and a Unix time is put in that zone. A local time with an offset has to be at an offset the zone has then, as iso8601->moment demands: in Stockholm, 2026-07-01 12:00+01:00 is None, as July is +02:00, and 2026-10-25 02:30 reads at +02:00 and at +01:00, as the clocks go back that night. A local time without an offset is None in a gap or an overlap, as datetime->moment/opt answers.

A local time whose instant is outside the range, such as 0001-01-01 00:00 in Stockholm, is None, and so is a Unix time whose local time is, such as the first second in New York.

It never guesses a zone: a text with an offset and no zone is an instant, read with text->instant.

See also: moment->text, text->moment/resolver, text->instant, iso8601->moment

text->moment/resolver

function

(: text->moment/resolver (-> string (List MomentPart) Resolver (#:names DateNames) (Option Moment)))
(text->moment/resolver s parts r #:names (parameter-ref current-date-names))

Reads a moment in a format of your own, resolving a local time in a gap or an overlap.

s
The text.
parts
The format, as moment->text takes it. It needs zone.
r
What to do with a local time without an offset in a gap or an overlap.
#:names
The names the name fields and am-pm read.
returns
Some moment, or None.
raises ArgumentException
When the format has no zone, or cannot be read as text->date says.

As text->moment, except that a local time without an offset is found as datetime->moment finds it. Where the local time and its instant cannot both be in the calendar, it is None.

See also: text->moment, datetime->moment

Values

utc

value

(: utc TimeZone)

The UTC zone, which every machine has.

See also: timezone

current-clock

value

(: current-clock (Param Clock))

The clock now, today and the others read when not given #:clock.

(parameterize ((current-clock (fixed-clock t z)))
  (render-invoice order))   ;; every (now) and (today) inside answers t

A (Param Clock), starting as system-clock. A binding lasts only inside its parameterize and the fibers started there, so one test cannot leave a fake clock behind for the next.

See also: system-clock-in, fixed-clock, now

system-clock

value

(: system-clock Clock)

The machine’s clock, in the machine’s zone.

TimeProvider.System, and the value current-clock starts with. It is the wall clock, and steps back when the machine’s time is corrected.

See also: system-clock-in, current-clock

english-names

value

(: english-names DateNames)

English names: January, Jan, Monday, Mon, AM and PM.

See also: swedish-names, current-date-names

swedish-names

value

(: swedish-names DateNames)

Swedish names: januari, jan, måndag, mån, fm and em.

See also: english-names, current-date-names

current-date-names

value

(: current-date-names (Param DateNames))

The names table the writers and readers use when not given #:names.

A (Param DateNames), starting as english-names.

See also: english-names, swedish-names, DateNames