@f-o-t/datetime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +726 -0
- package/biome.json +39 -0
- package/bunup.config.ts +14 -0
- package/examples/plugins-demo.ts +85 -0
- package/fot.config.ts +5 -0
- package/package.json +47 -0
- package/src/core/datetime.test.ts +1498 -0
- package/src/core/datetime.ts +694 -0
- package/src/core/factory.test.ts +167 -0
- package/src/core/factory.ts +32 -0
- package/src/errors.ts +82 -0
- package/src/index.ts +20 -0
- package/src/plugins/business-days/business-days.test.ts +225 -0
- package/src/plugins/business-days/index.ts +126 -0
- package/src/plugins/format/format.test.ts +173 -0
- package/src/plugins/format/index.ts +78 -0
- package/src/plugins/format/tokens.ts +153 -0
- package/src/plugins/index.ts +15 -0
- package/src/plugins/plugin-base.test.ts +211 -0
- package/src/plugins/plugin-base.ts +104 -0
- package/src/plugins/relative-time/index.ts +169 -0
- package/src/plugins/relative-time/relative-time.test.ts +164 -0
- package/src/plugins/timezone/index.ts +152 -0
- package/src/plugins/timezone/timezone.test.ts +135 -0
- package/src/schemas.test.ts +283 -0
- package/src/schemas.ts +104 -0
- package/src/types.ts +122 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,1498 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test as it } from "bun:test";
|
|
2
|
+
import { InvalidDateError } from "../errors";
|
|
3
|
+
import { DateTime } from "./datetime";
|
|
4
|
+
|
|
5
|
+
describe("DateTime", () => {
|
|
6
|
+
describe("Constructor", () => {
|
|
7
|
+
it("should create DateTime from current time when no input provided", () => {
|
|
8
|
+
const now = new Date();
|
|
9
|
+
const dt = new DateTime();
|
|
10
|
+
expect(dt).toBeInstanceOf(DateTime);
|
|
11
|
+
expect(dt.isValid()).toBe(true);
|
|
12
|
+
// Should be within 100ms of now
|
|
13
|
+
expect(Math.abs(dt.valueOf() - now.getTime())).toBeLessThan(100);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should create DateTime from Date object", () => {
|
|
17
|
+
const date = new Date("2024-01-15T10:30:00.000Z");
|
|
18
|
+
const dt = new DateTime(date);
|
|
19
|
+
expect(dt.isValid()).toBe(true);
|
|
20
|
+
expect(dt.valueOf()).toBe(date.getTime());
|
|
21
|
+
expect(dt.toDate()).toEqual(date);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should create DateTime from ISO string", () => {
|
|
25
|
+
const isoString = "2024-01-15T10:30:00.000Z";
|
|
26
|
+
const dt = new DateTime(isoString);
|
|
27
|
+
expect(dt.isValid()).toBe(true);
|
|
28
|
+
expect(dt.toISO()).toBe(isoString);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should create DateTime from timestamp (number)", () => {
|
|
32
|
+
const timestamp = 1705314600000; // 2024-01-15T10:30:00.000Z
|
|
33
|
+
const dt = new DateTime(timestamp);
|
|
34
|
+
expect(dt.isValid()).toBe(true);
|
|
35
|
+
expect(dt.valueOf()).toBe(timestamp);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should create DateTime from another DateTime instance", () => {
|
|
39
|
+
const dt1 = new DateTime("2024-01-15T10:30:00.000Z");
|
|
40
|
+
const dt2 = new DateTime(dt1);
|
|
41
|
+
expect(dt2.isValid()).toBe(true);
|
|
42
|
+
expect(dt2.valueOf()).toBe(dt1.valueOf());
|
|
43
|
+
// Should be a different instance
|
|
44
|
+
expect(dt2).not.toBe(dt1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should throw InvalidDateError for invalid input type", () => {
|
|
48
|
+
expect(() => new DateTime({} as any)).toThrow(InvalidDateError);
|
|
49
|
+
expect(() => new DateTime([] as any)).toThrow(InvalidDateError);
|
|
50
|
+
expect(() => new DateTime(true as any)).toThrow(InvalidDateError);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should create invalid DateTime for Invalid Date", () => {
|
|
54
|
+
const dt = new DateTime("invalid-date-string");
|
|
55
|
+
expect(dt.isValid()).toBe(false);
|
|
56
|
+
expect(Number.isNaN(dt.valueOf())).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should create invalid DateTime for invalid timestamp", () => {
|
|
60
|
+
const dt = new DateTime(NaN);
|
|
61
|
+
expect(dt.isValid()).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("Core Methods", () => {
|
|
66
|
+
describe("isValid()", () => {
|
|
67
|
+
it("should return true for valid dates", () => {
|
|
68
|
+
expect(new DateTime("2024-01-15").isValid()).toBe(true);
|
|
69
|
+
expect(new DateTime(1705314600000).isValid()).toBe(true);
|
|
70
|
+
expect(new DateTime(new Date()).isValid()).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should return false for invalid dates", () => {
|
|
74
|
+
expect(new DateTime("not-a-date").isValid()).toBe(false);
|
|
75
|
+
expect(new DateTime(NaN).isValid()).toBe(false);
|
|
76
|
+
expect(new DateTime(new Date("invalid")).isValid()).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("toDate()", () => {
|
|
81
|
+
it("should return a Date object", () => {
|
|
82
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
83
|
+
const date = dt.toDate();
|
|
84
|
+
expect(date).toBeInstanceOf(Date);
|
|
85
|
+
expect(date.getTime()).toBe(1705314600000);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should return a clone, not the internal date", () => {
|
|
89
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
90
|
+
const date1 = dt.toDate();
|
|
91
|
+
const date2 = dt.toDate();
|
|
92
|
+
expect(date1).not.toBe(date2);
|
|
93
|
+
expect(date1.getTime()).toBe(date2.getTime());
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("toISO()", () => {
|
|
98
|
+
it("should return ISO 8601 string", () => {
|
|
99
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
100
|
+
expect(dt.toISO()).toBe("2024-01-15T10:30:00.000Z");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should handle different timezones", () => {
|
|
104
|
+
const dt = new DateTime(1705314600000);
|
|
105
|
+
const iso = dt.toISO();
|
|
106
|
+
expect(iso).toMatch(
|
|
107
|
+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/,
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("valueOf()", () => {
|
|
113
|
+
it("should return Unix timestamp in milliseconds", () => {
|
|
114
|
+
const timestamp = 1705314600000;
|
|
115
|
+
const dt = new DateTime(timestamp);
|
|
116
|
+
expect(dt.valueOf()).toBe(timestamp);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should allow numeric operations", () => {
|
|
120
|
+
const dt1 = new DateTime("2024-01-15T10:30:00.000Z");
|
|
121
|
+
const dt2 = new DateTime("2024-01-15T11:30:00.000Z");
|
|
122
|
+
const diff = +dt2 - +dt1;
|
|
123
|
+
expect(diff).toBe(3600000); // 1 hour in ms
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("Immutability", () => {
|
|
129
|
+
it("should not modify original Date when creating DateTime", () => {
|
|
130
|
+
const originalDate = new Date("2024-01-15T10:30:00.000Z");
|
|
131
|
+
const originalTime = originalDate.getTime();
|
|
132
|
+
const dt = new DateTime(originalDate);
|
|
133
|
+
originalDate.setHours(15);
|
|
134
|
+
expect(dt.valueOf()).toBe(originalTime);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should not allow external modification of internal date", () => {
|
|
138
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
139
|
+
const originalTime = dt.valueOf();
|
|
140
|
+
const date = dt.toDate();
|
|
141
|
+
date.setHours(15);
|
|
142
|
+
expect(dt.valueOf()).toBe(originalTime);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe("Plugin System", () => {
|
|
147
|
+
beforeEach(() => {
|
|
148
|
+
// Clear all plugins before each test
|
|
149
|
+
DateTime["plugins"].clear();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe("extend()", () => {
|
|
153
|
+
it("should register a plugin", () => {
|
|
154
|
+
const plugin = {
|
|
155
|
+
name: "test-plugin",
|
|
156
|
+
install: (DateTimeClass: any) => {
|
|
157
|
+
DateTimeClass.prototype.testMethod = () => "test";
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
DateTime.extend(plugin);
|
|
161
|
+
expect(DateTime.hasPlugin("test-plugin")).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should call plugin install function", () => {
|
|
165
|
+
let installCalled = false;
|
|
166
|
+
const plugin = {
|
|
167
|
+
name: "test-plugin",
|
|
168
|
+
install: () => {
|
|
169
|
+
installCalled = true;
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
DateTime.extend(plugin);
|
|
173
|
+
expect(installCalled).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should pass options to plugin install", () => {
|
|
177
|
+
let receivedOptions: any;
|
|
178
|
+
const plugin = {
|
|
179
|
+
name: "test-plugin",
|
|
180
|
+
install: (_: any, options: any) => {
|
|
181
|
+
receivedOptions = options;
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
const options = { foo: "bar" };
|
|
185
|
+
DateTime.extend(plugin, options);
|
|
186
|
+
expect(receivedOptions).toEqual(options);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should throw error when registering plugin with duplicate name", () => {
|
|
190
|
+
const plugin1 = {
|
|
191
|
+
name: "duplicate",
|
|
192
|
+
install: () => {},
|
|
193
|
+
};
|
|
194
|
+
const plugin2 = {
|
|
195
|
+
name: "duplicate",
|
|
196
|
+
install: () => {},
|
|
197
|
+
};
|
|
198
|
+
DateTime.extend(plugin1);
|
|
199
|
+
expect(() => DateTime.extend(plugin2)).toThrow(
|
|
200
|
+
"Plugin duplicate is already registered",
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("should extend DateTime prototype with plugin methods", () => {
|
|
205
|
+
const plugin = {
|
|
206
|
+
name: "custom-plugin",
|
|
207
|
+
install: (DateTimeClass: any) => {
|
|
208
|
+
DateTimeClass.prototype.customMethod = function () {
|
|
209
|
+
return this.valueOf() + 1000;
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
DateTime.extend(plugin);
|
|
214
|
+
const dt = new DateTime(1000);
|
|
215
|
+
expect((dt as any).customMethod()).toBe(2000);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe("hasPlugin()", () => {
|
|
220
|
+
it("should return true for registered plugins", () => {
|
|
221
|
+
const plugin = {
|
|
222
|
+
name: "test-plugin",
|
|
223
|
+
install: () => {},
|
|
224
|
+
};
|
|
225
|
+
DateTime.extend(plugin);
|
|
226
|
+
expect(DateTime.hasPlugin("test-plugin")).toBe(true);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("should return false for non-registered plugins", () => {
|
|
230
|
+
expect(DateTime.hasPlugin("nonexistent")).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("getPlugin()", () => {
|
|
235
|
+
it("should return plugin for registered plugins", () => {
|
|
236
|
+
const plugin = {
|
|
237
|
+
name: "test-plugin",
|
|
238
|
+
install: () => {},
|
|
239
|
+
};
|
|
240
|
+
DateTime.extend(plugin);
|
|
241
|
+
expect(DateTime.getPlugin("test-plugin")).toBe(plugin);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should return undefined for non-registered plugins", () => {
|
|
245
|
+
expect(DateTime.getPlugin("nonexistent")).toBeUndefined();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe("Arithmetic Operations", () => {
|
|
251
|
+
describe("addMilliseconds()", () => {
|
|
252
|
+
it("should add milliseconds and return new DateTime", () => {
|
|
253
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
254
|
+
const result = dt.addMilliseconds(500);
|
|
255
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
256
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 500);
|
|
257
|
+
expect(dt.valueOf()).toBe(
|
|
258
|
+
new DateTime("2024-01-15T10:30:00.000Z").valueOf(),
|
|
259
|
+
); // Original unchanged
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("should handle negative values", () => {
|
|
263
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
264
|
+
const result = dt.addMilliseconds(-500);
|
|
265
|
+
expect(result.valueOf()).toBe(dt.valueOf() - 500);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("should handle zero", () => {
|
|
269
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
270
|
+
const result = dt.addMilliseconds(0);
|
|
271
|
+
expect(result.valueOf()).toBe(dt.valueOf());
|
|
272
|
+
expect(result).not.toBe(dt); // Still returns new instance
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe("addSeconds()", () => {
|
|
277
|
+
it("should add seconds and return new DateTime", () => {
|
|
278
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
279
|
+
const result = dt.addSeconds(30);
|
|
280
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
281
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 30000);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("should handle negative values", () => {
|
|
285
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
286
|
+
const result = dt.addSeconds(-30);
|
|
287
|
+
expect(result.valueOf()).toBe(dt.valueOf() - 30000);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe("addMinutes()", () => {
|
|
292
|
+
it("should add minutes and return new DateTime", () => {
|
|
293
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
294
|
+
const result = dt.addMinutes(15);
|
|
295
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
296
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 15 * 60 * 1000);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("should handle negative values", () => {
|
|
300
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
301
|
+
const result = dt.addMinutes(-15);
|
|
302
|
+
expect(result.valueOf()).toBe(dt.valueOf() - 15 * 60 * 1000);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
describe("addHours()", () => {
|
|
307
|
+
it("should add hours and return new DateTime", () => {
|
|
308
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
309
|
+
const result = dt.addHours(2);
|
|
310
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
311
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 2 * 60 * 60 * 1000);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("should handle negative values", () => {
|
|
315
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
316
|
+
const result = dt.addHours(-2);
|
|
317
|
+
expect(result.valueOf()).toBe(dt.valueOf() - 2 * 60 * 60 * 1000);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("should handle daylight saving time transitions", () => {
|
|
321
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
322
|
+
const result = dt.addHours(24);
|
|
323
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 24 * 60 * 60 * 1000);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe("addDays()", () => {
|
|
328
|
+
it("should add days and return new DateTime", () => {
|
|
329
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
330
|
+
const result = dt.addDays(5);
|
|
331
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
332
|
+
expect(result.toISO()).toBe("2024-01-20T10:30:00.000Z");
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("should handle negative values", () => {
|
|
336
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
337
|
+
const result = dt.addDays(-5);
|
|
338
|
+
expect(result.toISO()).toBe("2024-01-10T10:30:00.000Z");
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("should handle month boundaries", () => {
|
|
342
|
+
const dt = new DateTime("2024-01-30T10:30:00.000Z");
|
|
343
|
+
const result = dt.addDays(5);
|
|
344
|
+
expect(result.toISO()).toBe("2024-02-04T10:30:00.000Z");
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it("should handle year boundaries", () => {
|
|
348
|
+
const dt = new DateTime("2024-12-30T10:30:00.000Z");
|
|
349
|
+
const result = dt.addDays(5);
|
|
350
|
+
expect(result.toISO()).toBe("2025-01-04T10:30:00.000Z");
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
describe("addWeeks()", () => {
|
|
355
|
+
it("should add weeks and return new DateTime", () => {
|
|
356
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
357
|
+
const result = dt.addWeeks(2);
|
|
358
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
359
|
+
expect(result.toISO()).toBe("2024-01-29T10:30:00.000Z");
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("should handle negative values", () => {
|
|
363
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
364
|
+
const result = dt.addWeeks(-2);
|
|
365
|
+
expect(result.toISO()).toBe("2024-01-01T10:30:00.000Z");
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("should handle month boundaries", () => {
|
|
369
|
+
const dt = new DateTime("2024-01-25T10:30:00.000Z");
|
|
370
|
+
const result = dt.addWeeks(2);
|
|
371
|
+
expect(result.toISO()).toBe("2024-02-08T10:30:00.000Z");
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
describe("addMonths()", () => {
|
|
376
|
+
it("should add months and return new DateTime", () => {
|
|
377
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
378
|
+
const result = dt.addMonths(2);
|
|
379
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
380
|
+
expect(result.toISO()).toBe("2024-03-15T10:30:00.000Z");
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it("should handle negative values", () => {
|
|
384
|
+
const dt = new DateTime("2024-03-15T10:30:00.000Z");
|
|
385
|
+
const result = dt.addMonths(-2);
|
|
386
|
+
expect(result.toISO()).toBe("2024-01-15T10:30:00.000Z");
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it("should handle year boundaries", () => {
|
|
390
|
+
const dt = new DateTime("2024-11-15T10:30:00.000Z");
|
|
391
|
+
const result = dt.addMonths(3);
|
|
392
|
+
expect(result.toISO()).toBe("2025-02-15T10:30:00.000Z");
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("should handle day overflow (Jan 31 -> Feb 28/29)", () => {
|
|
396
|
+
const dt = new DateTime("2024-01-31T10:30:00.000Z");
|
|
397
|
+
const result = dt.addMonths(1);
|
|
398
|
+
// JavaScript Date automatically adjusts - Jan 31 + 1 month = Mar 2 (since Feb 29 exists but Jan has 31 days)
|
|
399
|
+
expect(result.toISO()).toBe("2024-03-02T10:30:00.000Z");
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it("should handle day overflow in non-leap year", () => {
|
|
403
|
+
const dt = new DateTime("2025-01-31T10:30:00.000Z");
|
|
404
|
+
const result = dt.addMonths(1);
|
|
405
|
+
// JavaScript Date automatically adjusts - Jan 31 + 1 month = Mar 3 (since Feb has only 28 days)
|
|
406
|
+
expect(result.toISO()).toBe("2025-03-03T10:30:00.000Z");
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it("should handle multiple month overflow", () => {
|
|
410
|
+
const dt = new DateTime("2024-01-31T10:30:00.000Z");
|
|
411
|
+
const result = dt.addMonths(13);
|
|
412
|
+
// 13 months from Jan 31, 2024 = Feb 31, 2025 -> Mar 3, 2025
|
|
413
|
+
expect(result.toISO()).toBe("2025-03-03T10:30:00.000Z");
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
describe("addYears()", () => {
|
|
418
|
+
it("should add years and return new DateTime", () => {
|
|
419
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
420
|
+
const result = dt.addYears(2);
|
|
421
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
422
|
+
expect(result.toISO()).toBe("2026-01-15T10:30:00.000Z");
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it("should handle negative values", () => {
|
|
426
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
427
|
+
const result = dt.addYears(-2);
|
|
428
|
+
expect(result.toISO()).toBe("2022-01-15T10:30:00.000Z");
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("should handle leap year to non-leap year (Feb 29 -> Feb 28)", () => {
|
|
432
|
+
const dt = new DateTime("2024-02-29T10:30:00.000Z");
|
|
433
|
+
const result = dt.addYears(1);
|
|
434
|
+
// JavaScript Date automatically adjusts Feb 29 -> Mar 1 in non-leap year
|
|
435
|
+
expect(result.toISO()).toBe("2025-03-01T10:30:00.000Z");
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("should handle large year additions", () => {
|
|
439
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
440
|
+
const result = dt.addYears(100);
|
|
441
|
+
expect(result.toISO()).toBe("2124-01-15T10:30:00.000Z");
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
describe("subtractMilliseconds()", () => {
|
|
446
|
+
it("should subtract milliseconds and return new DateTime", () => {
|
|
447
|
+
const dt = new DateTime("2024-01-15T10:30:00.500Z");
|
|
448
|
+
const result = dt.subtractMilliseconds(500);
|
|
449
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
450
|
+
expect(result.valueOf()).toBe(dt.valueOf() - 500);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("should handle negative values (effectively adds)", () => {
|
|
454
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
455
|
+
const result = dt.subtractMilliseconds(-500);
|
|
456
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 500);
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
describe("subtractSeconds()", () => {
|
|
461
|
+
it("should subtract seconds and return new DateTime", () => {
|
|
462
|
+
const dt = new DateTime("2024-01-15T10:30:30.000Z");
|
|
463
|
+
const result = dt.subtractSeconds(30);
|
|
464
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
465
|
+
expect(result.valueOf()).toBe(dt.valueOf() - 30000);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it("should handle negative values", () => {
|
|
469
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
470
|
+
const result = dt.subtractSeconds(-30);
|
|
471
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 30000);
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
describe("subtractMinutes()", () => {
|
|
476
|
+
it("should subtract minutes and return new DateTime", () => {
|
|
477
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
478
|
+
const result = dt.subtractMinutes(15);
|
|
479
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
480
|
+
expect(result.valueOf()).toBe(dt.valueOf() - 15 * 60 * 1000);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it("should handle negative values", () => {
|
|
484
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
485
|
+
const result = dt.subtractMinutes(-15);
|
|
486
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 15 * 60 * 1000);
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
describe("subtractHours()", () => {
|
|
491
|
+
it("should subtract hours and return new DateTime", () => {
|
|
492
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
493
|
+
const result = dt.subtractHours(2);
|
|
494
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
495
|
+
expect(result.valueOf()).toBe(dt.valueOf() - 2 * 60 * 60 * 1000);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it("should handle negative values", () => {
|
|
499
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
500
|
+
const result = dt.subtractHours(-2);
|
|
501
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 2 * 60 * 60 * 1000);
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
describe("subtractDays()", () => {
|
|
506
|
+
it("should subtract days and return new DateTime", () => {
|
|
507
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
508
|
+
const result = dt.subtractDays(5);
|
|
509
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
510
|
+
expect(result.toISO()).toBe("2024-01-10T10:30:00.000Z");
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it("should handle negative values", () => {
|
|
514
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
515
|
+
const result = dt.subtractDays(-5);
|
|
516
|
+
expect(result.toISO()).toBe("2024-01-20T10:30:00.000Z");
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it("should handle month boundaries", () => {
|
|
520
|
+
const dt = new DateTime("2024-02-05T10:30:00.000Z");
|
|
521
|
+
const result = dt.subtractDays(10);
|
|
522
|
+
expect(result.toISO()).toBe("2024-01-26T10:30:00.000Z");
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
describe("subtractWeeks()", () => {
|
|
527
|
+
it("should subtract weeks and return new DateTime", () => {
|
|
528
|
+
const dt = new DateTime("2024-01-29T10:30:00.000Z");
|
|
529
|
+
const result = dt.subtractWeeks(2);
|
|
530
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
531
|
+
expect(result.toISO()).toBe("2024-01-15T10:30:00.000Z");
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it("should handle negative values", () => {
|
|
535
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
536
|
+
const result = dt.subtractWeeks(-2);
|
|
537
|
+
expect(result.toISO()).toBe("2024-01-29T10:30:00.000Z");
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
describe("subtractMonths()", () => {
|
|
542
|
+
it("should subtract months and return new DateTime", () => {
|
|
543
|
+
const dt = new DateTime("2024-03-15T10:30:00.000Z");
|
|
544
|
+
const result = dt.subtractMonths(2);
|
|
545
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
546
|
+
expect(result.toISO()).toBe("2024-01-15T10:30:00.000Z");
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it("should handle negative values", () => {
|
|
550
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
551
|
+
const result = dt.subtractMonths(-2);
|
|
552
|
+
expect(result.toISO()).toBe("2024-03-15T10:30:00.000Z");
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it("should handle year boundaries", () => {
|
|
556
|
+
const dt = new DateTime("2024-02-15T10:30:00.000Z");
|
|
557
|
+
const result = dt.subtractMonths(3);
|
|
558
|
+
expect(result.toISO()).toBe("2023-11-15T10:30:00.000Z");
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it("should handle day overflow", () => {
|
|
562
|
+
const dt = new DateTime("2024-03-31T10:30:00.000Z");
|
|
563
|
+
const result = dt.subtractMonths(1);
|
|
564
|
+
// JavaScript Date automatically adjusts Mar 31 -> Feb 31 -> Mar 2
|
|
565
|
+
expect(result.toISO()).toBe("2024-03-02T10:30:00.000Z");
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
describe("subtractYears()", () => {
|
|
570
|
+
it("should subtract years and return new DateTime", () => {
|
|
571
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
572
|
+
const result = dt.subtractYears(2);
|
|
573
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
574
|
+
expect(result.toISO()).toBe("2022-01-15T10:30:00.000Z");
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it("should handle negative values", () => {
|
|
578
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
579
|
+
const result = dt.subtractYears(-2);
|
|
580
|
+
expect(result.toISO()).toBe("2026-01-15T10:30:00.000Z");
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it("should handle leap year to non-leap year", () => {
|
|
584
|
+
const dt = new DateTime("2024-02-29T10:30:00.000Z");
|
|
585
|
+
const result = dt.subtractYears(1);
|
|
586
|
+
// JavaScript Date automatically adjusts Feb 29 -> Mar 1 in non-leap year
|
|
587
|
+
expect(result.toISO()).toBe("2023-03-01T10:30:00.000Z");
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
describe("Chaining arithmetic operations", () => {
|
|
592
|
+
it("should allow chaining multiple operations", () => {
|
|
593
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
594
|
+
const result = dt
|
|
595
|
+
.addDays(5)
|
|
596
|
+
.addHours(2)
|
|
597
|
+
.addMinutes(30)
|
|
598
|
+
.subtractSeconds(15);
|
|
599
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
600
|
+
// 10:30 + 2h = 12:30, + 30m = 13:00, - 15s = 12:59:45
|
|
601
|
+
expect(result.toISO()).toBe("2024-01-20T12:59:45.000Z");
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it("should maintain immutability during chaining", () => {
|
|
605
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
606
|
+
const original = dt.valueOf();
|
|
607
|
+
dt.addDays(5).addHours(2);
|
|
608
|
+
expect(dt.valueOf()).toBe(original); // Original unchanged
|
|
609
|
+
});
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
describe("Edge cases", () => {
|
|
613
|
+
it("should handle invalid dates", () => {
|
|
614
|
+
const dt = new DateTime("invalid");
|
|
615
|
+
const result = dt.addDays(1);
|
|
616
|
+
expect(result.isValid()).toBe(false);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it("should handle very large numbers", () => {
|
|
620
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
621
|
+
const result = dt.addDays(10000);
|
|
622
|
+
expect(result.isValid()).toBe(true);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it("should handle floating point numbers", () => {
|
|
626
|
+
const dt = new DateTime("2024-01-15T10:30:00.000Z");
|
|
627
|
+
const result = dt.addMilliseconds(123.456);
|
|
628
|
+
// JavaScript Date truncates to integer milliseconds
|
|
629
|
+
expect(result.valueOf()).toBe(dt.valueOf() + 123);
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
describe("Comparison Methods", () => {
|
|
635
|
+
describe("isBefore()", () => {
|
|
636
|
+
it("should return true when date is before other", () => {
|
|
637
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
638
|
+
const dt2 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
639
|
+
expect(dt1.isBefore(dt2)).toBe(true);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it("should return false when date is after other", () => {
|
|
643
|
+
const dt1 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
644
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
645
|
+
expect(dt1.isBefore(dt2)).toBe(false);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it("should return false when dates are equal", () => {
|
|
649
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
650
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
651
|
+
expect(dt1.isBefore(dt2)).toBe(false);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it("should work with millisecond precision", () => {
|
|
655
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
656
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.001Z");
|
|
657
|
+
expect(dt1.isBefore(dt2)).toBe(true);
|
|
658
|
+
expect(dt2.isBefore(dt1)).toBe(false);
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it("should accept DateTime instance", () => {
|
|
662
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
663
|
+
const dt2 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
664
|
+
expect(dt1.isBefore(dt2)).toBe(true);
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
describe("isAfter()", () => {
|
|
669
|
+
it("should return true when date is after other", () => {
|
|
670
|
+
const dt1 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
671
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
672
|
+
expect(dt1.isAfter(dt2)).toBe(true);
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
it("should return false when date is before other", () => {
|
|
676
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
677
|
+
const dt2 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
678
|
+
expect(dt1.isAfter(dt2)).toBe(false);
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
it("should return false when dates are equal", () => {
|
|
682
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
683
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
684
|
+
expect(dt1.isAfter(dt2)).toBe(false);
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
it("should work with millisecond precision", () => {
|
|
688
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.001Z");
|
|
689
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
690
|
+
expect(dt1.isAfter(dt2)).toBe(true);
|
|
691
|
+
expect(dt2.isAfter(dt1)).toBe(false);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
it("should accept DateTime instance", () => {
|
|
695
|
+
const dt1 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
696
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
697
|
+
expect(dt1.isAfter(dt2)).toBe(true);
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
describe("isSame()", () => {
|
|
702
|
+
it("should return true when dates are equal", () => {
|
|
703
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
704
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
705
|
+
expect(dt1.isSame(dt2)).toBe(true);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it("should return false when dates are different", () => {
|
|
709
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
710
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.001Z");
|
|
711
|
+
expect(dt1.isSame(dt2)).toBe(false);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it("should work with millisecond precision", () => {
|
|
715
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.123Z");
|
|
716
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.123Z");
|
|
717
|
+
expect(dt1.isSame(dt2)).toBe(true);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it("should return true for same timestamp from different inputs", () => {
|
|
721
|
+
const timestamp = 1705314600000;
|
|
722
|
+
const dt1 = new DateTime(timestamp);
|
|
723
|
+
const dt2 = new DateTime("2024-01-15T10:30:00.000Z");
|
|
724
|
+
expect(dt1.isSame(dt2)).toBe(true);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it("should accept DateTime instance", () => {
|
|
728
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
729
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
730
|
+
expect(dt1.isSame(dt2)).toBe(true);
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
describe("isSameOrBefore()", () => {
|
|
735
|
+
it("should return true when date is before other", () => {
|
|
736
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
737
|
+
const dt2 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
738
|
+
expect(dt1.isSameOrBefore(dt2)).toBe(true);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
it("should return true when dates are equal", () => {
|
|
742
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
743
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
744
|
+
expect(dt1.isSameOrBefore(dt2)).toBe(true);
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
it("should return false when date is after other", () => {
|
|
748
|
+
const dt1 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
749
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
750
|
+
expect(dt1.isSameOrBefore(dt2)).toBe(false);
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it("should work with millisecond precision", () => {
|
|
754
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
755
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.001Z");
|
|
756
|
+
expect(dt1.isSameOrBefore(dt2)).toBe(true);
|
|
757
|
+
expect(dt1.isSameOrBefore(dt1)).toBe(true);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
it("should accept DateTime instance", () => {
|
|
761
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
762
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
763
|
+
expect(dt1.isSameOrBefore(dt2)).toBe(true);
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
describe("isSameOrAfter()", () => {
|
|
768
|
+
it("should return true when date is after other", () => {
|
|
769
|
+
const dt1 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
770
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
771
|
+
expect(dt1.isSameOrAfter(dt2)).toBe(true);
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it("should return true when dates are equal", () => {
|
|
775
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
776
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
777
|
+
expect(dt1.isSameOrAfter(dt2)).toBe(true);
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it("should return false when date is before other", () => {
|
|
781
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
782
|
+
const dt2 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
783
|
+
expect(dt1.isSameOrAfter(dt2)).toBe(false);
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
it("should work with millisecond precision", () => {
|
|
787
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.001Z");
|
|
788
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
789
|
+
expect(dt1.isSameOrAfter(dt2)).toBe(true);
|
|
790
|
+
expect(dt1.isSameOrAfter(dt1)).toBe(true);
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
it("should accept DateTime instance", () => {
|
|
794
|
+
const dt1 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
795
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
796
|
+
expect(dt1.isSameOrAfter(dt2)).toBe(true);
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
describe("isBetween()", () => {
|
|
801
|
+
it("should return true when date is between start and end (exclusive)", () => {
|
|
802
|
+
const dt = new DateTime("2024-01-15T11:00:00.000Z");
|
|
803
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
804
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
805
|
+
expect(dt.isBetween(start, end)).toBe(true);
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
it("should return false when date equals start (exclusive)", () => {
|
|
809
|
+
const dt = new DateTime("2024-01-15T10:00:00.000Z");
|
|
810
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
811
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
812
|
+
expect(dt.isBetween(start, end)).toBe(false);
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
it("should return false when date equals end (exclusive)", () => {
|
|
816
|
+
const dt = new DateTime("2024-01-15T12:00:00.000Z");
|
|
817
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
818
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
819
|
+
expect(dt.isBetween(start, end)).toBe(false);
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it("should return false when date is before start (exclusive)", () => {
|
|
823
|
+
const dt = new DateTime("2024-01-15T09:00:00.000Z");
|
|
824
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
825
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
826
|
+
expect(dt.isBetween(start, end)).toBe(false);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
it("should return false when date is after end (exclusive)", () => {
|
|
830
|
+
const dt = new DateTime("2024-01-15T13:00:00.000Z");
|
|
831
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
832
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
833
|
+
expect(dt.isBetween(start, end)).toBe(false);
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
it("should return true when date is between start and end (inclusive)", () => {
|
|
837
|
+
const dt = new DateTime("2024-01-15T11:00:00.000Z");
|
|
838
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
839
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
840
|
+
expect(dt.isBetween(start, end, true)).toBe(true);
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
it("should return true when date equals start (inclusive)", () => {
|
|
844
|
+
const dt = new DateTime("2024-01-15T10:00:00.000Z");
|
|
845
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
846
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
847
|
+
expect(dt.isBetween(start, end, true)).toBe(true);
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
it("should return true when date equals end (inclusive)", () => {
|
|
851
|
+
const dt = new DateTime("2024-01-15T12:00:00.000Z");
|
|
852
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
853
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
854
|
+
expect(dt.isBetween(start, end, true)).toBe(true);
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
it("should return false when date is before start (inclusive)", () => {
|
|
858
|
+
const dt = new DateTime("2024-01-15T09:00:00.000Z");
|
|
859
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
860
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
861
|
+
expect(dt.isBetween(start, end, true)).toBe(false);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
it("should return false when date is after end (inclusive)", () => {
|
|
865
|
+
const dt = new DateTime("2024-01-15T13:00:00.000Z");
|
|
866
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
867
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
868
|
+
expect(dt.isBetween(start, end, true)).toBe(false);
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
it("should work with millisecond precision (exclusive)", () => {
|
|
872
|
+
const dt = new DateTime("2024-01-15T10:00:00.001Z");
|
|
873
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
874
|
+
const end = new DateTime("2024-01-15T10:00:00.002Z");
|
|
875
|
+
expect(dt.isBetween(start, end)).toBe(true);
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
it("should work with millisecond precision (inclusive)", () => {
|
|
879
|
+
const dt = new DateTime("2024-01-15T10:00:00.000Z");
|
|
880
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
881
|
+
const end = new DateTime("2024-01-15T10:00:00.002Z");
|
|
882
|
+
expect(dt.isBetween(start, end, true)).toBe(true);
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
it("should accept DateTime instances", () => {
|
|
886
|
+
const dt = new DateTime("2024-01-15T11:00:00.000Z");
|
|
887
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
888
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
889
|
+
expect(dt.isBetween(start, end)).toBe(true);
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
it("should default to exclusive when inclusive parameter is not provided", () => {
|
|
893
|
+
const dt = new DateTime("2024-01-15T10:00:00.000Z");
|
|
894
|
+
const start = new DateTime("2024-01-15T10:00:00.000Z");
|
|
895
|
+
const end = new DateTime("2024-01-15T12:00:00.000Z");
|
|
896
|
+
expect(dt.isBetween(start, end)).toBe(false);
|
|
897
|
+
});
|
|
898
|
+
});
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
describe("Getter Methods", () => {
|
|
902
|
+
describe("year()", () => {
|
|
903
|
+
it("should return the UTC year", () => {
|
|
904
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
905
|
+
expect(dt.year()).toBe(2024);
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
it("should return correct year for different dates", () => {
|
|
909
|
+
expect(new DateTime("2025-12-31T23:59:59.999Z").year()).toBe(2025);
|
|
910
|
+
expect(new DateTime("2000-01-01T00:00:00.000Z").year()).toBe(2000);
|
|
911
|
+
});
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
describe("month()", () => {
|
|
915
|
+
it("should return the UTC month (0-indexed)", () => {
|
|
916
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
917
|
+
expect(dt.month()).toBe(0); // January
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
it("should return correct month for different dates", () => {
|
|
921
|
+
expect(new DateTime("2024-02-15T10:30:00.000Z").month()).toBe(1); // February
|
|
922
|
+
expect(new DateTime("2024-12-15T10:30:00.000Z").month()).toBe(11); // December
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
describe("date()", () => {
|
|
927
|
+
it("should return the UTC day of month", () => {
|
|
928
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
929
|
+
expect(dt.date()).toBe(15);
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
it("should return correct date for different days", () => {
|
|
933
|
+
expect(new DateTime("2024-01-01T10:30:00.000Z").date()).toBe(1);
|
|
934
|
+
expect(new DateTime("2024-01-31T10:30:00.000Z").date()).toBe(31);
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
describe("day()", () => {
|
|
939
|
+
it("should return the UTC day of week (0-indexed, 0=Sunday)", () => {
|
|
940
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z"); // Monday
|
|
941
|
+
expect(dt.day()).toBe(1);
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
it("should return correct day for different days of week", () => {
|
|
945
|
+
expect(new DateTime("2024-01-14T10:30:00.000Z").day()).toBe(0); // Sunday
|
|
946
|
+
expect(new DateTime("2024-01-20T10:30:00.000Z").day()).toBe(6); // Saturday
|
|
947
|
+
});
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
describe("hour()", () => {
|
|
951
|
+
it("should return the UTC hour", () => {
|
|
952
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
953
|
+
expect(dt.hour()).toBe(10);
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
it("should return correct hour for different times", () => {
|
|
957
|
+
expect(new DateTime("2024-01-15T00:30:00.000Z").hour()).toBe(0);
|
|
958
|
+
expect(new DateTime("2024-01-15T23:30:00.000Z").hour()).toBe(23);
|
|
959
|
+
});
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
describe("minute()", () => {
|
|
963
|
+
it("should return the UTC minute", () => {
|
|
964
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
965
|
+
expect(dt.minute()).toBe(30);
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
it("should return correct minute for different times", () => {
|
|
969
|
+
expect(new DateTime("2024-01-15T10:00:00.000Z").minute()).toBe(0);
|
|
970
|
+
expect(new DateTime("2024-01-15T10:59:00.000Z").minute()).toBe(59);
|
|
971
|
+
});
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
describe("second()", () => {
|
|
975
|
+
it("should return the UTC second", () => {
|
|
976
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
977
|
+
expect(dt.second()).toBe(45);
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
it("should return correct second for different times", () => {
|
|
981
|
+
expect(new DateTime("2024-01-15T10:30:00.000Z").second()).toBe(0);
|
|
982
|
+
expect(new DateTime("2024-01-15T10:30:59.000Z").second()).toBe(59);
|
|
983
|
+
});
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
describe("millisecond()", () => {
|
|
987
|
+
it("should return the UTC millisecond", () => {
|
|
988
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
989
|
+
expect(dt.millisecond()).toBe(123);
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
it("should return correct millisecond for different times", () => {
|
|
993
|
+
expect(new DateTime("2024-01-15T10:30:45.000Z").millisecond()).toBe(
|
|
994
|
+
0,
|
|
995
|
+
);
|
|
996
|
+
expect(new DateTime("2024-01-15T10:30:45.999Z").millisecond()).toBe(
|
|
997
|
+
999,
|
|
998
|
+
);
|
|
999
|
+
});
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
describe("Setter Methods", () => {
|
|
1004
|
+
describe("setYear()", () => {
|
|
1005
|
+
it("should return new DateTime with updated year", () => {
|
|
1006
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1007
|
+
const result = dt.setYear(2025);
|
|
1008
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1009
|
+
expect(result.year()).toBe(2025);
|
|
1010
|
+
expect(result.toISO()).toBe("2025-01-15T10:30:45.123Z");
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
it("should not modify original DateTime", () => {
|
|
1014
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1015
|
+
const original = dt.toISO();
|
|
1016
|
+
dt.setYear(2025);
|
|
1017
|
+
expect(dt.toISO()).toBe(original);
|
|
1018
|
+
});
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
describe("setMonth()", () => {
|
|
1022
|
+
it("should return new DateTime with updated month", () => {
|
|
1023
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1024
|
+
const result = dt.setMonth(5); // June (0-indexed)
|
|
1025
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1026
|
+
expect(result.month()).toBe(5);
|
|
1027
|
+
expect(result.toISO()).toBe("2024-06-15T10:30:45.123Z");
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
it("should not modify original DateTime", () => {
|
|
1031
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1032
|
+
const original = dt.toISO();
|
|
1033
|
+
dt.setMonth(5);
|
|
1034
|
+
expect(dt.toISO()).toBe(original);
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
it("should handle day overflow", () => {
|
|
1038
|
+
const dt = new DateTime("2024-01-31T10:30:45.123Z");
|
|
1039
|
+
const result = dt.setMonth(1); // February
|
|
1040
|
+
// Jan 31 -> Feb 31 -> Mar 2 (2024 is leap year, Feb has 29 days)
|
|
1041
|
+
expect(result.toISO()).toBe("2024-03-02T10:30:45.123Z");
|
|
1042
|
+
});
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
describe("setDate()", () => {
|
|
1046
|
+
it("should return new DateTime with updated date", () => {
|
|
1047
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1048
|
+
const result = dt.setDate(25);
|
|
1049
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1050
|
+
expect(result.date()).toBe(25);
|
|
1051
|
+
expect(result.toISO()).toBe("2024-01-25T10:30:45.123Z");
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
it("should not modify original DateTime", () => {
|
|
1055
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1056
|
+
const original = dt.toISO();
|
|
1057
|
+
dt.setDate(25);
|
|
1058
|
+
expect(dt.toISO()).toBe(original);
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
it("should handle day overflow", () => {
|
|
1062
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1063
|
+
const result = dt.setDate(32);
|
|
1064
|
+
// Jan 32 -> Feb 1
|
|
1065
|
+
expect(result.toISO()).toBe("2024-02-01T10:30:45.123Z");
|
|
1066
|
+
});
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
describe("setHour()", () => {
|
|
1070
|
+
it("should return new DateTime with updated hour", () => {
|
|
1071
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1072
|
+
const result = dt.setHour(15);
|
|
1073
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1074
|
+
expect(result.hour()).toBe(15);
|
|
1075
|
+
expect(result.toISO()).toBe("2024-01-15T15:30:45.123Z");
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
it("should not modify original DateTime", () => {
|
|
1079
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1080
|
+
const original = dt.toISO();
|
|
1081
|
+
dt.setHour(15);
|
|
1082
|
+
expect(dt.toISO()).toBe(original);
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
it("should handle hour overflow", () => {
|
|
1086
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1087
|
+
const result = dt.setHour(25);
|
|
1088
|
+
// Hour 25 -> next day, hour 1
|
|
1089
|
+
expect(result.toISO()).toBe("2024-01-16T01:30:45.123Z");
|
|
1090
|
+
});
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
describe("setMinute()", () => {
|
|
1094
|
+
it("should return new DateTime with updated minute", () => {
|
|
1095
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1096
|
+
const result = dt.setMinute(45);
|
|
1097
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1098
|
+
expect(result.minute()).toBe(45);
|
|
1099
|
+
expect(result.toISO()).toBe("2024-01-15T10:45:45.123Z");
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
it("should not modify original DateTime", () => {
|
|
1103
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1104
|
+
const original = dt.toISO();
|
|
1105
|
+
dt.setMinute(45);
|
|
1106
|
+
expect(dt.toISO()).toBe(original);
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
it("should handle minute overflow", () => {
|
|
1110
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1111
|
+
const result = dt.setMinute(65);
|
|
1112
|
+
// Minute 65 -> next hour, minute 5
|
|
1113
|
+
expect(result.toISO()).toBe("2024-01-15T11:05:45.123Z");
|
|
1114
|
+
});
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
describe("setSecond()", () => {
|
|
1118
|
+
it("should return new DateTime with updated second", () => {
|
|
1119
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1120
|
+
const result = dt.setSecond(30);
|
|
1121
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1122
|
+
expect(result.second()).toBe(30);
|
|
1123
|
+
expect(result.toISO()).toBe("2024-01-15T10:30:30.123Z");
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
it("should not modify original DateTime", () => {
|
|
1127
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1128
|
+
const original = dt.toISO();
|
|
1129
|
+
dt.setSecond(30);
|
|
1130
|
+
expect(dt.toISO()).toBe(original);
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
it("should handle second overflow", () => {
|
|
1134
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1135
|
+
const result = dt.setSecond(65);
|
|
1136
|
+
// Second 65 -> next minute, second 5
|
|
1137
|
+
expect(result.toISO()).toBe("2024-01-15T10:31:05.123Z");
|
|
1138
|
+
});
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
describe("setMillisecond()", () => {
|
|
1142
|
+
it("should return new DateTime with updated millisecond", () => {
|
|
1143
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1144
|
+
const result = dt.setMillisecond(456);
|
|
1145
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1146
|
+
expect(result.millisecond()).toBe(456);
|
|
1147
|
+
expect(result.toISO()).toBe("2024-01-15T10:30:45.456Z");
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
it("should not modify original DateTime", () => {
|
|
1151
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1152
|
+
const original = dt.toISO();
|
|
1153
|
+
dt.setMillisecond(456);
|
|
1154
|
+
expect(dt.toISO()).toBe(original);
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
it("should handle millisecond overflow", () => {
|
|
1158
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1159
|
+
const result = dt.setMillisecond(1500);
|
|
1160
|
+
// Millisecond 1500 -> next second, millisecond 500
|
|
1161
|
+
expect(result.toISO()).toBe("2024-01-15T10:30:46.500Z");
|
|
1162
|
+
});
|
|
1163
|
+
});
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
describe("Start/End Methods", () => {
|
|
1167
|
+
describe("startOfDay()", () => {
|
|
1168
|
+
it("should return new DateTime at start of day (00:00:00.000)", () => {
|
|
1169
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1170
|
+
const result = dt.startOfDay();
|
|
1171
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1172
|
+
expect(result.toISO()).toBe("2024-01-15T00:00:00.000Z");
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
it("should not modify original DateTime", () => {
|
|
1176
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1177
|
+
const original = dt.toISO();
|
|
1178
|
+
dt.startOfDay();
|
|
1179
|
+
expect(dt.toISO()).toBe(original);
|
|
1180
|
+
});
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
describe("endOfDay()", () => {
|
|
1184
|
+
it("should return new DateTime at end of day (23:59:59.999)", () => {
|
|
1185
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1186
|
+
const result = dt.endOfDay();
|
|
1187
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1188
|
+
expect(result.toISO()).toBe("2024-01-15T23:59:59.999Z");
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
it("should not modify original DateTime", () => {
|
|
1192
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1193
|
+
const original = dt.toISO();
|
|
1194
|
+
dt.endOfDay();
|
|
1195
|
+
expect(dt.toISO()).toBe(original);
|
|
1196
|
+
});
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
describe("startOfHour()", () => {
|
|
1200
|
+
it("should return new DateTime at start of hour (XX:00:00.000)", () => {
|
|
1201
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1202
|
+
const result = dt.startOfHour();
|
|
1203
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1204
|
+
expect(result.toISO()).toBe("2024-01-15T10:00:00.000Z");
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
it("should not modify original DateTime", () => {
|
|
1208
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1209
|
+
const original = dt.toISO();
|
|
1210
|
+
dt.startOfHour();
|
|
1211
|
+
expect(dt.toISO()).toBe(original);
|
|
1212
|
+
});
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
describe("endOfHour()", () => {
|
|
1216
|
+
it("should return new DateTime at end of hour (XX:59:59.999)", () => {
|
|
1217
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1218
|
+
const result = dt.endOfHour();
|
|
1219
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1220
|
+
expect(result.toISO()).toBe("2024-01-15T10:59:59.999Z");
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
it("should not modify original DateTime", () => {
|
|
1224
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1225
|
+
const original = dt.toISO();
|
|
1226
|
+
dt.endOfHour();
|
|
1227
|
+
expect(dt.toISO()).toBe(original);
|
|
1228
|
+
});
|
|
1229
|
+
});
|
|
1230
|
+
|
|
1231
|
+
describe("startOfWeek()", () => {
|
|
1232
|
+
it("should return new DateTime at start of week (Sunday 00:00:00.000)", () => {
|
|
1233
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z"); // Monday
|
|
1234
|
+
const result = dt.startOfWeek();
|
|
1235
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1236
|
+
// Previous Sunday is Jan 14
|
|
1237
|
+
expect(result.toISO()).toBe("2024-01-14T00:00:00.000Z");
|
|
1238
|
+
expect(result.day()).toBe(0); // Sunday
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
it("should handle when day is already Sunday", () => {
|
|
1242
|
+
const dt = new DateTime("2024-01-14T10:30:45.123Z"); // Sunday
|
|
1243
|
+
const result = dt.startOfWeek();
|
|
1244
|
+
expect(result.toISO()).toBe("2024-01-14T00:00:00.000Z");
|
|
1245
|
+
expect(result.day()).toBe(0); // Sunday
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
it("should not modify original DateTime", () => {
|
|
1249
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1250
|
+
const original = dt.toISO();
|
|
1251
|
+
dt.startOfWeek();
|
|
1252
|
+
expect(dt.toISO()).toBe(original);
|
|
1253
|
+
});
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
describe("endOfWeek()", () => {
|
|
1257
|
+
it("should return new DateTime at end of week (Saturday 23:59:59.999)", () => {
|
|
1258
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z"); // Monday
|
|
1259
|
+
const result = dt.endOfWeek();
|
|
1260
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1261
|
+
// Next Saturday is Jan 20
|
|
1262
|
+
expect(result.toISO()).toBe("2024-01-20T23:59:59.999Z");
|
|
1263
|
+
expect(result.day()).toBe(6); // Saturday
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1266
|
+
it("should handle when day is already Saturday", () => {
|
|
1267
|
+
const dt = new DateTime("2024-01-20T10:30:45.123Z"); // Saturday
|
|
1268
|
+
const result = dt.endOfWeek();
|
|
1269
|
+
expect(result.toISO()).toBe("2024-01-20T23:59:59.999Z");
|
|
1270
|
+
expect(result.day()).toBe(6); // Saturday
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
it("should not modify original DateTime", () => {
|
|
1274
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1275
|
+
const original = dt.toISO();
|
|
1276
|
+
dt.endOfWeek();
|
|
1277
|
+
expect(dt.toISO()).toBe(original);
|
|
1278
|
+
});
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
describe("startOfMonth()", () => {
|
|
1282
|
+
it("should return new DateTime at start of month (day 1, 00:00:00.000)", () => {
|
|
1283
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1284
|
+
const result = dt.startOfMonth();
|
|
1285
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1286
|
+
expect(result.toISO()).toBe("2024-01-01T00:00:00.000Z");
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
it("should handle when day is already 1st", () => {
|
|
1290
|
+
const dt = new DateTime("2024-01-01T10:30:45.123Z");
|
|
1291
|
+
const result = dt.startOfMonth();
|
|
1292
|
+
expect(result.toISO()).toBe("2024-01-01T00:00:00.000Z");
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
it("should not modify original DateTime", () => {
|
|
1296
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1297
|
+
const original = dt.toISO();
|
|
1298
|
+
dt.startOfMonth();
|
|
1299
|
+
expect(dt.toISO()).toBe(original);
|
|
1300
|
+
});
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
describe("endOfMonth()", () => {
|
|
1304
|
+
it("should return new DateTime at end of month (last day, 23:59:59.999)", () => {
|
|
1305
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1306
|
+
const result = dt.endOfMonth();
|
|
1307
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1308
|
+
expect(result.toISO()).toBe("2024-01-31T23:59:59.999Z");
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1311
|
+
it("should handle February in leap year", () => {
|
|
1312
|
+
const dt = new DateTime("2024-02-15T10:30:45.123Z");
|
|
1313
|
+
const result = dt.endOfMonth();
|
|
1314
|
+
expect(result.toISO()).toBe("2024-02-29T23:59:59.999Z");
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
it("should handle February in non-leap year", () => {
|
|
1318
|
+
const dt = new DateTime("2025-02-15T10:30:45.123Z");
|
|
1319
|
+
const result = dt.endOfMonth();
|
|
1320
|
+
expect(result.toISO()).toBe("2025-02-28T23:59:59.999Z");
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
it("should handle 30-day months", () => {
|
|
1324
|
+
const dt = new DateTime("2024-04-15T10:30:45.123Z");
|
|
1325
|
+
const result = dt.endOfMonth();
|
|
1326
|
+
expect(result.toISO()).toBe("2024-04-30T23:59:59.999Z");
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
it("should not modify original DateTime", () => {
|
|
1330
|
+
const dt = new DateTime("2024-01-15T10:30:45.123Z");
|
|
1331
|
+
const original = dt.toISO();
|
|
1332
|
+
dt.endOfMonth();
|
|
1333
|
+
expect(dt.toISO()).toBe(original);
|
|
1334
|
+
});
|
|
1335
|
+
});
|
|
1336
|
+
|
|
1337
|
+
describe("startOfYear()", () => {
|
|
1338
|
+
it("should return new DateTime at start of year (Jan 1, 00:00:00.000)", () => {
|
|
1339
|
+
const dt = new DateTime("2024-06-15T10:30:45.123Z");
|
|
1340
|
+
const result = dt.startOfYear();
|
|
1341
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1342
|
+
expect(result.toISO()).toBe("2024-01-01T00:00:00.000Z");
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
it("should handle when already at start of year", () => {
|
|
1346
|
+
const dt = new DateTime("2024-01-01T10:30:45.123Z");
|
|
1347
|
+
const result = dt.startOfYear();
|
|
1348
|
+
expect(result.toISO()).toBe("2024-01-01T00:00:00.000Z");
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
it("should not modify original DateTime", () => {
|
|
1352
|
+
const dt = new DateTime("2024-06-15T10:30:45.123Z");
|
|
1353
|
+
const original = dt.toISO();
|
|
1354
|
+
dt.startOfYear();
|
|
1355
|
+
expect(dt.toISO()).toBe(original);
|
|
1356
|
+
});
|
|
1357
|
+
});
|
|
1358
|
+
|
|
1359
|
+
describe("endOfYear()", () => {
|
|
1360
|
+
it("should return new DateTime at end of year (Dec 31, 23:59:59.999)", () => {
|
|
1361
|
+
const dt = new DateTime("2024-06-15T10:30:45.123Z");
|
|
1362
|
+
const result = dt.endOfYear();
|
|
1363
|
+
expect(result).toBeInstanceOf(DateTime);
|
|
1364
|
+
expect(result.toISO()).toBe("2024-12-31T23:59:59.999Z");
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
it("should handle when already at end of year", () => {
|
|
1368
|
+
const dt = new DateTime("2024-12-31T10:30:45.123Z");
|
|
1369
|
+
const result = dt.endOfYear();
|
|
1370
|
+
expect(result.toISO()).toBe("2024-12-31T23:59:59.999Z");
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1373
|
+
it("should not modify original DateTime", () => {
|
|
1374
|
+
const dt = new DateTime("2024-06-15T10:30:45.123Z");
|
|
1375
|
+
const original = dt.toISO();
|
|
1376
|
+
dt.endOfYear();
|
|
1377
|
+
expect(dt.toISO()).toBe(original);
|
|
1378
|
+
});
|
|
1379
|
+
});
|
|
1380
|
+
});
|
|
1381
|
+
|
|
1382
|
+
describe("Difference Method", () => {
|
|
1383
|
+
describe("diff()", () => {
|
|
1384
|
+
it("should calculate difference in milliseconds by default", () => {
|
|
1385
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1386
|
+
const dt2 = new DateTime("2024-01-15T10:00:01.500Z");
|
|
1387
|
+
expect(dt1.diff(dt2)).toBe(-1500);
|
|
1388
|
+
expect(dt2.diff(dt1)).toBe(1500);
|
|
1389
|
+
});
|
|
1390
|
+
|
|
1391
|
+
it("should calculate difference in seconds", () => {
|
|
1392
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1393
|
+
const dt2 = new DateTime("2024-01-15T10:01:30.000Z");
|
|
1394
|
+
expect(dt1.diff(dt2, "second")).toBe(-90);
|
|
1395
|
+
expect(dt2.diff(dt1, "second")).toBe(90);
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
it("should calculate difference in minutes", () => {
|
|
1399
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1400
|
+
const dt2 = new DateTime("2024-01-15T11:30:00.000Z");
|
|
1401
|
+
expect(dt1.diff(dt2, "minute")).toBe(-90);
|
|
1402
|
+
expect(dt2.diff(dt1, "minute")).toBe(90);
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1405
|
+
it("should calculate difference in hours", () => {
|
|
1406
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1407
|
+
const dt2 = new DateTime("2024-01-15T22:00:00.000Z");
|
|
1408
|
+
expect(dt1.diff(dt2, "hour")).toBe(-12);
|
|
1409
|
+
expect(dt2.diff(dt1, "hour")).toBe(12);
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1412
|
+
it("should calculate difference in days", () => {
|
|
1413
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1414
|
+
const dt2 = new DateTime("2024-01-20T10:00:00.000Z");
|
|
1415
|
+
expect(dt1.diff(dt2, "day")).toBe(-5);
|
|
1416
|
+
expect(dt2.diff(dt1, "day")).toBe(5);
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1419
|
+
it("should calculate difference in weeks", () => {
|
|
1420
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1421
|
+
const dt2 = new DateTime("2024-01-29T10:00:00.000Z");
|
|
1422
|
+
expect(dt1.diff(dt2, "week")).toBe(-2);
|
|
1423
|
+
expect(dt2.diff(dt1, "week")).toBe(2);
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
it("should calculate difference in months", () => {
|
|
1427
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1428
|
+
const dt2 = new DateTime("2024-04-15T10:00:00.000Z");
|
|
1429
|
+
expect(dt1.diff(dt2, "month")).toBe(-3);
|
|
1430
|
+
expect(dt2.diff(dt1, "month")).toBe(3);
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
it("should calculate difference in years", () => {
|
|
1434
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1435
|
+
const dt2 = new DateTime("2026-01-15T10:00:00.000Z");
|
|
1436
|
+
expect(dt1.diff(dt2, "year")).toBeCloseTo(-2, 1);
|
|
1437
|
+
expect(dt2.diff(dt1, "year")).toBeCloseTo(2, 1);
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
it("should return 0 for same dates", () => {
|
|
1441
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1442
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1443
|
+
expect(dt1.diff(dt2)).toBe(0);
|
|
1444
|
+
expect(dt1.diff(dt2, "second")).toBe(0);
|
|
1445
|
+
expect(dt1.diff(dt2, "day")).toBe(0);
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
it("should handle fractional results for seconds", () => {
|
|
1449
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1450
|
+
const dt2 = new DateTime("2024-01-15T10:00:00.500Z");
|
|
1451
|
+
expect(dt1.diff(dt2, "second")).toBe(-0.5);
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1454
|
+
it("should handle fractional results for minutes", () => {
|
|
1455
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1456
|
+
const dt2 = new DateTime("2024-01-15T10:00:30.000Z");
|
|
1457
|
+
expect(dt1.diff(dt2, "minute")).toBe(-0.5);
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
it("should handle fractional results for hours", () => {
|
|
1461
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1462
|
+
const dt2 = new DateTime("2024-01-15T10:30:00.000Z");
|
|
1463
|
+
expect(dt1.diff(dt2, "hour")).toBe(-0.5);
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1466
|
+
it("should handle fractional results for days", () => {
|
|
1467
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1468
|
+
const dt2 = new DateTime("2024-01-15T22:00:00.000Z");
|
|
1469
|
+
expect(dt1.diff(dt2, "day")).toBe(-0.5);
|
|
1470
|
+
});
|
|
1471
|
+
|
|
1472
|
+
it("should handle month boundaries correctly", () => {
|
|
1473
|
+
const dt1 = new DateTime("2024-01-31T10:00:00.000Z");
|
|
1474
|
+
const dt2 = new DateTime("2024-02-29T10:00:00.000Z"); // Leap year
|
|
1475
|
+
expect(dt1.diff(dt2, "month")).toBe(-1);
|
|
1476
|
+
});
|
|
1477
|
+
|
|
1478
|
+
it("should handle year boundaries correctly", () => {
|
|
1479
|
+
const dt1 = new DateTime("2023-12-31T10:00:00.000Z");
|
|
1480
|
+
const dt2 = new DateTime("2024-01-01T10:00:00.000Z");
|
|
1481
|
+
expect(dt1.diff(dt2, "year")).toBeCloseTo(0, 2); // Less than 1 year
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
it("should handle leap years in year calculations", () => {
|
|
1485
|
+
const dt1 = new DateTime("2024-01-01T00:00:00.000Z");
|
|
1486
|
+
const dt2 = new DateTime("2025-01-01T00:00:00.000Z");
|
|
1487
|
+
// 366 days in 2024 (leap year)
|
|
1488
|
+
expect(dt1.diff(dt2, "year")).toBeCloseTo(-1, 2);
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
it("should accept DateTime instance", () => {
|
|
1492
|
+
const dt1 = new DateTime("2024-01-15T10:00:00.000Z");
|
|
1493
|
+
const dt2 = new DateTime("2024-01-15T11:00:00.000Z");
|
|
1494
|
+
expect(dt1.diff(dt2, "hour")).toBe(-1);
|
|
1495
|
+
});
|
|
1496
|
+
});
|
|
1497
|
+
});
|
|
1498
|
+
});
|