| 1 | import { describe, expect, it } from "@effect/vitest"; |
| 2 | |
| 3 | import { compareDateTimeStrings } from "./dateTime.ts"; |
| 4 | |
| 5 | describe("compareDateTimeStrings", () => { |
| 6 | it("compares valid date-time strings by absolute time", () => { |
| 7 | expect( |
| 8 | compareDateTimeStrings("2026-09-01T12:00:00.000Z", "2026-09-01T05:00:00.000-07:00"), |
| 9 | ).toBe(0); |
| 10 | expect( |
| 11 | compareDateTimeStrings("2026-09-01T12:00:01.000Z", "2026-09-01T12:00:00.000Z"), |
| 12 | ).toBeGreaterThan(0); |
| 13 | }); |
| 14 | |
| 15 | it.each([ |
| 16 | ["2024-02-29T12:00:00Z", "2024-02-29T17:30:00+05:30"], |
| 17 | ["2000-02-29T00:00:00.100Z", "2000-02-28T20:30:00.1-03:30"], |
| 18 | ["0000-01-01T00:00:00.000Z", "+000000-01-01T00:00:00.000+00:00"], |
| 19 | ["+010000-01-01T00:00:00.000Z", "9999-12-31T23:00:00.000-01:00"], |
| 20 | ["2026-09-01T24:00:00Z", "2026-09-02T00:00:00Z"], |
| 21 | ["2026-09-01T24:00:00.0000Z", "2026-09-02T00:00:00.000Z"], |
| 22 | ["2024-02-29T24:00:00+05:30", "2024-03-01T00:00:00+05:30"], |
| 23 | ["2026-12-31T24:00:00-07:00", "2027-01-01T07:00:00Z"], |
| 24 | ["2026-09-01T12:00Z", "2026-09-01T12:00:00.000Z"], |
| 25 | ["2026-09-01T05:00-07:00", "2026-09-01T12:00:00Z"], |
| 26 | ["2026-09-01T24:00Z", "2026-09-02T00:00:00Z"], |
| 27 | ["2026-09-01T24:00+05:30", "2026-09-02T00:00:00+05:30"], |
| 28 | ])("preserves equal ISO instants %s and %s", (left, right) => { |
| 29 | expect(compareDateTimeStrings(left, right)).toBe(0); |
| 30 | }); |
| 31 | |
| 32 | it("sorts malformed values before valid values", () => { |
| 33 | expect(compareDateTimeStrings("invalid", "2026-09-01T12:00:00.000Z")).toBeLessThan(0); |
| 34 | expect(compareDateTimeStrings("2026-09-01T12:00:00.000Z", "invalid")).toBeGreaterThan(0); |
| 35 | }); |
| 36 | |
| 37 | it.each([ |
| 38 | "2014-02-30", |
| 39 | "2014-03-02", |
| 40 | "2014-03-02T00:00:00", |
| 41 | "2014-03-02T00:00:00.000", |
| 42 | "03/02/2014", |
| 43 | "March 2, 2014", |
| 44 | "Sun, 02 Mar 2014 00:00:00 GMT", |
| 45 | "2014-03-02T00:00:00.000Z\n", |
| 46 | "2014-02-30T00:00:00.000Z", |
| 47 | "1900-02-29T00:00:00.000-07:00", |
| 48 | "2024-04-31T00:00:00.000+05:30", |
| 49 | "2024-03-02T12:00:00.000+24:00", |
| 50 | "2026-09-01T24:01:00Z", |
| 51 | "2026-09-01T24:00:01Z", |
| 52 | "2026-09-01T24:00:00.0001Z", |
| 53 | "2026-09-01T24:01Z", |
| 54 | "2026-09-01T25:00Z", |
| 55 | ])("treats %s as malformed without native date guessing", (malformed) => { |
| 56 | const valid = "1970-01-01T00:00:00.000Z"; |
| 57 | expect(compareDateTimeStrings(malformed, valid)).toBeLessThan(0); |
| 58 | expect(compareDateTimeStrings(valid, malformed)).toBeGreaterThan(0); |
| 59 | expect(compareDateTimeStrings(malformed, "invalid")).toBeLessThan(0); |
| 60 | expect(compareDateTimeStrings("invalid", malformed)).toBeGreaterThan(0); |
| 61 | expect(compareDateTimeStrings(malformed, malformed)).toBe(0); |
| 62 | }); |
| 63 | |
| 64 | it("uses code-unit order for malformed date-time strings", () => { |
| 65 | expect(compareDateTimeStrings("invalid-a", "invalid-B")).toBeGreaterThan(0); |
| 66 | expect(compareDateTimeStrings("invalid-B", "invalid-a")).toBeLessThan(0); |
| 67 | }); |
| 68 | |
| 69 | it("returns zero for equal malformed date-time strings", () => { |
| 70 | expect(compareDateTimeStrings("invalid", "invalid")).toBe(0); |
| 71 | }); |
| 72 | |
| 73 | it("gives every permutation of mixed values the same order", () => { |
| 74 | const early = "2026-09-01T12:00:00.000+14:00"; |
| 75 | const late = "2026-09-01T00:00:00.000-12:00"; |
| 76 | const malformed = "2026-09-01T06:invalid"; |
| 77 | const expected = [malformed, early, late]; |
| 78 | |
| 79 | const permutations = [ |
| 80 | [early, late, malformed], |
| 81 | [early, malformed, late], |
| 82 | [late, early, malformed], |
| 83 | [late, malformed, early], |
| 84 | [malformed, early, late], |
| 85 | [malformed, late, early], |
| 86 | ]; |
| 87 | |
| 88 | for (const values of permutations) { |
| 89 | expect(values.toSorted(compareDateTimeStrings)).toEqual(expected); |
| 90 | } |
| 91 | }); |
| 92 | }); |