[object Object]

← back to NationalPaperHangers Yfr2 C3

reject impossible measure timestamps

f3845d6e0bbd9e40fb0296b5efab0737fa01806f · 2026-08-29 14:10:03 -0700 · Steve Abrams

Files touched

Diff

commit f3845d6e0bbd9e40fb0296b5efab0737fa01806f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 29 14:10:03 2026 -0700

    reject impossible measure timestamps
---
 lib/domain/measure-tier.js  | 22 ++++++++++++++++------
 tests/measure-tier.test.js  |  8 +++++++-
 verification/e2e-proof.json |  2 +-
 3 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/lib/domain/measure-tier.js b/lib/domain/measure-tier.js
index ec4e856..60dd61e 100644
--- a/lib/domain/measure-tier.js
+++ b/lib/domain/measure-tier.js
@@ -22,12 +22,22 @@ function positive(value) {
 }
 
 function validDate(value) {
-  if (!(value instanceof Date) && !(typeof value === 'string'
-    && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?(?:Z|[+-]\d{2}:\d{2})$/.test(value))) {
-    return false;
-  }
-  const date = value instanceof Date ? value : new Date(value);
-  return Number.isFinite(date.getTime());
+  if (value instanceof Date) return Number.isFinite(value.getTime());
+  if (typeof value !== 'string') return false;
+  const match = value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,6})?(Z|([+-])(\d{2}):(\d{2}))$/);
+  if (!match) return false;
+  const [, yearText, monthText, dayText, hourText, minuteText, secondText, zone, , zoneHourText, zoneMinuteText] = match;
+  const year = Number(yearText);
+  const month = Number(monthText);
+  const day = Number(dayText);
+  const hour = Number(hourText);
+  const minute = Number(minuteText);
+  const second = Number(secondText);
+  const daysInMonth = month >= 1 && month <= 12
+    ? new Date(Date.UTC(year, month, 0)).getUTCDate() : 0;
+  const validZone = zone === 'Z' || (Number(zoneHourText) <= 23 && Number(zoneMinuteText) <= 59);
+  return year >= 1 && day >= 1 && day <= daysInMonth && hour <= 23 && minute <= 59 && second <= 59
+    && validZone && Number.isFinite(new Date(value).getTime());
 }
 
 function positiveIntegerId(value) {
diff --git a/tests/measure-tier.test.js b/tests/measure-tier.test.js
index 8cd81d3..e99c425 100644
--- a/tests/measure-tier.test.js
+++ b/tests/measure-tier.test.js
@@ -175,7 +175,13 @@ test('accepts database numeric strings but rejects JavaScript coercion traps', (
 
 test('accepts Date/ISO timestamps and rejects coercible non-timestamps', () => {
   assert.equal(classifyMeasureTier(base({ captured_at: new Date('2026-08-01T12:00:00Z') })).tier, 'standard');
-  for (const value of [true, 1, [], {}, '2026-08-01', '08/01/2026']) {
+  assert.equal(classifyMeasureTier(base({ captured_at: '2024-02-29T12:00:00+05:30' })).tier, 'standard');
+  for (const value of [
+    true, 1, [], {}, '2026-08-01', '08/01/2026',
+    '0000-01-01T12:00:00Z', '2026-02-29T12:00:00Z', '2026-02-31T12:00:00Z', '2026-13-01T12:00:00Z',
+    '2026-01-01T24:00:00Z', '2026-01-01T12:60:00Z', '2026-01-01T12:00:60Z',
+    '2026-01-01T12:00:00+24:00'
+  ]) {
     assert.equal(classifyMeasureTier(base({ captured_at: value })).tier, 'ineligible', String(value));
   }
 });
diff --git a/verification/e2e-proof.json b/verification/e2e-proof.json
index 055ac26..0007cf5 100644
--- a/verification/e2e-proof.json
+++ b/verification/e2e-proof.json
@@ -75,7 +75,7 @@
     "missing consent/contact/routing/scope cannot be inferred",
     "product_name and notes cannot satisfy specification fields",
     "malformed room captures cannot produce spec_ready",
-    "boolean, array, padded, malformed, and other coercible numeric/date values cannot qualify",
+    "boolean, array, padded, malformed, impossible-calendar, and other coercible numeric/date values cannot qualify; a valid leap-day offset timestamp does qualify",
     "legacy routed records remain update-compatible and legacy room captures remain NULL",
     "current route without routed_to and installer-mismatched exact routes are rejected by PostgreSQL",
     "inline partial UNIQUE and mutable touch_kind primary shapes are rejected",

← b9ca758 harden measure attribution migration proof  ·  back to NationalPaperHangers Yfr2 C3  ·  (newest)