feat(formula): timezone-aware today() / daysFromNow() / daysAgo()#1998
Merged
Conversation
ADR-0053 Phase 2 D1. today()/daysFromNow()/daysAgo() become calendar-day functions resolved in a reference timezone (threaded ExecutionContext.timezone -> EvalContext.timezone -> CEL stdlib). Each returns the reference-tz calendar day as a UTC-midnight Date — the representation consistent with Field.date hydration, the SQL driver's date-filter normalization, and Phase 1 storage — so `record.date == daysFromNow(n)` matches in-memory too. Timezone math uses Intl.DateTimeFormat (DST-safe). BEHAVIOR CHANGE: daysFromNow(n)/daysAgo(n) previously kept now's wall-clock time; they now drop it and return the calendar day at midnight (the ADR "defect #3" fix). today() is unchanged at UTC. Sub-day offsets: now() + duration("Nh"). objectql threads execCtx.timezone into applyFormulaPlan (read-time formula fields) and applyFieldDefaults (default-value expressions). With no reference tz configured the zone is UTC. Tests: tz-aware today()/daysFromNow()/daysAgo() (America/New_York day boundary); daysFromNow midnight semantics; existing wall-clock assertions updated to calendar-day. formula 121 green, objectql 639 green. Part of #1980. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 16 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Jul 18, 2026
os-zhuang
added a commit
that referenced
this pull request
Jul 18, 2026
Add acceptance tests for the timezone-aware today()/daysFromNow()/daysAgo() functions (compute-tz core of ADR-0053 Phase 2, decision D1). The implementation already shipped (#1998/#2001/#2006); these lock the issue's criteria and pin the DST-boundary + equality behavior: - AC1: today() at 2026-06-16T02:00Z in America/Los_Angeles == UTC-midnight of 2026-06-15. - AC3: reference tz unset vs 'UTC' is byte-for-byte the pre-Phase-2 behavior for all three functions. - AC2: calendar days are correct across both 2026 US DST transitions (spring-forward Mar 8, fall-back Nov 1); a Field.datetime instant compares equal to daysFromNow(n) across DST; a Field.date string matches via the hydration-safe idioms (ordering operators, date(), daysBetween()). - A characterization guard documents the known cel-js equality limitation: a bare `date-string == today()` silently returns false because cel-js's isEqual hard-codes `string == X` to false. This is timezone-independent and cross-cutting; the fix belongs in the data layer (hydrate date fields to Date where field types are known) and is tracked as a separate follow-up. Test-only; no changeset (no functional change). Claude-Session: https://claude.ai/code/session_01SuiM565BZ3TR1VD3prMguB Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #1980. The compute-tz core of ADR-0053 Phase 2, building on the resolver (#1978).
What
today()/daysFromNow()/daysAgo()become calendar-day functions resolved in a reference timezone. The zone is threadedExecutionContext.timezone(#1978) →EvalContext.timezone→ CEL stdlib, and the timezone math usesIntl.DateTimeFormat(DST-safe, no hand-rolled offsets).Each returns the reference-tz calendar day expressed as a UTC-midnight
Date— decision D1 in the ADR. This is the one representation consistent with:Field.datestring hydrates in CEL (Date.parse("2026-06-15")= UTC midnight),getUTC*),So
record.close_date == daysFromNow(30)now matches in-memory too — not just at the storage boundary. (Avoids the "local-midnight-as-instant" trap that would re-introduce the off-by-one.)daysFromNow(n)/daysAgo(n)previously keptnow's wall-clock time (daysFromNow(30)at10:00Z→…T10:00:00Z). They now drop the time and return the calendar day at midnight (…T00:00:00Z) — the ADR-0053 "defect #3" fix, and what makes date-equality work in-memory.today()is unchanged at UTC (already start-of-day).now() + duration("Nh")(cel-jsduration()is supported — confirmed by existing tests).UTC:today()is byte-for-byte unchanged; only thedaysFromNow/daysAgomidnight-truncation differs from before.The two pre-existing tests that asserted the old wall-clock behavior are updated to the calendar-day semantic. A scan found no other package relies on the old time-carrying behavior.
Wiring
objectqlthreadsexecCtx.timezoneinto read-time formula evaluation (applyFormulaPlan, from #1979) and default-value expressions (applyFieldDefaults).Testing
today()/daysFromNow()/daysAgo()across theAmerica/New_Yorkday boundary;daysFromNowmidnight semantics; UTC default unchanged fortoday().@objectstack/formula121 green,@objectstack/objectql639 green; DTS build + typecheck clean.minor.🤖 Generated with Claude Code