Skip to content

feat(formula): timezone-aware today() / daysFromNow() / daysAgo()#1998

Merged
os-zhuang merged 1 commit into
mainfrom
feat/tz-time-functions-1980
Jun 17, 2026
Merged

feat(formula): timezone-aware today() / daysFromNow() / daysAgo()#1998
os-zhuang merged 1 commit into
mainfrom
feat/tz-time-functions-1980

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

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 threaded ExecutionContext.timezone (#1978) → EvalContext.timezone → CEL stdlib, and the timezone math uses Intl.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:

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.)

⚠️ Behavior change

daysFromNow(n) / daysAgo(n) previously kept now's wall-clock time (daysFromNow(30) at 10: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).
  • For a genuine sub-day offset, use the documented escape hatch now() + duration("Nh") (cel-js duration() is supported — confirmed by existing tests).
  • With no reference timezone configured, the zone resolves to UTC: today() is byte-for-byte unchanged; only the daysFromNow/daysAgo midnight-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

objectql threads execCtx.timezone into read-time formula evaluation (applyFormulaPlan, from #1979) and default-value expressions (applyFieldDefaults).

Testing

  • New: tz-aware today()/daysFromNow()/daysAgo() across the America/New_York day boundary; daysFromNow midnight semantics; UTC default unchanged for today().
  • @objectstack/formula 121 green, @objectstack/objectql 639 green; DTS build + typecheck clean.
  • Changeset: minor.

Note: consuming a non-UTC reference timezone end-to-end also needs the discoverable localization settings manifest noted in #1978 (a small follow-up). The resolver + this slice are wired; an org just can't set the value via UI yet.

🤖 Generated with Claude Code

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>
@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 17, 2026 2:46am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Jun 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/formula, @objectstack/objectql.

16 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/core/services.mdx (via @objectstack/objectql)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/objectql)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql)
  • content/docs/concepts/packages.mdx (via @objectstack/formula, @objectstack/objectql)
  • content/docs/guides/authentication.mdx (via @objectstack/objectql)
  • content/docs/guides/deployment-vercel.mdx (via @objectstack/objectql)
  • content/docs/guides/formula.mdx (via @objectstack/formula, packages/objectql)
  • content/docs/guides/kernel-services.mdx (via @objectstack/objectql)
  • content/docs/guides/metadata/validation.mdx (via @objectstack/formula)
  • content/docs/guides/objectql-migration.mdx (via @objectstack/objectql)
  • content/docs/guides/packages.mdx (via @objectstack/formula, @objectstack/objectql)
  • content/docs/guides/plugins.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectos/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/formula)
  • content/docs/releases/v9.mdx (via @objectstack/objectql)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang merged commit d99a75a into main Jun 17, 2026
14 checks passed
@os-zhuang
os-zhuang deleted the feat/tz-time-functions-1980 branch June 17, 2026 03:27
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant