Goal
Build a small scheduling core — timestamp parser, instant/civil-time model, future-event store, and recurrence expander — where every classic time bug is forced into the open and killed by a test. Time's type system (instant vs. civil vs. zoned, duration vs. period) stops being advice and becomes compiler-checked, test-proven muscle memory.
Subject: full brief & instructions
Practices
- Time, timezones & datetime — the type system of time, the future-civil-event storage rule, DST gaps and overlaps, clock injection; this exercise is that note's drills consolidated into one build.
- Testing strategies — the injected
Clockand deterministic fixtures are the testability discipline applied to time.
Milestones
- RFC 3339 parser. Hand-parse
2026-08-08T10:00:00+02:00(and friends) into civil components + offset without the language's datetime parser; reject the invalid. Show an offset round-trip loses the zone —+02:00is a moment's offset, notEurope/Paris. - The type system, as types. Distinct
Instant,LocalDateTime, andZonedDateTimetypes, plusDuration(physical seconds) vs.Period(calendar units). Conversionlocal + zone → instantruns against injectable zone rules and forces a resolution strategy where the mapping is a gap (0 candidates) or an overlap (2 candidates) — no silent default. - The future meeting. Store "09:00 in Paris, next June" as local time + IANA zone, never a baked UTC instant. Drill: swap in a changed rule set (a government abolishes DST) and show your stored meeting still fires at 09:00 wall time — while a UTC-baked copy is now an hour wrong.
- DST-safe recurrence expander. Expand an RRULE subset ("daily/weekly at HH:MM local") across the spring-forward gap (2:30 doesn't exist) and the fall-back overlap (2:30 happens twice), under explicit, named policies for each. Demonstrate period ≠ duration: "+24 hours" drifts across a transition; "+1 day" doesn't.
- The hostile-zone suite. Inject the clock, then run the acceptance fixtures:
Pacific/Kiritimati(UTC+14),Asia/Kolkata(+05:30), both US and EU DST edges, and a year of "daily digest at 02:30" with zero drift and no double-fires.
Stretch goals
Australia/Lord_Howe— the 30-minute DST shift that breaks "DST is always one hour".- Calendar arithmetic edges: Jan 31 + 1 month, leap-day birthdays on recurring events.
- Emit RFC 9557 (
2026-06-12T09:00:00+02:00[Europe/Paris]) so serialization stops losing the zone.
Related
- DST-safe scheduler — subject — the full work statement, gap/overlap semantics, and acceptance checks for this exercise.
- Time, timezones & datetime — cites
this exercise in its
# Practicesection. - UTF-8 codec + text-processing CLI — the sibling fundamentals build: same "implement the spec by hand" method, applied to text.