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.