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

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions