time: named time zones (design record first) #389
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Last slice of FIRST-PARTY.md gap 11: named time zones (
Europe/Berlin), with daylight saving. Too large and with too many open design questions to implement directly, so this issue's first deliverable is a design record. Depends on the wall-clock slice (DateTime,UtcOffset,OffsetDateTime).What the design record has to decide
docs/design/TIME.md(new, with pdisc frontmatter, linked from the design index and from FIRST-PARTY.md gap 11), decided by the loop with measurements where there are numbers:filescapability and does not exist on Windows or in the playground), or both with a defined order. Numbers: binary size cost of the full database vs. a compact transition table.TimeZonevalue must stay capability-free if the data is compiled in (capabilityOfImport, RELEASE.md 7.8).Disambiguation(Earlier,Later,Reject), no silent default except documented.TimeZone.named(String): Result<TimeZone, TimeParseError>,ZonedDateTime, conversion toTimestamp/OffsetDateTime,Showas RFC 9557 (2026-10-25T02:30:00+01:00[Europe/Berlin]).Acceptance criteria
docs/design/TIME.mdexists with the decisions above, each with reasoning and numbers where measurable, and gap 11 in FIRST-PARTY.md links itloop, linked here) filed from the record, each small enough for one PRtorb docschecks green in forge CIDependencies
#387 (wall-clock types, offsets).
[loop:torbscript] claim refine by session:loop-torbscript-20261025 until 2026-10-25T00:00:00Z
[loop:torbscript] intake: in scope (open, label
loop, authororion). No duplicate: a keyword search of all issues (zone, tz, tzdata, daylight, time zone, zoneinfo) finds only the finished slices #111, #387 and #388 of gap 11. No collision with a design record:docs/design/has no TIME record yet,FIRST-PARTY.mdgap 11 names this issue as the one open slice, and its dependency #387 (DateTime,UtcOffset,OffsetDateTime) is closed. It is a design-record-first item, so the next stage is refine.[loop:torbscript] decided by the loop: the design questions of this issue, so that
docs/design/TIME.mdrecords them and does not reopen them. No owner question applies (all of it is technical design insidestd/time; nothing is public policy, cost, or a break of a published contract). Numbers are measured on tzdata 2026c compiled withzic -b slim(447 distinct zone files behind 598 names).std/time, no system zoneinfo. A generatortools/tzdata/reads the pinned tzdata release and emits TorbScript constants, exactly like the Unicode tables (tools/fetch-unicode.shpins every input file by SHA-256, the generated output is reproducible from them). Why: the system database is absent on Windows and in the playground, differs per machine (the same program would answer differently), and would need thefilescapability. tzdata is public domain. Update path: a new tzdata release is a regenerated data file and astdpatch release; the module exposes the pinned version as a constant. A runtime override from the system's zoneinfo is not instd; it can come later as a separatefiles-capability package without changingTimeZone.Timestamppays for the data; if it does, the data goes to its own module that onlyTimeZoneimports.TimeZoneandZonedDateTimeare capability-free values, likeDuration,InstantandTimestamp(RELEASE.md 7.8, "every imported name decides").TimeZone.namedis pure: the data is in the program. OnlyTimestamp.now()and its kin stayclock. The record adds the two names to the list incapabilityOfImportanddocs/tooling/package-capabilities.mdas a slice (the registry test compares both).Disambiguationis an enum ofEarlier,LaterandReject, and it is a required argument, never a default.DateTime.inZone(zone, disambiguation): Result<ZonedDateTime, TimeParseError>. Worked example for Europe/Berlin: 2026-10-25 02:30 happens twice (+02:00, then +01:00):Earlieris the +02:00 instant,Laterthe +01:00 one. 2026-03-29 02:30 does not exist:Earlierreads it with the offset after the gap and gives 01:30+01:00,Laterreads it with the offset before and gives 03:30+02:00 (both move by the gap's length);Rejectfails in both cases. The infallible direction isTimestamp.inZone(zone)(an instant has exactly one local time).TimeZone.named(String): Result<TimeZone, TimeParseError>(case-sensitive IANA names,UTCand fixed offsets viaTimeZone.utc()andTimeZone.fixed(UtcOffset)),ZonedDateTime(instant, zone, the offset valid at that instant, derivedDateTime),toTimestamp(),toOffsetDateTime(),Showas RFC 9557 (2026-10-25T02:30:00+01:00[Europe/Berlin]) andparseof the same, where the offset must be valid in the zone at that local time (it picks the instant in an overlap; otherwise the parse fails). Calendar arithmetic (days, months) works on the local time and takes aDisambiguation;Durationarithmetic works on the instant. Details and names of the remaining members belong to the record.Mm.w.d[/time]form; the time can be outside 0 to 23 (/24,/-1,/2:45) and some zones have a negative DST. This is cheaper than the alternative of expanding to 2037 (the fat form stores 27 184 transitions, 46 % more, and is still wrong from 2038). NoJnornday forms are needed by the data; the record says so and the parser rejects them.Seed impact: none for the record (docs only). The record's slices table must flag it per slice; the expected answer is none, since the data is TorbScript constants and the generator is a
.trbprogram.[loop:torbscript] decided by the loop: three corrections to the issue text so that it fits the contract and the repository, each reasoned.
TIME.mdends with a numbered slices table (each slice one PR landing as one or two Conventional Commits, with its seed impact and its dependency); the loop files one slice as an issue at a time, just in time, when the one before it starts, after a duplicate search. Suggested slice order for the record to confirm or fix: (1) generator, pinned tzdata and the data constants; (2)TimeZone,named,offsetAtwith the POSIX rule evaluation; (3)ZonedDateTime,Disambiguation, conversions; (4) RFC 9557Showandparse; (5) capability table entries and the guide page.TIME.md. Every record indocs/design/opens with# Titleand a**Status: ...**line, none carries frontmatter. The new record does the same, gets its entry indocs/design/index.mdlike the others, and the docs command regenerates the list.Closes #389(the issue is not an epic any more: the slices table of the record is the checklist, and the slices become their own issues as above).Definition of ready, as it stands now:
docs/design/TIME.mddecides named time zones forstd/time, with the six decisions of the comment above as its backbone (each with reasoning and the numbers given there; more measurement only where the record needs it).docs/design/TIME.mdexists, status linedesigned, not built, with data source, size, capability, disambiguation, API, POSIX rules, and a slices table; (b) gap 11 inFIRST-PARTY.mdlinks the record and no longer calls named zones open without a pointer; (c)docs/design/index.mdlists it; (d) any TorbScript in the record follows the canon (no semicolons, command calls,fn area(): Intstyle) and thetorbscriptskill; (e)torb docschecks andtorb docs skill docs skills --checkgreen in forge CI (regenerateskills/if the docs change reaches it).docs/design/FIRST-PARTY.md, depends on #387 (done); the approach follows the Unicode tables (tools/fetch-unicode.sh) and RELEASE.md 7.8.docs(time): design record for named time zones.[loop:torbscript] refine: triaged (in scope, no duplicate, no design-record collision), decided the open design questions of the issue by the loop with measured tzdata 2026c numbers (data compiled in as generated TorbScript constants, ~76 KB compact for full history; capability-free values; required
Disambiguation; POSIX rules in the first version), corrected the acceptance criteria to the contract and repo conventions (slices table instead of up-front issues, no frontmatter on design records,Closes #389), set labelready. Seed impact: none. Size: one PR, one commit. No owner question. Next: implement (docs-only PR onloop/389-*). Tokens: about 120k (RE worker, estimate).[loop:torbscript] claim implement by session:loop-torbscript-20261025 until 2026-10-25T04:15:00Z
[loop:torbscript] implement: checkpoint, context limit reached before the commit. Done: worktree
torbscript-language-389-time-zones-record(branchloop/389-time-zones-record, from main) holds, uncommitted, the newdocs/design/TIME.md(status linedesigned, not built, sections 1-9: data compiled in via pinned tzdata+zic slim+.trbgenerator, 76 KB full history, capability-free values, requiredDisambiguationwith the Berlin table, API, POSIX rules, 6-slice table, all seed impact none), the gap 11 link inFIRST-PARTY.md, and the entry indocs/design/index.md(front matter list, generated list, scope paragraph). Verified against the code:torb buildemits nothing unreachable and module consts have no initialization, so the data costs only parse/check time, hence its ownzone-data.trb. Still to do: check lines over 120 columns in TIME.md (19 lines, tables may be fine), commitdocs(design): add the named time zones recordwithCloses #389, push, open the PR,ci-wait.sh, fixtorb docs/skills --checkdiffs if CI shows any (no skills mention design records, so likely none). Next: implement (continue). Tokens: about 110k (loop worker).[loop:torbscript] implement: PR #476 is open (head
2f8c36ef, bootstrap linux-x64 green, tier A and gates still queued); the finishing worker timed out while waiting on CI, claim released. next: waiting (forge CI on PR 476; green: hand over to review, red: fix round) until 2026-10-25T01:30:00Z