From 1195cbf9d9793ebc22684bd46a30057b7cccd37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gau=C2=B7Beo?= Date: Tue, 10 Nov 2026 22:36:04 +0700 Subject: [PATCH] fix(keystatic): prevent fields.date() from silently truncating non-midnight Date values (#1615) - Return `value.toISOString()` in `fields.date().parse()` when a `Date` instance carries a non-zero UTC time component so `validate()` rejects it consistently with quoted ISO datetime strings instead of silently discarding the time. - Add unit tests in `packages/keystatic/src/form/fields/date/date.test.ts` and a patch changeset. Fixes #1615 Signed-off-by: Xuan Thinh Nguyen --- .../date-field-preserve-non-midnight-instant.md | 5 +++++ .../keystatic/src/form/fields/date/date.test.ts | 16 ++++++++++++++++ .../keystatic/src/form/fields/date/index.tsx | 8 ++++++++ 3 files changed, 29 insertions(+) create mode 100644 .changeset/date-field-preserve-non-midnight-instant.md create mode 100644 packages/keystatic/src/form/fields/date/date.test.ts diff --git a/.changeset/date-field-preserve-non-midnight-instant.md b/.changeset/date-field-preserve-non-midnight-instant.md new file mode 100644 index 000000000..54064de1d --- /dev/null +++ b/.changeset/date-field-preserve-non-midnight-instant.md @@ -0,0 +1,5 @@ +--- +'@keystatic/core': patch +--- + +Avoid silently truncating non-midnight UTC `Date` values in `fields.date().parse()` (#1615). diff --git a/packages/keystatic/src/form/fields/date/date.test.ts b/packages/keystatic/src/form/fields/date/date.test.ts new file mode 100644 index 000000000..f99dc71d9 --- /dev/null +++ b/packages/keystatic/src/form/fields/date/date.test.ts @@ -0,0 +1,16 @@ +import { expect, test } from '@jest/globals'; +import { date } from './index'; + +test('parses UTC midnight Date instances into YYYY-MM-DD', () => { + const field = date({ label: 'Publish Date', validation: { isRequired: true } }); + const parsed = field.parse(new Date('2026-09-01T00:00:00.000Z')); + expect(parsed).toBe('2026-09-01'); + expect(field.validate(parsed)).toBe('2026-09-01'); +}); + +test('does not silently truncate Date instances carrying a non-zero UTC time (#1615)', () => { + const field = date({ label: 'Publish Date', validation: { isRequired: true } }); + const parsed = field.parse(new Date('2026-09-01T13:00:00.000Z')); + expect(parsed).toBe('2026-09-01T13:00:00.000Z'); + expect(() => field.validate(parsed)).toThrow('Publish Date is not a valid date'); +}); diff --git a/packages/keystatic/src/form/fields/date/index.tsx b/packages/keystatic/src/form/fields/date/index.tsx index a8224857e..996fffdfc 100644 --- a/packages/keystatic/src/form/fields/date/index.tsx +++ b/packages/keystatic/src/form/fields/date/index.tsx @@ -52,6 +52,14 @@ export function date({ return null; } if (value instanceof Date) { + if ( + value.getUTCHours() !== 0 || + value.getUTCMinutes() !== 0 || + value.getUTCSeconds() !== 0 || + value.getUTCMilliseconds() !== 0 + ) { + return value.toISOString(); + } const year = value.getUTCFullYear(); const month = String(value.getUTCMonth() + 1).padStart(2, '0'); const day = String(value.getUTCDate()).padStart(2, '0');