Skip to content

Commit f08e476

Browse files
dmealingclaude
andcommitted
docs(api-contract): the real field.timestamp wire form, and field.time
The Tier 1 type-encoding table mandated `YYYY-MM-DDTHH:mm:ss.sssZ` for every `field.timestamp`. No port emits that, and the table was wrong three ways: 1. `.sss` unconditionally. The rule every port byte-matches — pinned by `fixtures/persistence-conformance/normalization.md` — is millisecond resolution, TRUNCATED, trailing zeros stripped, and the fraction and its `.` omitted entirely when the sub-second value is zero. A whole-second instant is `2026-05-25T14:30:00Z`, never `...:00.000Z`. 2. One timestamp row, which silently contradicted ADR-0036 Wave 2 (merged): `field.timestamp` is an instant, tz-aware BY DEFAULT, so it carries `Z`; `@localTime: true` is the naive wall-clock opt-out and carries NO zone. The doc never mentioned `@localTime`. Both rows now exist, and the `Z` is called out as the discriminator between TIMESTAMPTZ and TIMESTAMP — eliding it for the first or adding it to the second is what makes a response compare unequal. 3. `field.time` was absent from the table outright. Added (`HH:MM:SS[.fff]`). The doc now defers to normalization.md as the single source of truth instead of restating the rule a third time and drifting again; only the two properties that decide byte-equality are repeated, and they are labelled as repetition. Ruled by the maintainer after the adopter estate surfaced the disagreement: the ports' behavior stands, the doc changes. Following the doc literally would have emitted `.000Z` and turned TypeScript red on a scenario it currently passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015NrWUxA6euwvnFcwKxcK6A
1 parent ad24e25 commit f08e476

1 file changed

Lines changed: 19 additions & 1 deletion

File tree

‎docs/features/api-contract.md‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,9 @@ for the one-line change.
252252
| `field.int`, `field.long`, `field.double` | number | `long` MAY be string on overflow; defer to per-port docs. |
253253
| `field.boolean` | boolean | – |
254254
| `field.date` | string | ISO 8601 calendar date (`YYYY-MM-DD`). |
255-
| `field.timestamp` | string | ISO 8601 with timezone (`YYYY-MM-DDTHH:mm:ss.sssZ`). |
255+
| `field.time` | string | `HH:MM:SS[.fff]`. |
256+
| `field.timestamp` | string | Instant, tz-aware by default (ADR-0036 Wave 2) — `YYYY-MM-DDTHH:MM:SS[.fff]Z`, **always UTC**. |
257+
| `field.timestamp` (`@localTime: true`) | string | Naive wall clock — `YYYY-MM-DDTHH:MM:SS[.fff]`, **no `Z`**. |
256258
| `field.currency` | **integer minor units** | Cents for USD, yen for JPY. Float arithmetic is forbidden. Server never formats. |
257259
| `field.object` (`@storage: jsonb`) | object | Nested per the sub-object schema. |
258260
| `field.object` (`@storage: flattened`) | object | Same JSON shape — only the storage differs. |
@@ -263,6 +265,22 @@ expects integer minor units on the wire, and the
263265
each port's codegen output. Float arithmetic for money has bitten
264266
every language at least once.
265267

268+
The temporal spellings above are **not** this document's to define — they are
269+
the cross-port wire forms pinned by
270+
[`fixtures/persistence-conformance/normalization.md`](../../fixtures/persistence-conformance/normalization.md),
271+
which every port byte-matches. Two properties of that rule are easy to get
272+
wrong and are restated here only because they decide whether a response
273+
compares equal:
274+
275+
- **The `Z` discriminates the two timestamp kinds.** Never elide it for a
276+
default (tz-aware) `field.timestamp`, and never add it for an
277+
`@localTime: true` one. The suffix is what tells `TIMESTAMPTZ` from
278+
`TIMESTAMP`.
279+
- **Sub-seconds are millisecond resolution, truncated, with no trailing
280+
zeros** — and the fractional component *and its `.`* are **omitted
281+
entirely when zero**. A whole-second instant is `2026-05-25T14:30:00Z`,
282+
never `2026-05-25T14:30:00.000Z`.
283+
266284
### Error response
267285

268286
Non-2xx responses MUST return:

0 commit comments

Comments
 (0)