From 0dcd3dafef345a336da5debe6baf637cb388e82c Mon Sep 17 00:00:00 2001 From: breken-ai <312387581+breken-ai@users.noreply.github.com> Date: Tue, 29 Sep 2026 22:53:01 -0700 Subject: [PATCH] fix(type): parse string.date.epoch.parse input as milliseconds The morph passed the validated integer string straight to new Date(), which treats it as a date string instead of a timestamp. "0" became 2000-01-01 in local time and "1700000000000" became an Invalid Date. Convert the string to a number first so it is read as a Unix timestamp in milliseconds. Co-Authored-By: Claude Opus 5.5 --- ark/type/__tests__/keywords/date.test.ts | 11 +++++++++++ ark/type/keywords/string.ts | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ark/type/__tests__/keywords/date.test.ts b/ark/type/__tests__/keywords/date.test.ts index 500c0ef7e8..41844e6cd2 100644 --- a/ark/type/__tests__/keywords/date.test.ts +++ b/ark/type/__tests__/keywords/date.test.ts @@ -32,4 +32,15 @@ contextualize(() => { 'must be an ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ) date (was "05-21-1993")' ) }) + + it("string.date.epoch.parse", () => { + const parseEpoch = type("string.date.epoch.parse") + const toIso = (s: string) => parseEpoch.assert(s).toISOString() + attest(toIso("0")).equals("1970-01-01T00:00:00.000Z") + attest(toIso("1700000000000")).equals("2023-11-14T22:13:20.000Z") + attest(toIso("-86400000")).equals("1969-12-31T00:00:00.000Z") + attest(parseEpoch("1.5").toString()).snap( + 'must be a well-formed integer string (was "1.5")' + ) + }) }) diff --git a/ark/type/keywords/string.ts b/ark/type/keywords/string.ts index 0779d70a55..d2c23669b5 100644 --- a/ark/type/keywords/string.ts +++ b/ark/type/keywords/string.ts @@ -301,7 +301,9 @@ const epoch = Scope.module( root: epochRoot, parse: rootSchema({ in: epochRoot, - morphs: (s: string) => new Date(s), + // parse as a number so the string is treated as milliseconds + // rather than passed to the Date string parser + morphs: (s: string) => new Date(Number.parseInt(s)), declaredOut: intrinsic.Date }) },