Skip to content

ADR-0053 Phase 2 · Slice 3: timezone-aware today()/daysFromNow()/daysAgo() #1980

Description

@os-zhuang

Part of ADR-0053 Phase 2. Design: #1975 · Parent: #1928.

Slice 3 of 6 — compute-tz core. Make the time functions timezone-aware. Gated behind "reference tz unset → UTC".

Decision D1 (see #1975)

today()/daysFromNow()/daysAgo() return a Date at UTC-midnight of the reference-tz calendar daynew 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 EvalContextpackages/formula/src/types.ts.
  • registerStdLib(env, now)registerStdLib(env, now, timezone); buildEnv(now)buildEnv(now, ctx.timezone)packages/formula/src/stdlib.ts:40-57, packages/formula/src/cel-engine.ts:35-42 and :189-191.
  • 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.
  • tz unset / 'UTC' → byte-for-byte today's behavior.

Prerequisite

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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions