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
feat(spec,ci): temporal hooks onto the IDataDriver contract; conformance job with live non-UTC servers (#3979)
Closes the two debts the datetime storage work (#3912/#3942/#3954) left
open in ADR-0053.
D-A2 — contract promotion. temporalFilterValue and temporalFilterColumnSql
were duck-typed: analytics probed `typeof driver.x === 'function'` against
a locally-invented interface, and nothing at the type level said a driver
must implement both or neither. #3912's lesson is precisely that coercing
the comparand without normalising the column reintroduces half the bug, so
a driver implementing one hook alone would silently regress. Both are now
optional members of IDataDriver, documented as a PAIR with
"absent = identity" semantics for drivers whose storage form is the wire
form. SqlDriver `implements IDataDriver`, so its signatures are
compile-checked from here on; analytics Picks the contract instead of
inventing a local shape, keeping the runtime typeof guards as the correct
way to consume an optional member. coerceFilterValueForSql already sits
behind the hook as the last-resort boolean/number recovery — the demotion
D-A2 asked for — and stays in exactly that role.
D-A3 — the conformance backstop. The live-server suites from #3912/#3942
are opt-in (OS_TEST_POSTGRES_URL / OS_TEST_MYSQL_URL) and skip without a
server, so nothing ran them in CI and the seam could regress silently.
New `temporal-conformance` job: postgres:16 and mysql:8.0 service
containers, servers pointed at +08:00 post-start (services cannot override
the image command), the Node process at America/New_York, and assertions
in UTC — the three-way skew that caught every bug in this family, with
both suites asserting a non-UTC server so a mis-provisioned service fails
loudly rather than passing vacuously. The WHOLE driver-sql suite runs
under the skewed zone, so a TZ-sensitive assumption anywhere in the
driver's tests fails there before it ships.
The job's first run on its own PR confirmed it is not vacuous: the MySQL
suite ran 15 tests against MySQL 8.0.46 and the Postgres suite 5 against
PG 16.14 with `timezone changed to "Asia/Shanghai"` in the server log —
47 files, 480/480, zero skips. That also completes the #3942
compatibility claim, which had only been verified by hand on MariaDB
10.11.
Copy file name to clipboardExpand all lines: docs/adr/0053-date-and-datetime-semantics.md
+19-4Lines changed: 19 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# ADR-0053: `date` is a timezone-naive calendar day; `datetime` is an instant rendered in a reference timezone
2
2
3
-
**Status**: Accepted (2026-06-16) — Phase 1 + addendum D-A1 implemented (`sql-driver.ts``toDateOnly` write/read/filter normalization; analytics `coerceTemporalFilterValue`), Phase 2 landing incrementally; D-A2 (`temporalFilterValue`promotion onto the `IDataDriver` contract) still open as the ADR predicted. **Partly superseded (2026-07-29, addendum D-B1..D-B4):** Phase 1's "`Field.datetime` stays stored as UTC epoch ms" is replaced by one canonical UTC instant per dialect — `YYYY-MM-DDTHH:MM:SS.sssZ` text on SQLite, `timestamptz` on Postgres, `DATETIME(3)` on MySQL — applied on write and to filter comparands alike (#3912, #3942).
3
+
**Status**: Accepted (2026-06-16) — Phase 1 + addendum D-A1 implemented (`sql-driver.ts``toDateOnly` write/read/filter normalization; analytics `coerceTemporalFilterValue`), Phase 2 landing incrementally; D-A2 resolved 2026-07-30: `temporalFilterValue`+ `temporalFilterColumnSql` are optional `IDataDriver` contract members with identity semantics, and analytics types its driver seam from the contract. **Partly superseded (2026-07-29, addendum D-B1..D-B4):** Phase 1's "`Field.datetime` stays stored as UTC epoch ms" is replaced by one canonical UTC instant per dialect — `YYYY-MM-DDTHH:MM:SS.sssZ` text on SQLite, `timestamptz` on Postgres, `DATETIME(3)` on MySQL — applied on write and to filter comparands alike (#3912, #3942).
0 commit comments