Skip to content

docs(adr): ADR-0053 — date is a tz-naive calendar day, datetime is an instant (#1928) - #1962

Merged
os-zhuang merged 1 commit into
mainfrom
docs/adr-0053-date-datetime-semantics
Jun 16, 2026
Merged

docs(adr): ADR-0053 — date is a tz-naive calendar day, datetime is an instant (#1928)#1962
os-zhuang merged 1 commit into
mainfrom
docs/adr-0053-date-datetime-semantics

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Proposes the industry-standard date/datetime split to root-fix the silent date-equality bug from #1874/#1928 (the one templates hit and we mitigated with day-window ranges + the #1950 lint).

Problem (verified, file:line in the ADR)

  • Silent equality miss: the write path stores a Field.date as a full timestamp (formatInput doesn't normalize), but the filter path coerces query values to YYYY-MM-DD (coerceFilterValue). So date == daysFromNow(N) compares "2026-08-15T17:24Z" vs "2026-08-15" → never matches. Ranges work only by lexicographic ISO accident.
  • Timezone off-by-one: a date stored as UTC-midnight renders as the previous day for negative-offset users.
  • daysFromNow/daysAgo keep wall-clock time (unlike today()); today() is UTC, not user/org TZ — and sys-user-preference.timezone exists but is never read.

Decision (staged by risk)

  • Phase 1 (low risk, do first): Field.date becomes a YYYY-MM-DD string end-to-end via driver write/read normalization — aligns storage with the filter layer's already-existing date-only contract. Fixes equality with no new semantics; read-normalization repairs legacy rows with no migration.
  • Phase 2 (needs review): a reference-timezone model (org default + user override) so today()/daysFromNow() and datetime rendering are timezone-aware.

Includes a mainstream-platform comparison (Salesforce / Postgres / java.time / Temporal / Airtable — all converge on the same split), migration/rollback, non-goals, and rejected alternatives.

Docs-only; no code change in this PR.

🤖 Generated with Claude Code

… instant (#1928)
Proposes the industry-standard date/datetime split to root-fix the silent
date-equality bug (#1874/#1928): a Field.date is stored as a full timestamp
(write path doesn't normalize) while the filter path coerces query values to
YYYY-MM-DD, so `date == daysFromNow(N)` never matches; and a UTC-midnight date
renders off-by-one across timezones.
Decision, staged by risk:
- Phase 1 (low risk): Field.date is a YYYY-MM-DD string end-to-end (driver
write/read normalization), aligning storage with the filter layer's existing
date-only contract — fixes equality with no new semantics.
- Phase 2 (needs review): reference-timezone model so today()/daysFromNow() and
datetime rendering are tz-aware (wires up the unused sys-user-preference.timezone).
Includes verified file:line evidence of the three-layer asymmetry, a
mainstream-platform comparison (Salesforce/Postgres/java.time/Temporal/Airtable),
migration/rollback, non-goals, and rejected alternatives.
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 10:12am

Request Review

@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Jun 16, 2026
@os-zhuang
os-zhuang merged commit 296a0ed into mainJun 16, 2026
13 checks passed
@os-zhuang
os-zhuang deleted the docs/adr-0053-date-datetime-semantics branch June 16, 2026 11:07
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