Prod counts below are the 2026-08-17 snapshot. DBeaver on a Central machine uses session
America/Chicago. Naive TIMESTAMP ::date / ::time are safe. (timestamptz)::date is not.The split
Docs and Prisma comments said call clocks were UTC Instants, rendered inEvent.timezone / Organization.timezone (Null = UTC). Runtime stored the opposite for almost every product writer: venue wall-clock digits in a naive TIMESTAMP(3) with a fake Z (17:00 → 2026-08-05 17:00:00).
Will’s SQL rule (naive timestamps are UTC Instants; product conversion belongs in the app) is still the default. Call-time runtime is the exception. Treating 17:00Z as an Instant and projecting it through Chicago is the Mapache email bug (12:00–6:00 PM vs crew 5:00–11:00 PM).
Event.timezone (or org) null does not mean the digits are Instants. It means no IANA label is stored. Readers then assume UTC. Flex create without metadata.timezone writes 'UTC' — the same lie, not Instant proof.
Organization.timezone has no API writer. 17180000 fills it when Events collapse to one real IANA. Mixed orgs stay null unless already written in prod. Do not treat a still-null org TZ as Instant proof.
Target after alignment
Two corrections, never mixed:
Writer timeline
created_at names a writer era only if the row was never saved again. A later header save re-encodes the clocks.
Empty windows in the 2026-08-17 snapshot (
o_early_z, a_early_z, then-empty post-#1328 Flex) were no-ops. Later #1328 / #1387 writes fill e_after_flex_wallclock with Formula A walls — restore from the quote Instant.
What the migration already rewrites
20260817180000_event_clock_alignment — prove, then rewrite. Never convert a whole window.
Sibling IANA is a minority path: 12 orgs / 314 events
single_iana, 165 orgs / 596 events have no IANA, 1 org is Chicago+Detroit.
Leftovers that are classified
No more inventory needed. Do not convert. Already Instant — leave (do not Formula-B): FlexstartTime = show_start_at (10); avail device-offset 5→4 (15 — migration nulls as all-day, not wall→Instant); respondedAt.
20260817180000_event_clock_alignment owns the cutover (no six-file split). Hour-0 availability prefixes and after-cutoff evenings Formula B in this file (upper bound < 2026-09-15). Device Instant evenings leave. Remaining unlabeled Events after fill have no startTime. RoS has 0 clocks. Require Event.timezone later; this file does not SET NOT NULL. Phase 4/5 branch code (not prod): notifications, documents, duplicate/RoS encode, inbox, Google/offer topology (one resolved zone on create + resync), Staffing Board Event-IANA encodes, mobile schedule past_midnight + per-row civil dates, portal/storefront resolved timezone on the API contract, engagement event_timezone, timeline civil-day, and many client readers — deploy with the migrations. 20260819170000_event_schedule_item_formula_b writes schedule-item Formula B SQL (hour 10–23, inverted overnight ends including exact 00:00:00, invalid Event IANA → org, five dump ids skip-as-Instant; ceiling < 2026-09-15) — not applied in prod, and no rows convert while convert_schedule_wall is empty. This pass (branch only): MOBILE-01, NOTIFY-01, CAL-03, CLIENT-01, CAL-02, WEB-02, MOBILE-02, CONTRACT-01. See merge / deploy.
Fresh dump prod-postgres-20260819-pm.dump leftovers (classify only — not converted this pass): 145 Event evening starts with no IANA; 874 schedule evenings before Jul 15 floor; 65 in-window schedule evenings with no Event/org IANA; 686 Events with neither Event nor org IANA; MIG-02/03/04 empty (0 rows each). Classifier convert sets: 35 Event evening (not Flex-equal); 109 schedule evening (not Event-equal, not skip-listed); 5 schedule skip-as-Instant; 7 other low-hour schedule starts unconverted.
#379mobile Events (0 starts).Organization.timezone, ledger dates.
Still open
Prove wall-clock (or already-Instant) before picking a convert zone.Zone ladder
Event.timezoneif it is a real IANA (notUTC).flex_quotes.metadata->>'timezone'(skipUTC).tz-lookuponEventlat/lng, thenVenueviaEvent.venue_id(skip0,0). Same helper web uses on save. api-events has no server lookup; a one-off pass can run outside Postgres.- Sibling Event IANA (mode per org) — minority.
Organization.timezone— required. Single-IANA fill in17180000; mixed / leftover-Event orgs stamped in prod 2026-08-17 and 2026-08-18; leftover empty orgs Chicago-filled then SET NOT NULL. Product writer exists (create defaults UTC; settings update). ENG-588 replaces Chicago placeholders with Clerk / PostHog where known.Venue.address.state(IL/Illinois, …).- Default
America/Chicago— label fill only, after a sample of venues agrees.
FlexQuote.venue_id is a bare string, not a Prisma FK.
The one multi-IANA org (Chicago + Detroit) is why venue coords beat an org-level CT default. Query 19 is the human check; summarize, do not paste the grid.
How to read the leftover queries
Paste-safe counts: queries 1–17 on the alignment page. Human look, do not paste names / cities / org ids — send a one-liner:Out of scope
- ENG-581 / PR #1370 —
LedgerItem.datecalendar day, payable snap,20260307leftovers. - API / client writers and readers — Phase 1 Event save passes Instants through; Phase 4/5 branch code (not prod) formats/rebases call clocks through Event IANA (Staffing encodes, mobile schedule civil-day +
past_midnight, portal/storefront timezone on the API contract, one Google zone on create/resync, projection shift-by-gap). This pass (branch only): cancellation civil dates (NOTIFY-01), accepted ICS duration (CAL-03), RoS second-save reseed (CLIENT-01), resolved Event DTOtimezone(CAL-02), mobile civil-day grouping (MOBILE-01), CRM/DayStripPreview readers (WEB-02/MOBILE-02),event_timezonetype (CONTRACT-01). Deploy migrations + reader cutover together. Import/CRM and HubSpot-Flex n8n inbound paths stay a later pass. - Rewriting applied migrations — never edit
20260307. This work is a new file,20260817180000_event_clock_alignment.
Related
- ENG-580
- Event clock writeup (ENG-580)
- Event clock cohort alignment — time bounds, migration guards, leftover SQL
- Availability and engagement clock semantics — locked writer/accept contract
- Database — never edit applied migration SQL
- ENG-581