Skip to main content

max / goingson

2.4 KB · 43 lines History Blame Raw
1 -- Events carry their own timezone semantics instead of inheriting the reading
2 -- machine's zone.
3 --
4 -- Before this, an event was a bare UTC instant and every recurrence rule was
5 -- expanded against `system_tz()` at read time. That made the schedule a property
6 -- of the machine doing the reading: the same database opened in Denver and in
7 -- Lisbon produced different local times AND different weekdays, because a rule
8 -- anchored at 01:00 UTC is Monday in Denver and Tuesday in Lisbon.
9 --
10 -- Three kinds, following iCalendar's distinction:
11 --
12 -- relative civil wall clock, no zone. "06:00 wherever I am." Follows the
13 -- user; this is what a personal routine (wake, gym, errands) means.
14 -- local civil wall clock + an IANA zone. "10:00 America/Denver", correct
15 -- across that zone's DST whoever is reading. iCalendar's TZID.
16 -- absolute a fixed UTC instant. A moment, displayed in whatever zone you are
17 -- in. What every row was implicitly treated as until now.
18 --
19 -- Storage: for `relative` and `local` the civil columns are the truth and
20 -- `start_time`/`end_time` hold a materialized UTC projection, kept so every
21 -- existing range query, index, and ORDER BY keeps working untouched. For
22 -- `absolute` the UTC columns are the truth and the civil ones stay NULL.
23 --
24 -- The materialization is derived, never authoritative. For `local` it is
25 -- deterministic (civil + zone). For `relative` it depends on where the user
26 -- currently is, so it is recomputed when the system zone changes -- which is
27 -- also why the civil columns, not the UTC ones, are what a sync peer should
28 -- trust when the two disagree.
29 --
30 -- Deliberately behaviour-preserving: every existing row becomes `absolute`,
31 -- which is precisely how it was already being treated. Reclassifying a routine
32 -- as `relative` is a separate, deliberate act on the user's own data, not
33 -- something a migration should guess for every machine that runs it.
34
35 ALTER TABLE events ADD COLUMN tz_kind TEXT NOT NULL DEFAULT 'absolute';
36 ALTER TABLE events ADD COLUMN timezone TEXT;
37 ALTER TABLE events ADD COLUMN start_local TEXT;
38 ALTER TABLE events ADD COLUMN end_local TEXT;
39
40 -- Partial index: the zone-change recompute pass touches only relative rows, and
41 -- on a calendar of mostly fixed appointments that is a small slice of the table.
42 CREATE INDEX IF NOT EXISTS idx_events_tz_kind ON events(tz_kind) WHERE tz_kind != 'absolute';
43