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

vercelBot commented Jun 16, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 16, 2026 4:18pm

Request Review

@os-zhuang

Copy link
Copy Markdown
ContributorAuthor

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/m

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@os-zhuang