Skip to content

docs(adr): ADR-0053 — Phase 2 detailed design (reference-timezone model)#1975

Merged
os-zhuang merged 1 commit into
mainfrom
docs/adr-0053-phase2-design
Jun 16, 2026
Merged

docs(adr): ADR-0053 — Phase 2 detailed design (reference-timezone model)#1975
os-zhuang merged 1 commit into
mainfrom
docs/adr-0053-phase2-design

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What

Adds a Phase 2 — Detailed Design section to ADR-0053 now that Phase 1 landed (#1968Field.date is a tz-naive YYYY-MM-DD string at the driver boundary).

This is the plan for review, not a commitment to ship. Each slice is independently revertable and gated behind "reference timezone unset → UTC" (today's behavior). Refs ADR-0053, #1928.

Highlights

  • Two-axis reframecompute-tz (today()/daysFromNow, analytics bucketing, cron day-boundaries) vs render-tz (datetime display): distinct sources, distinct risk. Conflating them is the chief risk of Phase 2.
  • Reference-timezone resolver — precedence (user pref → tenant-scoped org default → UTC), the single resolveExecutionContext chokepoint, a new ExecutionContext.timezone field, and the three execution branches (interactive / scheduled job / flow).
  • Shared DST-safe primitive reused from preference-resolver's Intl.formatToParts pattern — no hand-rolled offset math.
  • Three decisions, with rationale:
    • D1today()/daysFromNow()/daysAgo() return a Date at UTC-midnight of the reference-tz calendar day (forced by cel-js hydration; explicit callout of the "local-midnight-as-instant" trap that would re-introduce the silent-miss bug). Side-effect: fixes the "keeps wall-clock time" defect for free.
    • D2 — tz-aware analytics buckets in-memory, uniformly; do not emit dialect-specific date_trunc … AT TIME ZONE (SQLite/MySQL have no tz support → cross-driver bucket skew).
    • D3 — wire report schedules onto the existing croner+tz adapter rather than delete the dormant cron_expression/timezone fields (the enforce resolution the spec-liveness gate wants).
  • applyFormulaPlan blind spot — read-time formula fields evaluate with no now/ctx/tz; worth fixing regardless of timezone.
  • Per-slice implementation map (6 slices, file-level), open prerequisites (cel-js duration() support; email rendering bypasses the formatter pipeline), and the feature-flag rollback.

Follow-up

The 6 implementation slices are being filed as separate tracked issues (linked under #1928).

🤖 Generated with Claude Code

Adds a "Phase 2 — Detailed Design" section to ADR-0053 capturing the
implementation plan now that Phase 1 (date-only storage, #1968) has landed:

- Reframe into two axes — compute-tz (today()/daysFromNow, analytics
  bucketing, cron) vs render-tz (datetime display) — with distinct sources
  and risk profiles.
- Reference-timezone resolver: precedence (user pref → tenant-scoped org
  default → UTC), the single resolveExecutionContext chokepoint, a new
  ExecutionContext.timezone field, and the three execution branches
  (interactive / scheduled job / flow).
- Shared DST-safe primitive reused from preference-resolver's
  Intl.formatToParts pattern (no hand-rolled offset math).
- Three decisions with rationale:
  D1 today() returns a Date at UTC-midnight of the reference-tz calendar day
     (forced by cel-js hydration; avoids the local-midnight-as-instant trap);
  D2 tz-aware analytics buckets in-memory uniformly, not dialect date_trunc
     (SQLite/MySQL have no tz support → cross-driver bucket skew);
  D3 wire report schedules onto the existing croner+tz adapter rather than
     delete the dormant cron_expression/timezone fields (liveness enforce).
- The applyFormulaPlan read-time blind spot (no now/ctx/tz) to fix anyway.
- Per-slice implementation map (file-level), open prerequisites (cel-js
  duration support; email rendering bypasses the formatter pipeline), and
  the "reference tz unset → UTC" feature-flag rollback.

Refs ADR-0053, #1928. Builds on Phase 1 (#1968).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 16, 2026 4:18pm

Request Review

@os-zhuang

Copy link
Copy Markdown
Contributor Author

Phase 2 implementation slices (tracked)

Filed from the per-slice map in this ADR section. Each is independently revertable and gated behind "reference tz unset → UTC":

  1. ADR-0053 Phase 2 · Slice 1: reference-timezone resolver + ExecutionContext.timezone #1978 — reference-timezone resolver + ExecutionContext.timezone (foundational; blocks 3–5)
  2. ADR-0053 Phase 2 · Slice 2: thread context into applyFormulaPlan (read-time formula fields) #1979 — thread context into applyFormulaPlan (independent; worth doing on its own)
  3. ADR-0053 Phase 2 · Slice 3: timezone-aware today()/daysFromNow()/daysAgo() #1980 — tz-aware today()/daysFromNow()/daysAgo() (produces the shared partsInTz util)
  4. ADR-0053 Phase 2 · Slice 4: render datetime in reference timezone (formatters + email) #1981 — render datetime in reference tz (formatters + email)
  5. ADR-0053 Phase 2 · Slice 5: timezone-aware analytics date bucketing #1982 — tz-aware analytics date bucketing (depends on 1 + 3)
  6. ADR-0053 Phase 2 · Slice 6: activate report-schedule cron + timezone (liveness enforce) #1983 — activate report-schedule cron + timezone (self-contained; liveness enforce)

Cron day-boundaries (sys_job) need no change — already tz-wired via croner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant