time: Date, Weekday and RFC 3339 parsing of Timestamp (gap 11, slice 1) #111
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
First slice of FIRST-PARTY.md gap 11 (calendar types and parsing for
std/time). Gap 11 is too large for one PR (wall-clock types, offsets, named zones, formats), so it is split; this issue is the part every other slice builds on:Date,Weekday, aTimeParseError, and reading aTimestampfrom RFC 3339 text.torb/sqlkeeps its privateDate,timestampOf,dateOfand Hinnant's algorithms until it can move onto these (not part of this issue).Follow-up slices: #387 (wall-clock
TimeOfDay/DateTime/UtcOffset/OffsetDateTime), #388 (HTTP-date and basic ISO 8601 formats), #389 (named time zones, design record first).Design (decided by the loop, see the comment of 2026-10-20)
All in
std/time/src/lib.trb(split intosrc/date.trbetc. if the file grows), re-exported from the package; no prelude change, no syntax change, no new native, no seed impact.Date: a day in the proleptic Gregorian calendar, years 0000 to 9999. Private fielddays(days since 1970-01-01), so every value is valid by construction.with Compare, Equals, Hash, Show.static fn of(year: Int64, month: Int64, day: Int64): Result<Date, TimeParseError>(fails for a month outside 1..12 or a day the month does not have)static fn fromDaysSinceEpoch(days: Int64): Result<Date, TimeParseError>andfn daysSinceEpoch(): Int64fn year(): Int64,fn month(): Int64,fn day(): Int64,fn weekday(): Weekday,fn isLeapYear(): Bool,static fn daysInMonth(year, month)fn plusDays(days: Int64): Result<Date, TimeParseError>(fails outside years 0000..9999) andfn daysUntil(other: Date): Int64; no operator overloads (aDateplus aDurationwould hide the day-versus-24-hours question)static fn parse(text: String): Result<Date, TimeParseError>readsYYYY-MM-DDonly (4-digit year, strict);show()writes the sameWeekday: enumMonday..SundaywithShow("Monday"),Compareby ISO order,fn number(): Int64(ISO: Monday = 1).TimeParseError: a record withmessage: StringandShow; this is whatDateandTimestampfail with. Naming followsNumberParseError.Timestamp.parse(text: String): Result<Timestamp, TimeParseError>, RFC 3339date-time:T,tor one space between date and time;Z/zor+hh:mm/-hh:mmoffset (hours 00-23, minutes 00-59;-00:00is UTC); the offset is applied, the result is the UTC instant..; more than nine are truncated (not rounded), documented.:60) is refused with a clear message:Timestampis POSIX time and has no leap seconds. Relaxing that later is not breaking.Int64of nanoseconds (1677-09-21T00:12:43.145224192Z to 2262-04-11T23:47:16.854775807Z) fails with a message naming that range. WideningTimestampis out of scope; dates outside the range are whatDateis for.ShowofTimestampalready writes RFC 3339 UTC;Timestamp.parse(t.show())must round-trip for everyTimestamp.Timestamp.date(): Date(the UTC day),Date.startOfDay(): Result<Timestamp, TimeParseError>(00:00:00 UTC, fails outside the Timestamp range).Timestamp's private helperscivilDate,floorDivided,paddedare reused (not duplicated);daysSinceEpochis Hinnant'sdays_from_civil.std/tomlkeeps its ownTomlDate(TOML-specific rules); moving it ontoDateis a separate refactor, not here.Seed
Touches the seed: no. No syntax change, no native, no driver flag, no std name the compiler looks up by string. One compiler edit:
capabilityOfImportincompiler/src/package/archive.trblists the value names ofstd/timethat reach no capability; addDate,Weekday,TimeParseErrorthere (plus a case incompiler/tests/capability.test.trb), otherwiseuse Date from "std/time"reports theclockcapability. Becausecompiler/srcchanges, this is a normal tier A bootstrap in CI, no seed refresh.Acceptance criteria
Date,Weekday,TimeParseErrorandTimestamp.parse,Timestamp.date,Date.startOfDayas designed above, instd/timecapabilityOfImportlistsDate,Weekday,TimeParseErroras capability-free;compiler/tests/capability.test.trbcovers it (use Date from "std/time"reaches nothing,use Clock from "std/time"still reachesclock)std/time/tests/(date.test.trb,parse.test.trb): leap years (1900, 2000, 2024, 2100), year 0000 and 9999,Datebefore 1970, weekday of known dates (1970-01-01 Thursday, 2000-02-29 Tuesday, 2024-12-31 Tuesday), invalid dates (2023-02-29, month 13, day 0),Timestamp.parsewithZ,z,+02:00,-05:30,-00:00, lowercaset, space separator, 0/1/3/9/12 fraction digits, leap second refused, missing offset refused (2026-09-27T08:15:00has no offset), trailing garbage refused, out-of-range refused with the range in the message, round tripTimestamp.parse(t.show()) == Ok tfor the epoch, a negative value, and the Int64 limits1985-04-12T23:20:50.52Z,1996-12-19T16:39:57-08:00,1990-12-31T23:59:60Zrefused)docs/standard-library/time.mddocuments the new declarations withtrb checkexamples;torb docs skill docs skillsregenerated and committedFIRST-PARTY.mdgap 11 is marked "partly done" with the date, naming what this slice delivered and the follow-up issues; it is marked done only when the last slice landsfeat(time): Date, Weekday and RFC 3339 parsing of Timestampanddocs(time): ...; gatesagreen in forge CIDependencies
None.
Moved from the planning lists of the docs; the design record stays the specification.
[loop:torbscript] intake: in scope (author orion, label loop), source FIRST-PARTY.md gap 11, not a duplicate. No dependencies. next: waiting (refine queued, ready buffer full) until 2026-10-10T04:00:00Z
[loop:torbscript] claim refine by session:loop-torbscript-20261020 until 2026-10-20T00:00Z
time: calendar types and RFC 3339 parsingto time: Date, Weekday and RFC 3339 parsing of Timestamp (gap 11, slice 1)[loop:torbscript] decided by the loop: gap 11 is split into four issues, #111 (this one:
Date,Weekday,TimeParseError,Timestamp.parse), #387 (TimeOfDay,DateTime,UtcOffset,OffsetDateTime), #388 (HTTP-date, basic ISO 8601), #389 (named time zones, design recorddocs/design/TIME.mdfirst). Why: the whole gap is four independent pieces of API plus a tz-database design question; one PR would be far over two commits, and the first slice is what the drivers andTimestampconsumers need soonest.Decisions in #111, all technical, none an owner category:
Datestores days since 1970-01-01 privately (valid by construction, cheapCompare/Hash), accessors are methods (year(), ...), years 0000-9999; no operator overloads, becauseDate + Durationhides the day-versus-24-hours question;plusDaysinstead.Timestamp): a name likeDatein every file would clash with user types; it isuse Date from "std/time", andcapabilityOfImport(compiler/src/package/archive.trb) lists it as capability-free so the import does not reportclock. Not a seed change: a pure list addition, no syntax, native or looked-up std name.Timestampis anInt64of nanoseconds, i.e. 1677-09-21 to 2262-04-11; text outside it fails with a message naming the range. Widening the representation would change a published value type, so out of scope;Datecovers the wider years.T/t/space,Z/z/numeric offset applied to give UTC, offset mandatory (a text without one is not an instant), fraction truncated beyond nine digits,:60leap second refused (POSIX time has none; accepting later is non-breaking).TimeParseError, named afterNumberParseError; parsing isstatic fn parseas instd/toml'sTomlDate.TomlDatestays; moving it ontoDateis a separate refactor with no user value now.std/time/src/lib.trbare reused, not duplicated;torb/sqlmoves onto the public API later.Seed: untouched. Two Conventional Commits at most (feat + docs).
[loop:torbscript] refine: #111 is ready as slice 1 of gap 11 (Date, Weekday, TimeParseError, Timestamp.parse; body and acceptance criteria rewritten, decisions in the comment above); the rest is split into #387 (wall-clock types, offsets), #388 (HTTP-date, basic ISO 8601) and #389 (named zones, design record first), all label loop, not yet refined. Seed untouched. Tokens: about 85k (sonnet, estimate), next: implement
[loop:torbscript] claim implement by session:loop-torbscript-20261020 until 2026-10-20T04:46Z
[loop:torbscript] implement: Date, Weekday, TimeParseError, Timestamp.parse, Timestamp.date and Date.startOfDay in std/time, capability-free in capabilityOfImport, tests, docs, regenerated skills/ and gap 11 marked partly done in FIRST-PARTY.md. PR #390 (two commits). It also fixes an overflow in Timestamp.show at the first instant of the range. Forge CI is green on
8927a418(gates, tier A, bootstrap linux-x64, playground, commit messages, pdisc). Token usage: roughly 1.1M across the implementation worker, including one context compaction. next: review--explain-copieshint line for nested writes (take out, put back) #302