Source guide/temporal.simd

1# Temporal Types
2
3Dates, times, and anything that depends on a calendar sit inside
4a thicket of subtle questions: which calendar, which time zone,
5what counts as "today", how should this render under a given
6locale. Silo's temporal types address the thicket by making the
7relevant parameters type-level — a date's calendar is part of its
8type, not something that falls out of a format string — and by
9delegating the hard parts (calendar arithmetic, zone rules, locale
10rendering) to ICU4X.
11
12## Calendar-parameterised dates
13
14A `Date` carries its calendar as a type parameter:
15
16```silo
17:fn today ( -> (Date Gregorian) ) +Temporal ... :end
18```
19
20`(Date Gregorian)` is a Gregorian-calendar date. `(Date Buddhist)`
21is a Buddhist date. `(Date Japanese)` is a Japanese-era date. The
22type tracks the calendar, which means you can't accidentally mix
23two calendars in one computation — the compiler catches it.
24
25Calendar conversion is a `From` impl:
26
27```silo
28my-date ((Date Buddhist) .from)
29```
30
31Every calendar that ICU4X supports has a calendar type here, and
32every pair of calendars has a `From` impl that goes through the
33Julian-day canonical intermediate.
34
35### Shortening `DateTime` in your own code
36
37Writing `(DateTime Gregorian)` in every signature gets old fast
38when a project only cares about one calendar. The idiom is to
39add a local alias at the top of your own module:
40
41```silo
42:alias(pub) DateTime (DateTime Gregorian)
43```
44
45This shadows the stdlib's `DateTime` type-constructor with a
46zero-argument alias that means "Gregorian DateTime." Inside
47your module, bare `DateTime` now works in signatures, and the
48stdlib's generic `(DateTime C)` is still available whenever you
49need a different calendar. Projects that work with several
50calendars alias a differently-named type for each.
51
52The standard library keeps the explicit parameterised form so
53calendar-sensitive code in the library itself — calendar
54conversion, era handling, locale-specific month-count logic —
55can't accidentally commit to one calendar.
56
57## `Time`, `DateTime`, `TimeZone`
58
59`Time` is a time-of-day (no date, no zone) with fields
60`.h .m .s .ms`:
61
62```silo
630 0 30 14 Time                 # ⌊Time{ h ↦ 14  m ↦ 30  s ↦ 0  ms ↦ 0 }⌉
64```
65
66Construction is ordinary record construction: the first-popped
67(top of stack) becomes the first-declared field, so values are
68pushed in **reverse declaration order** — ms, s, m, h — and the
69constructor pops them in `.h .m .s .ms` order. Reading right-to-
70left in the source you'd see "14 (hours) 30 (minutes) 0 (seconds)
710 (ms)", which is how a Silo person reads constructor arguments.
72
73`DateTime` bundles a date and a time, parameterised by calendar:
74
75```silo
76:fn now ( -> (DateTime Gregorian) ) +Temporal ... :end
77```
78
79A `DateTime` **without** a time zone is a "wall-clock" value —
80"14:30 local time on March 5th" — with no commitment to what UTC
81offset that maps to. Useful for storing human-facing dates that
82shouldn't drift when a zone changes (a birthday, a wedding date, a
83meeting title).
84
85A `DateTime` **with** a time zone is a "point in time" — a value
86that maps to a specific UTC instant. The zone is a third type
87parameter:
88
89```silo
90:fn now-utc ( -> (DateTime Gregorian TimeZone) ) +Temporal ... :end
91```
92
93Two-parameter `DateTime` is wall-clock; three-parameter is
94zoned. The compiler distinguishes them, and neither can be
95silently converted to the other — the conversion is explicit,
96because it depends on which zone you assume.
97
98## Locale-aware rendering
99
100Temporal values render through locale-aware format traits —
101`{Date}`, `{DateTime}`, `{Time}` — which consult the
102[`CurrentLocale` aspect](./14-localization.simd#locale) that
103[chapter 14](./14-localization.simd) introduces in detail. The
104chapter-14 machinery is what you use here; nothing temporal
105adds to the locale story beyond participating in it:
106
107```silo
108now "{DateTime}" format
109# ⌊"April 10, 2026, 9:30:12 AM"⌉   under an en-US locale
110
111'de-DE (Locale .try-from) .unwrap :with CurrentLocale
112  now "{DateTime}" format
113  # ⌊"10. April 2026, 09:30:12"⌉   date-format conventions follow de-DE
114:end
115```
116
117Everything else about locales — what `Locale` actually is, how
118you build one, how the aspect defaults — lives in the
119localisation chapter. From here on we concentrate on temporal
120types themselves.
121
122## Durations and instants
123
124Alongside the calendar types Silo ships:
125
126- `Instant` — a monotonic point on the UTC timeline. Good for
127  "what time is it now" and "how much time elapsed between X and
128  Y".
129- `UDuration` — an unsigned duration. "Three minutes." Can't be
130  negative, constructed via factory words like `3 .from-mins`.
131- `IDuration` — a signed duration. The result of subtracting two
132  `Instant`s or two `DateTime`s. Can be positive, negative, or
133  zero.
134
135`UDuration` and `IDuration` are distinct types: the compiler
136makes you handle the difference between "how long" and "which
137direction". Subtracting two `UDuration`s gives an `IDuration`;
138adding a `UDuration` to an `Instant` gives another `Instant`.
139
140## The `+Temporal` effect
141
142Words that read the current time carry the `+Temporal` effect:
143
144```silo
145:fn now ( -> (DateTime Gregorian) ) +Temporal ... :end
146:fn today ( -> (Date Gregorian) ) +Temporal ... :end
147:fn sleep ( UDuration -> ) +Temporal ... :end
148```
149
150This is just an ordinary effect
151([chapter 10](./10-effects.simd)). The host provides the
152`+Temporal` handler. A test host can install a fake clock; a
153replay host can feed pre-recorded timestamps. Pure computations on
154already-obtained temporal values don't need the effect.
155
156## ICU4X integration
157
158Practically everything interesting in this chapter comes from
159ICU4X:
160
161- Calendars (`Gregorian`, `Buddhist`, `Japanese`, `Chinese`,
162  `Hebrew`, `Islamic`, etc.) are ICU4X calendars.
163- `TimeZone` rules come from ICU4X's time-zone database.
164- `Locale` is an ICU4X locale value.
165- The `DateTime` format trait goes through ICU4X's
166  `DateTimeFormatter`, which handles locale-specific skeletons,
167  the Gregorian→Hijri switch, eras, and everything else you'd
168  otherwise have to get wrong.
169- Calendar arithmetic (adding a month, finding the next Monday)
170  uses ICU4X's calendar operations, not naive day-counting.
171
172As with strings ([chapter 13](./13-strings.simd)), the practical
173consequence is that correct behaviour for the 99% case comes for
174free, and the places where Silo's stdlib differs from ICU4X are
175just the thin layer that exposes everything as Silo types with the
176right trait impls.
177
178## Key points
179
180- `Date`, `DateTime`, and `TimeZone` are parameterised by their
181  calendar and (optionally) time zone. The type keeps mixed
182  calendars out.
183- Two-parameter `DateTime` is wall-clock; three-parameter is
184  zoned. The distinction is a type difference.
185- Locale-aware format traits (`{Date}`, `{DateTime}`, `{Time}`)
186  consult the [`CurrentLocale`](./14-localization.simd#locale)
187  aspect — same machinery as every other locale-aware piece of
188  the standard library.
189- Calendar arithmetic, time-zone rules, and localised rendering
190  all go through ICU4X. You don't reach for a second library.
191- Reading the current time requires `+Temporal`; pure
192  computations on already-obtained temporal values don't.
193- `UDuration` (unsigned) and `IDuration` (signed) are distinct
194  types to keep "how long" and "which direction" from blurring.
195
196Next: [modules and imports](./16-modules.simd) — the `:use`
197system, visibility modes, and the no-orphan-rule discipline.