Fix 'invalid date' display for ISO datetime resolution_date values
The pg driver returns PostgreSQL DATE columns as ISO datetime strings (e.g. '2026-07-03T00:00:00.000Z'). The formatResolutionDate helper was strictly matching YYYY-MM-DD only, so these were classified as 'invalid'. Now the helper extracts the date prefix from ISO datetime strings before validating, correctly classifying them as 'set' with the YYYY-MM-DD value. Updated the property test filter and added an example test for the case.
This commit is contained in:
@@ -43,6 +43,14 @@ function isValidCalendarYmd(s) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// True iff `s` is an ISO datetime string whose date prefix is a valid calendar date.
|
||||||
|
// e.g. "2026-07-01T00:00:00.000Z" → true (the helper now extracts the date prefix).
|
||||||
|
function isIsoDateTimeWithValidDate(s) {
|
||||||
|
if (typeof s !== 'string') return false;
|
||||||
|
if (!/^\d{4}-\d{2}-\d{2}T/.test(s)) return false;
|
||||||
|
return isValidCalendarYmd(s.slice(0, 10));
|
||||||
|
}
|
||||||
|
|
||||||
// --- Shared arbitraries -----------------------------------------------------
|
// --- Shared arbitraries -----------------------------------------------------
|
||||||
|
|
||||||
// Four-digit zero-padded year string (0000–9999) — always matches \d{4}.
|
// Four-digit zero-padded year string (0000–9999) — always matches \d{4}.
|
||||||
@@ -130,7 +138,7 @@ const wrongShapeArb = fc.oneof(
|
|||||||
|
|
||||||
const invalidStringArb = fc
|
const invalidStringArb = fc
|
||||||
.oneof(wrongShapeArb, badMonthArb, badDayArb, impossibleDayArb, twoDigitArb)
|
.oneof(wrongShapeArb, badMonthArb, badDayArb, impossibleDayArb, twoDigitArb)
|
||||||
.filter(s => typeof s === 'string' && s.trim() !== '' && !isValidCalendarYmd(s.trim()));
|
.filter(s => typeof s === 'string' && s.trim() !== '' && !isValidCalendarYmd(s.trim()) && !isIsoDateTimeWithValidDate(s.trim()));
|
||||||
|
|
||||||
// Any input category (used for totality / independence properties).
|
// Any input category (used for totality / independence properties).
|
||||||
const anyInputArb = fc.oneof(
|
const anyInputArb = fc.oneof(
|
||||||
|
|||||||
@@ -32,6 +32,13 @@ describe('formatResolutionDate', () => {
|
|||||||
value: '2024-02-29',
|
value: '2024-02-29',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("classifies an ISO datetime '2026-07-03T00:00:00.000Z' as set with the date prefix", () => {
|
||||||
|
expect(formatResolutionDate('2026-07-03T00:00:00.000Z')).toEqual({
|
||||||
|
state: 'set',
|
||||||
|
value: '2026-07-03',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('invalid — present but not a valid calendar date (Requirement 1.6)', () => {
|
describe('invalid — present but not a valid calendar date (Requirement 1.6)', () => {
|
||||||
|
|||||||
@@ -63,14 +63,20 @@ export function formatResolutionDate(raw) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Must match the strict YYYY-MM-DD shape.
|
// Must match the strict YYYY-MM-DD shape.
|
||||||
if (!YMD_SHAPE.test(trimmed)) {
|
// Also accept ISO datetime strings (e.g. "2026-07-03T00:00:00.000Z") by
|
||||||
|
// extracting the date prefix — the pg driver returns DATE columns this way.
|
||||||
|
let candidate = trimmed;
|
||||||
|
if (!YMD_SHAPE.test(candidate) && /^\d{4}-\d{2}-\d{2}T/.test(candidate)) {
|
||||||
|
candidate = candidate.slice(0, 10);
|
||||||
|
}
|
||||||
|
if (!YMD_SHAPE.test(candidate)) {
|
||||||
return { state: 'invalid' };
|
return { state: 'invalid' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shape is correct; verify it is a real calendar date.
|
// Shape is correct; verify it is a real calendar date.
|
||||||
const year = Number(trimmed.slice(0, 4));
|
const year = Number(candidate.slice(0, 4));
|
||||||
const month = Number(trimmed.slice(5, 7));
|
const month = Number(candidate.slice(5, 7));
|
||||||
const day = Number(trimmed.slice(8, 10));
|
const day = Number(candidate.slice(8, 10));
|
||||||
|
|
||||||
if (month < 1 || month > 12) {
|
if (month < 1 || month > 12) {
|
||||||
return { state: 'invalid' };
|
return { state: 'invalid' };
|
||||||
@@ -80,7 +86,7 @@ export function formatResolutionDate(raw) {
|
|||||||
return { state: 'invalid' };
|
return { state: 'invalid' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid calendar date; the trimmed value is already the canonical
|
// Valid calendar date; the candidate value is the canonical
|
||||||
// zero-padded YYYY-MM-DD form.
|
// zero-padded YYYY-MM-DD form.
|
||||||
return { state: 'set', value: trimmed };
|
return { state: 'set', value: candidate };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user