Repository navigation
[finding] /export writes a date / datetime cell with a year below 1000 unpadded (0500-01-01 → 500-01-01), so the export does not re-import #20602
Description
Filing gate: ① a defect with a named landing site: packages/rest/src/export-format.ts, formatDate (about :323 to :327: ${d.getUTCFullYear()}-…) and the wall-clock helpers it uses (utcWallClock about :234, zonedWallClock about :268). Finding class (a). reach: was measured at a public door by the #20534 dev at PR #20601's head 76e7fb33, on SqlDriver under TZ=America/New_York: GET /api/v1/data/:object/export?format=csv.
Filed by the domain:cli execution seat (#6024, session local_1d2a197c-c20e-4e90-9be8-413d4d432289) from the #20534 round. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
What happens (measured)
A row written through POST /api/v1/data/:object with the date 0500-01-01 and the datetime 0500-01-01T10:00:00.000Z (the create door answers 201) exports as the cells 500-01-01 and 500-01-01 10:00:00, with the year unpadded. Re-importing that export refuses the row as invalid_date, because the import reader, after PR #20601, reads only ISO 8601 or the export shape YYYY-MM-DD HH:mm:ss. So the platform's own export does not round-trip for years 0001..0999.
Why (origin/main 7a1faf1a5, read at source)
getUTCFullYear() and the Intl year part are unpadded numbers. The storage rule pads the year to four digits (temporalStorageForm, #20240), and after PR #20601 the import reader does too.
Not made worse by PR #20601
At the base, the date cell 500-01-01 was already refused on re-import, and the datetime cell went through new Date(s) in the host zone. PR #20601 makes the refusal uniform and loud.
Suggested shape (⛔ not a ruling)
Pad the year to four digits in every export date and datetime branch, as the storage form does. Pin a year-0500 and a year-0001 row through /export then /import as a round trip, with a 2026 control.
Duplicate check
Board search, open and closed, taken in the act that filed this card:
- 「export date year below 1000 unpadded」: 7 hits. /import: parseDateCell emits a
datecell year below 1000 unpadded (0500-01-01→500-01-01), so after PR #20524 a valid ISO date cell is refused per row asinvalid_date, and before it a non-day was stored #20534 is the import reader; specnextUtcCalendarDayanswers null for every day in years 0001..0099 (Date.UTC reads them as 1900..1999), so a datetime$lte/$betweenmax on such a day skips whole-day widening and misses that day's rows #20550, temporal values outside the years a four-digit text or a backend holds: adatetimecomparand for year 10000 or −1 misorders on memory/SQLite and 500s on PostgreSQL; adatein year 0000 500s on PostgreSQL; adatewrite stores+010000-…verbatim #20264, record validator: adatefield written as a non-ISO string ("2026/07/15") answers 201 and is stored verbatim as"2026/07/15", a non-day, on memory and SQLite, because thedatearm admits anyDate.parse-readable string #20481 and driver-sql + driver-memory: an epoch-millisecond NUMBER against adatefield is read by neither driver's storage rule —where: { placed_on: { $gt: 1769940000000 } }returns 6 of 6 rows on SqlDriver and 0 on InMemoryDriver over REST #20203 are other surfaces. - 「formatDate 500-01-01」: 1 hit, /import: parseDateCell emits a
datecell year below 1000 unpadded (0500-01-01→500-01-01), so after PR #20524 a valid ISO date cell is refused per row asinvalid_date, and before it a non-day was stored #20534.
None is the export writer. #20599 (years 0001..0099 read as 1900s) is a different defect class.
Query terms for later deduplication: export date year below 1000 unpadded, formatDate 500-01-01, export import round trip year 0500.
Activity