1import { describe, expect, it } from "@effect/vitest";
2
3import { compareDateTimeStrings } from "./dateTime.ts";
4
5describe("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});