File size: 6,664 Bytes
c09f67c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import { describe, expect, it } from "bun:test";
import { formatAmountValue, formatDate } from "./utils";

describe("formatAmountValue", () => {
  it("should handle numbers with comma as decimal separator", () => {
    expect(formatAmountValue({ amount: "1.234,56" })).toBe(1234.56);
  });

  it("should handle numbers with period as thousands separator", () => {
    expect(formatAmountValue({ amount: "1.234.56" })).toBe(1234.56);
  });

  it("should handle numbers with period as decimal separator", () => {
    expect(formatAmountValue({ amount: "1234.56" })).toBe(1234.56);
  });

  it("should handle plain numbers", () => {
    expect(formatAmountValue({ amount: "1234" })).toBe(1234);
  });

  it("should invert the amount when inverted is true", () => {
    expect(formatAmountValue({ amount: "1234.56", inverted: true })).toBe(
      -1234.56,
    );
  });

  it("should handle negative numbers", () => {
    expect(formatAmountValue({ amount: "-1234.56" })).toBe(-1234.56);
  });

  it("should invert negative numbers when inverted is true", () => {
    expect(formatAmountValue({ amount: "-1234.56", inverted: true })).toBe(
      1234.56,
    );
  });

  it("should handle zero", () => {
    expect(formatAmountValue({ amount: "0" })).toBe(0);
    expect(formatAmountValue({ amount: "0", inverted: true })).toBe(-0);
  });
});

describe("formatDate", () => {
  it("should format a valid date string", () => {
    expect(formatDate("2023-05-15")).toBe("2023-05-15");
  });

  it("should handle date strings with non-date characters", () => {
    expect(formatDate("2023/05/15")).toBe("2023-05-15");
    expect(formatDate("May 15, 2023")).toBe("2023-05-15");
  });

  it("should return undefined for invalid date strings", () => {
    expect(formatDate("invalid-date")).toBeUndefined();
    expect(formatDate("2023-13-45")).toBeUndefined();
  });

  it("should reject invalid days for month (Feb 30, Apr 31)", () => {
    expect(formatDate("2023-02-30")).toBeUndefined();
    expect(formatDate("2023-04-31")).toBeUndefined();
    expect(formatDate("2023-06-31")).toBeUndefined();
    expect(formatDate("2023-09-31")).toBeUndefined();
    expect(formatDate("2023-11-31")).toBeUndefined();
  });

  it("should handle leap year dates correctly", () => {
    // 2024 is a leap year - Feb 29 is valid
    expect(formatDate("2024-02-29")).toBe("2024-02-29");
    // 2023 is not a leap year - Feb 29 is invalid
    expect(formatDate("2023-02-29")).toBeUndefined();
    // 2000 is a leap year (divisible by 400)
    expect(formatDate("2000-02-29")).toBe("2000-02-29");
    // 1900 was not a leap year (divisible by 100 but not 400)
    expect(formatDate("1900-02-29")).toBeUndefined();
  });

  it("should accept valid edge-of-month dates", () => {
    expect(formatDate("2023-01-31")).toBe("2023-01-31");
    expect(formatDate("2023-03-31")).toBe("2023-03-31");
    expect(formatDate("2023-04-30")).toBe("2023-04-30");
    expect(formatDate("2023-02-28")).toBe("2023-02-28");
  });

  it("should handle different date formats", () => {
    expect(formatDate("05/15/2023")).toBe("2023-05-15");
  });

  it("should handle dates with time", () => {
    expect(formatDate("2023-05-15T14:30:00")).toBe("2023-05-15");
  });

  it("should handle dates dot separated", () => {
    // chrono-node interprets as MM.DD.YYYY (US format)
    expect(formatDate("04.09.2024")).toBe("2024-04-09");
  });

  it("should handle dates with time (dot format)", () => {
    // chrono-node interprets as MM.DD.YYYY (US format)
    expect(formatDate("08.05.2024 09:12:07")).toBe("2024-08-05");
  });

  it("should handle dates 07/Aug/2024", () => {
    expect(formatDate("07/Aug/2024")).toBe("2024-08-07");
  });

  it("should handle dates 24-08-2024", () => {
    expect(formatDate("24-08-2024")).toBe("2024-08-24");
  });

  it("should handle dates in dd-MM-yyyy format", () => {
    expect(formatDate("24-09-2024")).toBe("2024-09-24");
  });

  it("should handle short date format", () => {
    // chrono-node interprets as MM/DD/YY (US format)
    expect(formatDate("11/4/24")).toBe("2024-11-04");
  });

  // Timezone suffix formats (e.g., from Shopify exports)
  it("should handle dates with UTC timezone suffix", () => {
    expect(formatDate("2025-10-01 00:00:00 UTC")).toBe("2025-10-01");
  });

  it("should handle dates with GMT timezone suffix", () => {
    expect(formatDate("2025-10-01 00:00:00 GMT")).toBe("2025-10-01");
  });

  it("should handle ISO dates with Z suffix", () => {
    expect(formatDate("2025-10-01T00:00:00Z")).toBe("2025-10-01");
  });

  it("should handle ISO dates with timezone offset", () => {
    expect(formatDate("2025-10-01T00:00:00+00:00")).toBe("2025-10-01");
  });

  it("should handle timezone offset without colon (+0000 format)", () => {
    expect(formatDate("2025-10-01T00:00:00+0000")).toBe("2025-10-01");
    expect(formatDate("2025-10-01T05:30:00+0530")).toBe("2025-10-01");
  });

  it("should handle negative timezone offsets", () => {
    expect(formatDate("2025-10-01T00:00:00-05:00")).toBe("2025-10-01");
    expect(formatDate("2025-10-01T00:00:00-0800")).toBe("2025-10-01");
  });

  it("should preserve date in source timezone (not shift to UTC)", () => {
    // Late-night dates with negative offsets would shift to next day if converted to UTC
    // e.g., 2025-10-01T23:00:00-05:00 is 2025-10-02T04:00:00Z in UTC
    // We should preserve the original date (2025-10-01), not the UTC date (2025-10-02)
    expect(formatDate("2025-10-01T23:00:00-05:00")).toBe("2025-10-01");
    expect(formatDate("2025-10-01T20:00:00-08:00")).toBe("2025-10-01");
    expect(formatDate("2025-10-01T22:00:00-03:00")).toBe("2025-10-01");
  });

  it("should handle space before timezone offset", () => {
    expect(formatDate("2025-10-01 00:00:00+00:00")).toBe("2025-10-01");
    expect(formatDate("2025-10-01 12:00:00-05:00")).toBe("2025-10-01");
  });

  it("should handle Shopify-style date with time and UTC", () => {
    expect(formatDate("2025-09-30 18:03:25 UTC")).toBe("2025-09-30");
  });

  // Natural language dates (chrono-node)
  it("should handle natural language dates", () => {
    expect(formatDate("October 1, 2025")).toBe("2025-10-01");
  });

  it("should handle abbreviated month dates", () => {
    expect(formatDate("Oct 1, 2025")).toBe("2025-10-01");
  });

  // Edge cases
  it("should handle empty strings", () => {
    expect(formatDate("")).toBeUndefined();
  });

  it("should handle whitespace-only strings", () => {
    expect(formatDate("   ")).toBeUndefined();
  });

  it("should handle strings with leading/trailing whitespace", () => {
    expect(formatDate("  2025-10-01  ")).toBe("2025-10-01");
  });
});