import { datetimeField, datetimeLocalToUnix, editDatetimeField, resolveDatetimeField, unixToDatetimeLocal, } from "./datetime"; /** * Every assertion here is about local time, so the zone has to be pinned. New * York is the zone the DST cases are written for: the fold is 2024-11-03 01:30 * and the gap is 2024-03-10 02:30. */ // The app never reads `process`, so `src` is typed without node's globals; the // test host is node, where assigning `TZ` re-reads the zone for `Date`. declare const process: { env: Record }; const originalTz = process.env["TZ"]; beforeAll(() => { process.env["TZ"] = "America/New_York"; }); afterAll(() => { process.env["TZ"] = originalTz; }); /** 2024-06-01T12:34:56 EDT. */ const SUMMER = 1_717_259_696; test("a non-zero-second instant round trips", () => { expect(unixToDatetimeLocal(SUMMER)).toBe("2024-06-01T12:34:56"); expect(datetimeLocalToUnix("2024-06-01T12:34:56")).toBe(SUMMER); }); test("text without seconds parses as :00", () => { expect(datetimeLocalToUnix("2024-06-01T12:34")).toBe(datetimeLocalToUnix("2024-06-01T12:34:00")); }); test("text that is not a datetime-local value names no instant", () => { expect(datetimeLocalToUnix("")).toBeUndefined(); expect(datetimeLocalToUnix("yesterday")).toBeUndefined(); expect(datetimeLocalToUnix("2024-06-01")).toBeUndefined(); expect(datetimeLocalToUnix("2024-13-01T00:00:00")).toBeUndefined(); }); /** 2024-11-03 01:30 EDT and 01:30 EST: two instants, one wall clock. */ const FOLD_FIRST = 1_730_611_800; const FOLD_SECOND = 1_730_615_400; test("the fall-back fold gives two instants the same text", () => { expect(unixToDatetimeLocal(FOLD_FIRST)).toBe("2024-11-03T01:30:00"); expect(unixToDatetimeLocal(FOLD_SECOND)).toBe("2024-11-03T01:30:00"); expect(datetimeLocalToUnix("2024-11-03T01:30:00")).toBe(FOLD_FIRST); }); test("an untouched fold bound applies the instant it was seeded with, not a re-parse of its text", () => { const field = datetimeField(FOLD_SECOND); expect(field.text).toBe("2024-11-03T01:30:00"); expect(resolveDatetimeField(field)).toEqual({ ok: true, value: FOLD_SECOND }); }); test("an edited fold bound resolves to the first of the two instants, which is what its text says", () => { const field = editDatetimeField(datetimeField(FOLD_SECOND), "2024-11-03T01:30:00"); expect(resolveDatetimeField(field)).toEqual({ ok: true, value: FOLD_FIRST }); }); test("a spring-forward time that exists on no clock is rejected rather than slid forward an hour", () => { const field = editDatetimeField(datetimeField(undefined), "2024-03-10T02:30:00"); expect(datetimeLocalToUnix("2024-03-10T02:30:00")).toBe(1_710_055_800); expect(unixToDatetimeLocal(1_710_055_800)).toBe("2024-03-10T03:30:00"); expect(resolveDatetimeField(field)).toEqual({ ok: false, reason: "nonexistent" }); }); test("an edited bound round trips with non-zero seconds", () => { const field = editDatetimeField(datetimeField(undefined), "2024-06-01T12:34:56"); expect(resolveDatetimeField(field)).toEqual({ ok: true, value: SUMMER }); }); test("clearing an edited bound drops the filter", () => { const field = editDatetimeField(datetimeField(SUMMER), ""); expect(resolveDatetimeField(field)).toEqual({ ok: true, value: undefined }); }); test("an unparseable edit is reported, never silently dropped", () => { const field = editDatetimeField(datetimeField(undefined), "2024-06-32T99:99"); expect(resolveDatetimeField(field)).toEqual({ ok: false, reason: "unparseable" }); }); test("an unset bound seeds an empty field that stays unset", () => { const field = datetimeField(undefined); expect(field.text).toBe(""); expect(resolveDatetimeField(field)).toEqual({ ok: true, value: undefined }); }); test("a fractional part on the seconds is parsed and dropped, not rejected", () => { // jsdom, and any engine that sanitizes to the full grammar, hands the input // back with milliseconds attached; a bound is a whole second either way. const field = editDatetimeField(datetimeField(undefined), "2023-11-14T23:13:37.000"); const resolved = resolveDatetimeField(field); expect(resolved).toEqual({ ok: true, value: datetimeLocalToUnix("2023-11-14T23:13:37") }); });