Filing gate: ① a product defect with a named landing site and a reach:. Finding class (a). reach: is POST /api/v1/data/:object/import with a date cell naming a year from 0001 to 0999, such as 0500-01-01. Derived at source, not driven: the two code facts below are read on origin/main (after fb386074f) and at PR #20524's head 4c5740d2d. The at-tier contract review of PR #20524 found it (record 5881202199, ① item 9 and ③ item 7) and recommended filing it.
Filed by the domain:engine execution seat 1 (session_01N8TPEsoJxPsdSdNKGnNGEN, os-warren). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
What happens
packages/rest/src/import-coerce.ts, parseDateCell, builds a date cell's value with the year as a bare number on all three date branches:
- the fast path:
${y}-${pad2(mo)}-${pad2(d)} with y = Number(ymd[1]);
- the wall-clock path:
${wall.year}-…;
- the
Date / new Date(s) paths: ${…getUTCFullYear()}-….
A cell 0500-01-01 therefore becomes 500-01-01.
Years 1000..9999 are unaffected, because their year already has four digits. The datetime branches return toISOString(), which pads, so they are unaffected too.
Why
parseDateCell predates the 0001..9999 range (#20264) and the four-digit day rule, and never pads the year. pad2 exists for month and day. The year needs a four-digit pad, which the date storage rule already uses (temporalStorageForm, #20240).
Suggested shape (⛔ not a ruling)
- Pad the year to four digits on every
date branch of parseDateCell.
- Pin
0500-01-01 and 0001-01-01 through /import on memory and SQLite, with a year-2026 control.
Dedupe
search_issues, run by this seat in objectstack-ai/objectstack, open and closed: "import parseDateCell date cell year below 1000 emitted unpadded 500-01-01" gives 3 hits.
None is this.
Dedupe words: parseDateCell unpadded year · import date cell year below 1000 · 0500-01-01 import 500-01-01
Filing gate: ① a product defect with a named landing site and a
reach:. Finding class (a).reach:isPOST /api/v1/data/:object/importwith adatecell naming a year from 0001 to 0999, such as0500-01-01. Derived at source, not driven: the two code facts below are read onorigin/main(afterfb386074f) and at PR #20524's head4c5740d2d. The at-tier contract review of PR #20524 found it (record 5881202199, ① item 9 and ③ item 7) and recommended filing it.Filed by the
domain:engineexecution seat 1 (session_01N8TPEsoJxPsdSdNKGnNGEN,os-warren). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.What happens
packages/rest/src/import-coerce.ts,parseDateCell, builds adatecell's value with the year as a bare number on all threedatebranches:${y}-${pad2(mo)}-${pad2(d)}withy = Number(ymd[1]);${wall.year}-…;Date/new Date(s)paths:${…getUTCFullYear()}-….A cell
0500-01-01therefore becomes500-01-01.datefield 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), thedatewrite arm admitted anyDate.parse-readable string, so500-01-01was stored as written: a non-day that sorts and compares wrongly as text (the coretemporalStorageForm: thedatearm leaves a year outside 1000..9999 unpadded — over REST the epoch-ms number for 0999-06-15 counts$gt0 /$lt7 on InMemoryDriver and SQLite (correct 6 / 0); its ISO string counts 6 / 0 #20240 class).datewrite arm admits only a leadingYYYY-MM-DD(core'sisUninterpretableTemporalComparand).500-01-01is refused per row withVALIDATION_FAILED/invalid_date. The same value written directly as0500-01-01throughPOST /api/v1/data/:objectis accepted (the year range is 0001..9999 since 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), so the import refuses a date the write door takes.Years 1000..9999 are unaffected, because their year already has four digits. The
datetimebranches returntoISOString(), which pads, so they are unaffected too.Why
parseDateCellpredates the 0001..9999 range (#20264) and the four-digit day rule, and never pads the year.pad2exists for month and day. The year needs a four-digit pad, which thedatestorage rule already uses (temporalStorageForm, #20240).Suggested shape (⛔ not a ruling)
datebranch ofparseDateCell.0500-01-01and0001-01-01through/importon memory and SQLite, with a year-2026 control.Dedupe
search_issues, run by this seat inobjectstack-ai/objectstack, open and closed: "import parseDateCell date cell year below 1000 emitted unpadded 500-01-01" gives 3 hits.datefield 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 is the write door.datetimecomparand for year 10000 or −1 misorders on memory/SQLite and 500s on PostgreSQL; adatein year 0000 500s on PostgreSQL; adatewrite stores+010000-…verbatim #20264 (closed) is the year range.temporalStorageForm: thedatearm leaves a year outside 1000..9999 unpadded — over REST the epoch-ms number for 0999-06-15 counts$gt0 /$lt7 on InMemoryDriver and SQLite (correct 6 / 0); its ISO string counts 6 / 0 #20240 (closed) is coretemporalStorageForm's unpadded year, the same class in the storage rule, not in the import reader.None is this.
Dedupe words:
parseDateCell unpadded year·import date cell year below 1000·0500-01-01 import 500-01-01