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.
| Type | What it is | Built on |
|---|---|---|
Date | a calendar date, no zone | DateOnly |
Time | a time of day, no zone | TimeOnly |
DateTime | a date and a time, no zone | DateTime, Kind always Unspecified |
Instant | a point on the global timeline | a long of UTC ticks |
TimeZone | an IANA zone, such as Europe/Stockholm | TimeZoneInfo |
Moment | a local datetime, its UTC offset, and its zone | a struct of the three |
Duration | a fixed length of time: hours, not calendar days | TimeSpan |
DatePeriod | calendar amounts: years, months, weeks, days | a struct of four int |
Period | a date period and a duration: 1 day and 3 hours | a struct of the two |
Clock | where now comes from, and its zone | the 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.
| Trait | Method | Date | Time | DateTime | Moment | Instant | Duration |
|---|---|---|---|---|---|---|---|
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->date | 2026-10-25 |
iso8601->time | 02:30, 02:30:00, 02:30:00.5, 02:30:00,5 |
iso8601->datetime | 2026-10-25T02:30:00 |
iso8601->instant | 2026-10-25T01:30:00Z, or any offset, converted to UTC |
iso8601->moment | 2026-10-25T02:30:00+01:00[Europe/Stockholm] |
iso8601->duration | PT26H30M, -PT1.5S |
iso8601->date-period | P1Y2M3W4D, P1M-3D |
iso8601->period | P1DT3H |
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.
- 100 ns. A tick is the finest unit.
- Years 1 to 9999, and the proleptic Gregorian calendar throughout.
- The zone data is the machine’s. On Linux and macOS,
.NET reads the IANA database from the operating system
(
/usr/share/zoneinfoon Linux). A slim container image may have none, and then(timezone "Europe/Stockholm")isNone. On Windows, .NET maps an IANA id onto Windows’ own zone data, which keeps little history, so dates before the 2000s may get today’s rules (Brussels’ 1914 gap is not there). - Offsets are whole minutes. Before standard time, a zone used local mean time, with an offset in seconds: Stockholm’s was +1:12:12. .NET keeps whole minutes only, so a moment before about 1900 can be up to a minute off the tz database.
- Offsets stop at ±14:00. .NET caps them there. Nine zones had local mean time beyond it: Alaska’s before 1867 (Sitka’s +14:58:47 reads as +14:00), and Manila, Guam, Saipan and Palau before 1845 (Manila’s −15:56:08 reads as −14:00, so the day it skipped is 22 hours and 3 minutes long rather than 24). Those moments are off by up to two hours.
- No leap seconds, and no other calendars. Names come in
English and Swedish, or from a
DateNamesof your own.
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.
- An instant is a
longof UTC ticks, not a UTCDateTime.DateTime.Equalsignores the Kind, so aDateTimesaying 12:00 local time compares equal to 12:00 UTC although the two are hours apart. Alonghas no Kind to get wrong. The cost: the range check on every addition is this module’s. - The value types are opaque. Nobody can build a moment
whose offset its zone never has, such as
2026-07-01T12:00+01:00[Europe/Stockholm](July is +02:00). Every moment comes from a function that checked it. - Periods are transparent. Any mix of numbers is a valid
period (1 month and 40 days is fine, since months differ in length),
so there is nothing to protect, and
(record-ref p months)works. Equality is field by field:(= (date-period #:weeks 1) (date-period #:days 7))is#f. - Elapsed time is measured between instants only.
(hours-between a b)on twoDateTimevalues would answer the wall-clock difference: 24 hours for 2026-10-25 00:00 to 2026-10-26 00:00, where 25 passed in Stockholm. Stored local times look like timestamps, so that answer would be taken for the time that passed, and code generic over anything with a duration between could not tell the two apart. So it does not compile, as in NodaTime, and the wall-clock reading is written out as a reading in UTC.DateTimeis notHasInstanteither, since(->instant some-datetime)would have to guess a zone. - Calendar differences read dates only, and count months as
java.time does.
(days-between 2026-01-01T23:00 2026-01-02T01:00)is 1, although two hours passed;hours-betweenis for elapsed time.(months-between 2026-01-31 2026-02-28)is 0, although(+months 2026-01-31 1)is 2026-02-28. In return, swapping two dates only flips the sign. - Two moments are compared in one zone, the first one’s.
Read each in its own zone, one instant would be a day apart from
itself in New York and in Tokyo. Both arguments have one type for
the same reason: a date against a moment would read the moment’s
local date without saying so, and
(->date m)says it. ->weekdaycounts as ISO 8601 does, Monday 1 to Sunday 7, as the text this module reads and writes is ISO 8601. It is not called->wday: C’stm_wday, Ruby’swdayand Racket Gregor’s->wdaycount Sunday as 0, so(= (->wday d) 0)written from habit would compile and never be true. For Sunday as 0, write(% (->weekday d) 7).- A week number comes with a year of its own,
->week-year. Printed beside->year, 2027-01-01 would be week 53 of 2027, a week 2027 does not have, and 2025-12-29 week 1 of 2025, a year early. The two are separate functions rather than one pair, since asking which week it is usually wants the number only; the two years differ in at most three days at either end of a year. - A format is a quoted list, not a pattern string. In a
pattern string a letter’s case changes its meaning: Java’s
YYYYis the week-based year whereyyyyis the year, soYYYY-MM-ddwrites 2025-12-29 as 2026-12-29, andmmis minutes whereMMis months. Here a field has a name, a wrong name is a compile error that lists the right ones, and text needs no escaping, being a string of its own. The cost: a format is longer thanyyyy-MM-dd, and cannot arrive as text from a configuration file; a program chooses among formats it holds. - One format union and one function per type. That is what
makes
hoursin a date’s format a compile error. A moment’s date is written with(date->text (->date m) ...), which says whose date it is, as a calendar difference asks for->datetoo. - A format that could read a text two ways is refused, not
read greedily. With
'(year month day), January 11 and November 1 of 2026 are both written2026111, so no reader can tell which was meant. Read greedily,2026110, which the same format writes for January 10, would be November 0, andNone. Trying every split instead would still have to pick one of the two dates. So the format is refused when it is used to read, and the message says what reads one way: a width, or text between the parts. The cost: a format that is fine for the dates a program has, such as'(year month day)for dates whose month and day both have two digits, is refused anyway. - A field left out is its first value, rather than
refused. HTML’s month input sends
2026-10and its week input2026-W43, and a text without a day means the month itself; reading it as the 1st, and a week as its Monday, gives aDatethat stands for it with no second function. The cost: thatDateis the month’s first day, not a month, and written back with a format that has a day it says01. - Month, day and
ydaycount withinyear, andweekwithinweek-year. The two years differ in up to three days at each end of a year, so a format that mixes them up is right most of the year and wrong near New Year, as Java’sYYYY-MM-ddis. Read with'((year 4) "-W" (week 2)),2026-W01would be January 1, the Thursday of week 1, and2026-W43nothing. Such a format is refused whatever the text, so the mix-up shows the first time the format is used. text->momentnever guesses a zone. RFC 3339’s2026-10-25T02:30:00+01:00has an offset, and +01:00 is Stockholm’s, Paris’s, Lagos’s and a dozen others’; a day later the first two are still at +01:00 and Lagos too, but in March they part. A moment read in a guessed zone would do its calendar arithmetic by the wrong rules, a month later and silently. So a moment’s format needszone, and a text without one is an instant, whichinstant->momentputs in a zone the program names. A text’s offset is not a zone either: it is checked against the zone read, asiso8601->momentchecks it.- An instant’s fields are UTC’s unless its format has an
offset. That is what
instant->textwrites, so the two are inverses, and it is the one reading that needs no zone: a program that stores UTC text writes and reads it with one format. A text in some local time without an offset is aDateTime, and becomes an instant only through a zone the program names, withdatetime->moment. - A moment’s
=compares every field, andcompareorders by instant, then local time, then zone id, so the two agree. 12:00+02:00[Europe/Stockholm] and 10:00+00:00[UTC] are one instant and two values; a sorted set keeps both, side by side. For the same instant, compare(->instant a)and(->instant b). - The resolvers are a closed set. A resolver of your own
(on a gap, go back to the last valid time) needs 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
/optcovers refusing. - Leaving the range raises.
(+days (date 9999 12 31) 1)raises rather than answering anOption, which would force amatchafter every addition. Constructors and readers answerNone, since bad input is expected there. - Calendar units take
int, clock units takelong. No date is two billion days away, but anintof seconds runs out at 68 years. now/momentanswers aMoment. AnInstanthas no zone, so now in Stockholm cannot be one.- The clock is a keyword argument, defaulting to a
parameter read at each call. A test binds
current-clockaround code it did not write:(parameterize ((current-clock (fixed-clock t z))) (render-invoice order))makes every(now)and(today)inside answert, however deep, with no clock passed down through every function between. A binding lasts only inside itsparameterizeand the fibers started there, so one test cannot leave a fake clock behind for the next, as it could with a global clock set and reset. It is a parameter rather than an effect by the prelude’s rule: a clock is a value the caller reads, as a random source is. The cost: a function that calls(now)without#:clockdoes not say in its signature that it reads the time. - The zone travels with the clock. There is no
current-zone parameter, so a test that fixes the clock fixes
todaytoo: under a fixed clock in Stockholm,(today)is Stockholm’s date, whether the test runs in Stockholm or on a build machine set to UTC. With a zone parameter of its own, a test could fix the instant and still get the machine’s date.#:tzasks about another zone, so there are no/zonetwins. - A manual clock is a
Clock, not a type of its own. Soclock-advance!on a fixed clock is an error when it runs rather than when it compiles. In return, a manual clock goes wherever a clock does,current-clockand#:clockincluded, with no conversion. - The prelude’s
monotonic-msis another clock. It counts milliseconds from an unspecified start, has no time of day, and never steps back, which is what measuring an interval needs. The two share no name. DateTimeis this module’s.(std clr-ord)ordersSystem.DateTimeand the other .NET time types without exporting a name for them, so a module that imports both has oneDateTime, and code at the edge to .NET writesSystem.DateTime. Renaming this oneLocalDateTime, as java.time and NodaTime do, would have asked forLocalDateandLocalTimebeside it.
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
ClockDateDateArithDateDiffDateNamesDateNumberDatePartDatePeriodDateTimeDateTimePartDurationGapHasDateHasInstantHasTimeInstantInstantPartMomentMomentPartOverlapPeriodResolverRomanNumberTimeTimeArithTimePartTimeZone
+date-period+date-period/resolver+days+duration+hours+milliseconds+minutes+months+period+seconds+weeks+years->date->date-in-zone-of->datetime->day->hours->instant->milliseconds->minutes->month->nanoseconds->seconds->time->timezone->unix->utc-offset->week->week-year->weekday->yday->year-date-period-date-period/resolver-days-duration-hours-milliseconds-minutes-months-period-seconds-weeks-yearsadjust-timezoneclock-advance!clock-timezonedatedate+time->datetimedate->textdate-perioddatetimedatetime->momentdatetime->moment/optdatetime->textdays-betweendurationduration->hoursduration->millisecondsduration->minutesduration->secondsduration-betweenduration-negatefixed-clockhours-betweenhttp-date->instantinstant->http-dateinstant->momentinstant->textinstant->unix-millisecondsinstant->unix-secondsinstant->utc-datetimeiso8601->dateiso8601->date-periodiso8601->datetimeiso8601->durationiso8601->instantiso8601->momentiso8601->periodiso8601->timemanual-clockmilliseconds-betweenminutes-betweenmoment->textmonths-betweennownow/momentperiodresolverseconds-betweensystem-clock-intext->datetext->datetimetext->instanttext->momenttext->moment/resolvertext->timetimetime->texttimezonetimezone-idtimezone-offset-attodayunix-milliseconds->instantunix-seconds->instantutc-datetime->instantweek-dateweeks-betweenweeks-in-yearyears-between
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.
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).
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.
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.
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->strwrites.
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)
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 |
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).
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.
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.
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.
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]
-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.
+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.
+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.
+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.
-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.
-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:
| Writer | Format | Fields |
|---|---|---|
date->text | DatePart | year, 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->text | TimePart | hours, hours12 (12 for midnight’s and noon’s hour), minutes, seconds, fraction, am-pm |
datetime->text | DateTimePart | both of those |
moment->text | MomentPart | both, offset (+01:00), zone (Europe/Stockholm), and unix and unix-ms |
instant->text | InstantPart | the fields of a DateTimePart, offset, unix and unix-ms |
(fraction n)isndigits of the second’s fraction, cut rather than rounded, and zeros past the seventh:(fraction 9)is nanoseconds. A barefractionis ISO 8601’s, point included:.5, and nothing at all for a whole second, so'((hours 2) ":" (minutes 2) fraction)reads well either way.(roman year)isMMXXVI, and 1900 isMCM. Numerals stop atMMMCMXCIX, so a later year goes on addingM: 4000 isMMMM. A numeral takes no width.unixis the Unix time in seconds andunix-msin milliseconds, both rounded toward the past, asinstant->unix-secondsrounds: half a second before 1970 is-1and-500.- A format kept in a variable says which kind it is, since nothing
else tells the list what it holds:
(def (: stamp (List DateTimePart)) '(...)).
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.
- Text and characters match exactly, case included.
- A number is ASCII digits only. Without a width it is one digit up
to the field’s most; with
(field w)it is at leastw, and at mostwor the field’s most, whichever is more. The most digits are 4 foryearandweek-year, 3 foryday, 1 forweekday, and 2 for the others. A number takes as many digits as there are, up to its most, and never gives one back. - A name, and
am-pm, matches its table’s own spelling, case included:oktober, notOktober, andPM, notpm. When two names match, the longer one is read. - A Roman numeral is read only in the form it is written:
MCM, notMDCCCC, andIV, notIIII. (fraction n)is exactlyndigits, and digits past the seventh are read and dropped. A barefractionis ISO 8601’s: nothing, or a point or a comma and at least one digit.unixis an optional minus and up to 12 digits, andunix-msup to 15. A time outside the years 1 to 9999 isNone, asunix-seconds->instantanswers.offsetisZ, or+hh:mmor-hh:mmwith an optional:ss.zoneis the longest run of the characters an IANA id is made of (ASCII letters and digits,/,_,-and+), and an id the machine does not know isNone.
The date. The fields read make the date:
- year, month and day; or year and
yday, the ordinal date 2026-298; orweek-year,weekandweekday, the week date 2026-W43-7. - A field the format leaves out is its first value: no
dayis the 1st, and noweekdayis Monday. So what HTML’s<input type="month">sends,2026-10, reads with'((year 4) "-" (month 2))as 2026-10-01, and what its<input type="week">sends,2026-W43, reads with'((week-year 4) "-W" (week 2))as that week’s Monday, 2026-10-19. - Every other field read has to be the date’s own. A weekday name
that is not the date’s, a
weekorydaybeside a calendar date that says another, or a Roman year beside a numeric one that says another, isNone. So is a value no date has: February 30, week 53 of 2027, year 0.
The time.
hours, orhours12andam-pm, where 12 AM is the hour 00 and 12 PM the hour 12. Minutes, seconds and the fraction left out are 0.am-pmbesidehourshas to agree with it, and two fractions have to agree to the fewer digits of the two.- A datetime is a date and a time, and without any time field it is midnight.
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:
- A date without a year. A date’s format needs
year,(roman year)orweek-year:'(month "/" day)cannot say which year. - A field without its own kind of year. Month, day and
ydaycount withinyear, andweekwithinweek-year.'((year 4) "-W" (week 2))would read2026-W43as nothing, and2026-W01as January 1, which is that week’s Thursday; it is refused, and a week is read withweek-year. A field of the other kind may stand beside a date its own kind makes, and is checked against it:'((year 4) "-" (month 2) "-" (day 2) " W" (week 2))reads. - A time without hours. A time’s format needs
hours, orhours12witham-pm, and so does a datetime’s that has minutes, seconds, a fraction oram-pm. hours12withoutam-pm: 1:05 could be either.- A moment without a zone.
text->moment’s format needszone. - A Unix time beside another field of the instant.
unixorunix-mssays the whole instant, and a second field could say another, so a format that reads one has no other field but text and, for a moment,zone.'(unix " " zone)reads, and'(unix " " (year 4))and'(unix " " unix-ms)are refused. - A text that reads two ways. A part that can be of more
than one length (a number without a width or with a width below its
most digits, a Roman numeral, a bare fraction,
unix,zone, or anoffset, which may have seconds) followed by a part that can begin with a character it could also take.'(year month day)reads2026110as January 10 or as November 0, so it is refused.'((year 4) (month 2) (day 2))reads, and so does'(year "-" month "-" day). A bare fraction takes a point and the digits after it, a zone the characters of an id, and an offset a colon, so none of those may follow them:'(offset "[" zone "]")reads. - A names table without twelve months or seven weekdays, when the format reads a name from it, as when it writes one.
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
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