You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
today()/daysFromNow()/daysAgo() return a Date at UTC-midnight of the reference-tz calendar day — new Date(Date.UTC(y, m, d)) with (y,m,d) computed in the reference tz. Not a date-only string; not local-midnight-as-instant (that trap re-introduces the silent-miss bug — cel-js's hydrateOverloadStrings rehydrates the field's date-only string to UTC midnight, so today() must match that).
Scope
Add timezone?: string to EvalContext — packages/formula/src/types.ts.
Replace startOfDayUtc/addDaysUtc (stdlib.ts:18-29) with tz-aware equivalents built on a shared partsInTz/calendarDayUtc util extracted from the proven Intl.formatToParts pattern in packages/services/service-messaging/src/preference-resolver.ts:347. No hand-rolled offset math (DST).
Side-effect: also fixes the "keeps wall-clock time" defect of daysFromNow/daysAgo.
Acceptance criteria
With reference tz America/Los_Angeles, at 2026-06-16T02:00Z, today() == the UTC-midnight Date of 2026-06-15.
record.due_date == today() and == daysFromNow(n) match the right calendar day across DST boundaries.
Confirm cel-js supports duration() + Timestamp arithmetic (the documented now() + duration("Nh") escape hatch). No duration usage exists in the formula package today.
Depends on
Slice 1 (timezone value); produces the shared partsInTz util reused by slice 5.
The compute-tz core shipped across #1998 (the slice), #2001 (shared calendarPartsInTz util), and #2006 (localization settings making the reference tz configurable end-to-end). Acceptance tests are added in #3181.
startOfDayUtc/addDaysUtc replaced by the DST-safe partsInTz/calendarDayUtc (Intl.formatToParts, no hand-rolled offset math). Shared calendarPartsInTz extracted to @objectstack/core (reused by slice 5); formula keeps a private copy to stay a leaf.
"keeps wall-clock time" defect of daysFromNow/daysAgo fixed (now midnight-truncated).
✅ AC2 (across DST) — calendar days resolve correctly across both 2026 US DST transitions; a Field.datetime instant compares equal to daysFromNow(n) across DST; a Field.date string matches via the hydration-safe idioms (date(...), ordering operators, daysBetween(...) == 0).
Closing as completed — Slice 3's deliverable (timezone-aware today()/daysFromNow()/daysAgo()) is done and verified. The equality-hydration gap continues in #3183.
Activity
fix(objectql): thread execution context into read-time formula evalua…
feat(runtime): resolve a reference timezone onto ExecutionContext (#1993
feat(formula): timezone-aware today()/daysFromNow()/daysAgo() (#1980) (…
feat(formula,email): render datetime in reference timezone (ADR-0053 …
feat(settings): localization settings — platform timezone/language/fo…
os-zhuang commentedon Jul 18, 2026
Slice 3 delivered — closing as completed
The compute-tz core shipped across #1998 (the slice), #2001 (shared
calendarPartsInTzutil), and #2006 (localization settings making the reference tz configurable end-to-end). Acceptance tests are added in #3181.Scope ✔︎
EvalContext.timezoneadded —packages/formula/src/types.ts.registerStdLib(env, now, timezone)/buildEnv(now, ctx.timezone)—stdlib.ts,cel-engine.ts.startOfDayUtc/addDaysUtcreplaced by the DST-safepartsInTz/calendarDayUtc(Intl.formatToParts, no hand-rolled offset math). SharedcalendarPartsInTzextracted to@objectstack/core(reused by slice 5); formula keeps a private copy to stay a leaf.daysFromNow/daysAgofixed (now midnight-truncated).duration()prerequisite confirmed (cel-engine.test.ts).Acceptance criteria
today()at2026-06-16T02:00ZinAmerica/Los_Angeles→ UTC-midnight of2026-06-15.'UTC'→ byte-for-byte today's behavior (all three functions).Field.datetimeinstant compares equal todaysFromNow(n)across DST; aField.datestring matches via the hydration-safe idioms (date(...), ordering operators,daysBetween(...) == 0).record.due_date == today()wheredue_dateis aField.datestring silently returnsfalse. This is not a timezone problem: cel-js's equality hard-codesstring == Xto false (overloads.jsisEqual), so it can't be fixed in the formula operator layer, and it's cross-cutting (every CEL==on a date-string field). Split out as BaredateField == today()silently returns false — fix via AST temporal-comparison rewrite in the CEL engine #3183 for a type-aware hydration fix in the data layer. Workarounds documented there; aKNOWN GAPguard test in test(formula): lock ADR-0053 Phase 2 Slice 3 acceptance criteria (#1980) #3181 locks the current behavior until BaredateField == today()silently returns false — fix via AST temporal-comparison rewrite in the CEL engine #3183 lands.Closing as completed — Slice 3's deliverable (timezone-aware
today()/daysFromNow()/daysAgo()) is done and verified. The equality-hydration gap continues in #3183.Generated by Claude Code