@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,694 @@
|
|
|
1
|
+
import { InvalidDateError, PluginError } from "../errors";
|
|
2
|
+
import { DateInputSchema } from "../schemas";
|
|
3
|
+
import type { DateInput, DateTimePlugin, TimeUnit } from "../types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Core DateTime class that wraps the native JavaScript Date object
|
|
7
|
+
* Provides immutable operations and extensibility through plugins
|
|
8
|
+
*/
|
|
9
|
+
export class DateTime {
|
|
10
|
+
/**
|
|
11
|
+
* Internal date storage
|
|
12
|
+
* @private
|
|
13
|
+
*/
|
|
14
|
+
private readonly _date: Date;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Static registry of installed plugins
|
|
18
|
+
* @private
|
|
19
|
+
*/
|
|
20
|
+
private static readonly plugins: Map<string, DateTimePlugin> = new Map();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new DateTime instance
|
|
24
|
+
* @param input - Date input (Date, ISO string, timestamp, DateTime, or undefined for current time)
|
|
25
|
+
* @throws {InvalidDateError} When input fails Zod validation
|
|
26
|
+
*/
|
|
27
|
+
constructor(input?: DateInput) {
|
|
28
|
+
// If no input provided, use current time
|
|
29
|
+
if (input === undefined) {
|
|
30
|
+
this._date = new Date();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Special handling for NaN - allow it but create invalid date
|
|
35
|
+
if (typeof input === "number" && Number.isNaN(input)) {
|
|
36
|
+
this._date = new Date(NaN);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Special handling for Invalid Date objects - allow them through
|
|
41
|
+
if (input instanceof Date && Number.isNaN(input.getTime())) {
|
|
42
|
+
this._date = new Date(input.getTime());
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Validate input with Zod schema
|
|
47
|
+
const validation = DateInputSchema.safeParse(input);
|
|
48
|
+
if (!validation.success) {
|
|
49
|
+
throw new InvalidDateError(
|
|
50
|
+
`Invalid date input: ${validation.error.message}`,
|
|
51
|
+
input,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Convert input to Date
|
|
56
|
+
if (input instanceof Date) {
|
|
57
|
+
// Clone the date to ensure immutability
|
|
58
|
+
this._date = new Date(input.getTime());
|
|
59
|
+
} else if (typeof input === "string") {
|
|
60
|
+
// Parse ISO string or other date string
|
|
61
|
+
// This may result in an Invalid Date, which is allowed
|
|
62
|
+
this._date = new Date(input);
|
|
63
|
+
} else if (typeof input === "number") {
|
|
64
|
+
// Use timestamp
|
|
65
|
+
this._date = new Date(input);
|
|
66
|
+
} else if (this.isDateTimeInstance(input)) {
|
|
67
|
+
// Clone from another DateTime instance
|
|
68
|
+
this._date = new Date(input.valueOf());
|
|
69
|
+
} else {
|
|
70
|
+
// Should never reach here due to Zod validation, but TypeScript needs it
|
|
71
|
+
throw new InvalidDateError("Unsupported date input type", input);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Type guard to check if value is a DateTime instance
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
private isDateTimeInstance(val: unknown): val is DateTime {
|
|
80
|
+
return (
|
|
81
|
+
val !== null &&
|
|
82
|
+
typeof val === "object" &&
|
|
83
|
+
"toDate" in val &&
|
|
84
|
+
typeof val.toDate === "function"
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Checks if the date is valid
|
|
90
|
+
* @returns true if the date is valid, false otherwise
|
|
91
|
+
*/
|
|
92
|
+
public isValid(): boolean {
|
|
93
|
+
return !Number.isNaN(this._date.getTime());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Returns the native JavaScript Date object (cloned for immutability)
|
|
98
|
+
* @returns A clone of the internal Date object
|
|
99
|
+
*/
|
|
100
|
+
public toDate(): Date {
|
|
101
|
+
return new Date(this._date.getTime());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Returns ISO 8601 string representation
|
|
106
|
+
* @returns ISO 8601 formatted string
|
|
107
|
+
*/
|
|
108
|
+
public toISO(): string {
|
|
109
|
+
return this._date.toISOString();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Returns the Unix timestamp in milliseconds
|
|
114
|
+
* @returns Unix timestamp
|
|
115
|
+
*/
|
|
116
|
+
public valueOf(): number {
|
|
117
|
+
return this._date.getTime();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ============================================
|
|
121
|
+
// Arithmetic Methods
|
|
122
|
+
// ============================================
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Adds milliseconds to the date
|
|
126
|
+
* @param amount - Number of milliseconds to add (can be negative)
|
|
127
|
+
* @returns New DateTime instance with added milliseconds
|
|
128
|
+
*/
|
|
129
|
+
public addMilliseconds(amount: number): DateTime {
|
|
130
|
+
return new DateTime(this.valueOf() + amount);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Adds seconds to the date
|
|
135
|
+
* @param amount - Number of seconds to add (can be negative)
|
|
136
|
+
* @returns New DateTime instance with added seconds
|
|
137
|
+
*/
|
|
138
|
+
public addSeconds(amount: number): DateTime {
|
|
139
|
+
return this.addMilliseconds(amount * 1000);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Adds minutes to the date
|
|
144
|
+
* @param amount - Number of minutes to add (can be negative)
|
|
145
|
+
* @returns New DateTime instance with added minutes
|
|
146
|
+
*/
|
|
147
|
+
public addMinutes(amount: number): DateTime {
|
|
148
|
+
return this.addMilliseconds(amount * 60 * 1000);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Adds hours to the date
|
|
153
|
+
* @param amount - Number of hours to add (can be negative)
|
|
154
|
+
* @returns New DateTime instance with added hours
|
|
155
|
+
*/
|
|
156
|
+
public addHours(amount: number): DateTime {
|
|
157
|
+
return this.addMilliseconds(amount * 60 * 60 * 1000);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Adds days to the date
|
|
162
|
+
* @param amount - Number of days to add (can be negative)
|
|
163
|
+
* @returns New DateTime instance with added days
|
|
164
|
+
*/
|
|
165
|
+
public addDays(amount: number): DateTime {
|
|
166
|
+
const newDate = this.toDate();
|
|
167
|
+
newDate.setDate(newDate.getDate() + amount);
|
|
168
|
+
return new DateTime(newDate);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Adds weeks to the date
|
|
173
|
+
* @param amount - Number of weeks to add (can be negative)
|
|
174
|
+
* @returns New DateTime instance with added weeks
|
|
175
|
+
*/
|
|
176
|
+
public addWeeks(amount: number): DateTime {
|
|
177
|
+
return this.addDays(amount * 7);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Adds months to the date
|
|
182
|
+
* @param amount - Number of months to add (can be negative)
|
|
183
|
+
* @returns New DateTime instance with added months
|
|
184
|
+
*/
|
|
185
|
+
public addMonths(amount: number): DateTime {
|
|
186
|
+
const newDate = this.toDate();
|
|
187
|
+
newDate.setMonth(newDate.getMonth() + amount);
|
|
188
|
+
return new DateTime(newDate);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Adds years to the date
|
|
193
|
+
* @param amount - Number of years to add (can be negative)
|
|
194
|
+
* @returns New DateTime instance with added years
|
|
195
|
+
*/
|
|
196
|
+
public addYears(amount: number): DateTime {
|
|
197
|
+
const newDate = this.toDate();
|
|
198
|
+
newDate.setFullYear(newDate.getFullYear() + amount);
|
|
199
|
+
return new DateTime(newDate);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Subtracts milliseconds from the date
|
|
204
|
+
* @param amount - Number of milliseconds to subtract (can be negative)
|
|
205
|
+
* @returns New DateTime instance with subtracted milliseconds
|
|
206
|
+
*/
|
|
207
|
+
public subtractMilliseconds(amount: number): DateTime {
|
|
208
|
+
return this.addMilliseconds(-amount);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Subtracts seconds from the date
|
|
213
|
+
* @param amount - Number of seconds to subtract (can be negative)
|
|
214
|
+
* @returns New DateTime instance with subtracted seconds
|
|
215
|
+
*/
|
|
216
|
+
public subtractSeconds(amount: number): DateTime {
|
|
217
|
+
return this.addSeconds(-amount);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Subtracts minutes from the date
|
|
222
|
+
* @param amount - Number of minutes to subtract (can be negative)
|
|
223
|
+
* @returns New DateTime instance with subtracted minutes
|
|
224
|
+
*/
|
|
225
|
+
public subtractMinutes(amount: number): DateTime {
|
|
226
|
+
return this.addMinutes(-amount);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Subtracts hours from the date
|
|
231
|
+
* @param amount - Number of hours to subtract (can be negative)
|
|
232
|
+
* @returns New DateTime instance with subtracted hours
|
|
233
|
+
*/
|
|
234
|
+
public subtractHours(amount: number): DateTime {
|
|
235
|
+
return this.addHours(-amount);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Subtracts days from the date
|
|
240
|
+
* @param amount - Number of days to subtract (can be negative)
|
|
241
|
+
* @returns New DateTime instance with subtracted days
|
|
242
|
+
*/
|
|
243
|
+
public subtractDays(amount: number): DateTime {
|
|
244
|
+
return this.addDays(-amount);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Subtracts weeks from the date
|
|
249
|
+
* @param amount - Number of weeks to subtract (can be negative)
|
|
250
|
+
* @returns New DateTime instance with subtracted weeks
|
|
251
|
+
*/
|
|
252
|
+
public subtractWeeks(amount: number): DateTime {
|
|
253
|
+
return this.addWeeks(-amount);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Subtracts months from the date
|
|
258
|
+
* @param amount - Number of months to subtract (can be negative)
|
|
259
|
+
* @returns New DateTime instance with subtracted months
|
|
260
|
+
*/
|
|
261
|
+
public subtractMonths(amount: number): DateTime {
|
|
262
|
+
return this.addMonths(-amount);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Subtracts years from the date
|
|
267
|
+
* @param amount - Number of years to subtract (can be negative)
|
|
268
|
+
* @returns New DateTime instance with subtracted years
|
|
269
|
+
*/
|
|
270
|
+
public subtractYears(amount: number): DateTime {
|
|
271
|
+
return this.addYears(-amount);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ============================================
|
|
275
|
+
// Comparison Methods
|
|
276
|
+
// ============================================
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Checks if this date is before another date
|
|
280
|
+
* @param other - DateTime instance to compare against
|
|
281
|
+
* @returns true if this date is before the other date, false otherwise
|
|
282
|
+
*/
|
|
283
|
+
public isBefore(other: DateTime): boolean {
|
|
284
|
+
return this.valueOf() < other.valueOf();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Checks if this date is after another date
|
|
289
|
+
* @param other - DateTime instance to compare against
|
|
290
|
+
* @returns true if this date is after the other date, false otherwise
|
|
291
|
+
*/
|
|
292
|
+
public isAfter(other: DateTime): boolean {
|
|
293
|
+
return this.valueOf() > other.valueOf();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Checks if this date is the same as another date
|
|
298
|
+
* @param other - DateTime instance to compare against
|
|
299
|
+
* @returns true if this date is the same as the other date, false otherwise
|
|
300
|
+
*/
|
|
301
|
+
public isSame(other: DateTime): boolean {
|
|
302
|
+
return this.valueOf() === other.valueOf();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Checks if this date is the same as or before another date
|
|
307
|
+
* @param other - DateTime instance to compare against
|
|
308
|
+
* @returns true if this date is the same as or before the other date, false otherwise
|
|
309
|
+
*/
|
|
310
|
+
public isSameOrBefore(other: DateTime): boolean {
|
|
311
|
+
return this.valueOf() <= other.valueOf();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Checks if this date is the same as or after another date
|
|
316
|
+
* @param other - DateTime instance to compare against
|
|
317
|
+
* @returns true if this date is the same as or after the other date, false otherwise
|
|
318
|
+
*/
|
|
319
|
+
public isSameOrAfter(other: DateTime): boolean {
|
|
320
|
+
return this.valueOf() >= other.valueOf();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Checks if this date is between two dates
|
|
325
|
+
* @param start - Start date of the range
|
|
326
|
+
* @param end - End date of the range
|
|
327
|
+
* @param inclusive - Whether to include the start and end dates in the comparison (default: false)
|
|
328
|
+
* @returns true if this date is between start and end, false otherwise
|
|
329
|
+
*/
|
|
330
|
+
public isBetween(
|
|
331
|
+
start: DateTime,
|
|
332
|
+
end: DateTime,
|
|
333
|
+
inclusive = false,
|
|
334
|
+
): boolean {
|
|
335
|
+
const thisTime = this.valueOf();
|
|
336
|
+
const startTime = start.valueOf();
|
|
337
|
+
const endTime = end.valueOf();
|
|
338
|
+
|
|
339
|
+
if (inclusive) {
|
|
340
|
+
return thisTime >= startTime && thisTime <= endTime;
|
|
341
|
+
}
|
|
342
|
+
return thisTime > startTime && thisTime < endTime;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// ============================================
|
|
346
|
+
// Getter Methods
|
|
347
|
+
// ============================================
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Gets the UTC year
|
|
351
|
+
* @returns The year (e.g., 2024)
|
|
352
|
+
*/
|
|
353
|
+
public year(): number {
|
|
354
|
+
return this._date.getUTCFullYear();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Gets the UTC month (0-indexed)
|
|
359
|
+
* @returns The month (0-11, where 0=January)
|
|
360
|
+
*/
|
|
361
|
+
public month(): number {
|
|
362
|
+
return this._date.getUTCMonth();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Gets the UTC day of the month
|
|
367
|
+
* @returns The day of month (1-31)
|
|
368
|
+
*/
|
|
369
|
+
public date(): number {
|
|
370
|
+
return this._date.getUTCDate();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Gets the UTC day of the week (0-indexed)
|
|
375
|
+
* @returns The day of week (0-6, where 0=Sunday)
|
|
376
|
+
*/
|
|
377
|
+
public day(): number {
|
|
378
|
+
return this._date.getUTCDay();
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Gets the UTC hour
|
|
383
|
+
* @returns The hour (0-23)
|
|
384
|
+
*/
|
|
385
|
+
public hour(): number {
|
|
386
|
+
return this._date.getUTCHours();
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Gets the UTC minute
|
|
391
|
+
* @returns The minute (0-59)
|
|
392
|
+
*/
|
|
393
|
+
public minute(): number {
|
|
394
|
+
return this._date.getUTCMinutes();
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Gets the UTC second
|
|
399
|
+
* @returns The second (0-59)
|
|
400
|
+
*/
|
|
401
|
+
public second(): number {
|
|
402
|
+
return this._date.getUTCSeconds();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Gets the UTC millisecond
|
|
407
|
+
* @returns The millisecond (0-999)
|
|
408
|
+
*/
|
|
409
|
+
public millisecond(): number {
|
|
410
|
+
return this._date.getUTCMilliseconds();
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ============================================
|
|
414
|
+
// Setter Methods
|
|
415
|
+
// ============================================
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Sets the UTC year
|
|
419
|
+
* @param year - The year to set
|
|
420
|
+
* @returns New DateTime instance with updated year
|
|
421
|
+
*/
|
|
422
|
+
public setYear(year: number): DateTime {
|
|
423
|
+
const newDate = this.toDate();
|
|
424
|
+
newDate.setUTCFullYear(year);
|
|
425
|
+
return new DateTime(newDate);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Sets the UTC month (0-indexed)
|
|
430
|
+
* @param month - The month to set (0-11, where 0=January)
|
|
431
|
+
* @returns New DateTime instance with updated month
|
|
432
|
+
*/
|
|
433
|
+
public setMonth(month: number): DateTime {
|
|
434
|
+
const newDate = this.toDate();
|
|
435
|
+
newDate.setUTCMonth(month);
|
|
436
|
+
return new DateTime(newDate);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Sets the UTC day of the month
|
|
441
|
+
* @param date - The day to set (1-31)
|
|
442
|
+
* @returns New DateTime instance with updated date
|
|
443
|
+
*/
|
|
444
|
+
public setDate(date: number): DateTime {
|
|
445
|
+
const newDate = this.toDate();
|
|
446
|
+
newDate.setUTCDate(date);
|
|
447
|
+
return new DateTime(newDate);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Sets the UTC hour
|
|
452
|
+
* @param hour - The hour to set (0-23)
|
|
453
|
+
* @returns New DateTime instance with updated hour
|
|
454
|
+
*/
|
|
455
|
+
public setHour(hour: number): DateTime {
|
|
456
|
+
const newDate = this.toDate();
|
|
457
|
+
newDate.setUTCHours(hour);
|
|
458
|
+
return new DateTime(newDate);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Sets the UTC minute
|
|
463
|
+
* @param minute - The minute to set (0-59)
|
|
464
|
+
* @returns New DateTime instance with updated minute
|
|
465
|
+
*/
|
|
466
|
+
public setMinute(minute: number): DateTime {
|
|
467
|
+
const newDate = this.toDate();
|
|
468
|
+
newDate.setUTCMinutes(minute);
|
|
469
|
+
return new DateTime(newDate);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Sets the UTC second
|
|
474
|
+
* @param second - The second to set (0-59)
|
|
475
|
+
* @returns New DateTime instance with updated second
|
|
476
|
+
*/
|
|
477
|
+
public setSecond(second: number): DateTime {
|
|
478
|
+
const newDate = this.toDate();
|
|
479
|
+
newDate.setUTCSeconds(second);
|
|
480
|
+
return new DateTime(newDate);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Sets the UTC millisecond
|
|
485
|
+
* @param millisecond - The millisecond to set (0-999)
|
|
486
|
+
* @returns New DateTime instance with updated millisecond
|
|
487
|
+
*/
|
|
488
|
+
public setMillisecond(millisecond: number): DateTime {
|
|
489
|
+
const newDate = this.toDate();
|
|
490
|
+
newDate.setUTCMilliseconds(millisecond);
|
|
491
|
+
return new DateTime(newDate);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// ============================================
|
|
495
|
+
// Start/End Methods
|
|
496
|
+
// ============================================
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Returns a new DateTime at the start of the day (00:00:00.000)
|
|
500
|
+
* @returns New DateTime instance at start of day
|
|
501
|
+
*/
|
|
502
|
+
public startOfDay(): DateTime {
|
|
503
|
+
const newDate = this.toDate();
|
|
504
|
+
newDate.setUTCHours(0, 0, 0, 0);
|
|
505
|
+
return new DateTime(newDate);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Returns a new DateTime at the end of the day (23:59:59.999)
|
|
510
|
+
* @returns New DateTime instance at end of day
|
|
511
|
+
*/
|
|
512
|
+
public endOfDay(): DateTime {
|
|
513
|
+
const newDate = this.toDate();
|
|
514
|
+
newDate.setUTCHours(23, 59, 59, 999);
|
|
515
|
+
return new DateTime(newDate);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Returns a new DateTime at the start of the hour (XX:00:00.000)
|
|
520
|
+
* @returns New DateTime instance at start of hour
|
|
521
|
+
*/
|
|
522
|
+
public startOfHour(): DateTime {
|
|
523
|
+
const newDate = this.toDate();
|
|
524
|
+
newDate.setUTCMinutes(0, 0, 0);
|
|
525
|
+
return new DateTime(newDate);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Returns a new DateTime at the end of the hour (XX:59:59.999)
|
|
530
|
+
* @returns New DateTime instance at end of hour
|
|
531
|
+
*/
|
|
532
|
+
public endOfHour(): DateTime {
|
|
533
|
+
const newDate = this.toDate();
|
|
534
|
+
newDate.setUTCMinutes(59, 59, 999);
|
|
535
|
+
return new DateTime(newDate);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Returns a new DateTime at the start of the week (Sunday 00:00:00.000)
|
|
540
|
+
* @returns New DateTime instance at start of week
|
|
541
|
+
*/
|
|
542
|
+
public startOfWeek(): DateTime {
|
|
543
|
+
const newDate = this.toDate();
|
|
544
|
+
const day = newDate.getUTCDay();
|
|
545
|
+
newDate.setUTCDate(newDate.getUTCDate() - day);
|
|
546
|
+
newDate.setUTCHours(0, 0, 0, 0);
|
|
547
|
+
return new DateTime(newDate);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Returns a new DateTime at the end of the week (Saturday 23:59:59.999)
|
|
552
|
+
* @returns New DateTime instance at end of week
|
|
553
|
+
*/
|
|
554
|
+
public endOfWeek(): DateTime {
|
|
555
|
+
const newDate = this.toDate();
|
|
556
|
+
const day = newDate.getUTCDay();
|
|
557
|
+
newDate.setUTCDate(newDate.getUTCDate() + (6 - day));
|
|
558
|
+
newDate.setUTCHours(23, 59, 59, 999);
|
|
559
|
+
return new DateTime(newDate);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Returns a new DateTime at the start of the month (day 1, 00:00:00.000)
|
|
564
|
+
* @returns New DateTime instance at start of month
|
|
565
|
+
*/
|
|
566
|
+
public startOfMonth(): DateTime {
|
|
567
|
+
const newDate = this.toDate();
|
|
568
|
+
newDate.setUTCDate(1);
|
|
569
|
+
newDate.setUTCHours(0, 0, 0, 0);
|
|
570
|
+
return new DateTime(newDate);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Returns a new DateTime at the end of the month (last day, 23:59:59.999)
|
|
575
|
+
* @returns New DateTime instance at end of month
|
|
576
|
+
*/
|
|
577
|
+
public endOfMonth(): DateTime {
|
|
578
|
+
const newDate = this.toDate();
|
|
579
|
+
// Set to next month, day 0 (last day of current month)
|
|
580
|
+
newDate.setUTCMonth(newDate.getUTCMonth() + 1, 0);
|
|
581
|
+
newDate.setUTCHours(23, 59, 59, 999);
|
|
582
|
+
return new DateTime(newDate);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Returns a new DateTime at the start of the year (Jan 1, 00:00:00.000)
|
|
587
|
+
* @returns New DateTime instance at start of year
|
|
588
|
+
*/
|
|
589
|
+
public startOfYear(): DateTime {
|
|
590
|
+
const newDate = this.toDate();
|
|
591
|
+
newDate.setUTCMonth(0, 1);
|
|
592
|
+
newDate.setUTCHours(0, 0, 0, 0);
|
|
593
|
+
return new DateTime(newDate);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Returns a new DateTime at the end of the year (Dec 31, 23:59:59.999)
|
|
598
|
+
* @returns New DateTime instance at end of year
|
|
599
|
+
*/
|
|
600
|
+
public endOfYear(): DateTime {
|
|
601
|
+
const newDate = this.toDate();
|
|
602
|
+
newDate.setUTCMonth(11, 31);
|
|
603
|
+
newDate.setUTCHours(23, 59, 59, 999);
|
|
604
|
+
return new DateTime(newDate);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ============================================
|
|
608
|
+
// Difference Method
|
|
609
|
+
// ============================================
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Calculates the difference between this date and another date
|
|
613
|
+
* @param other - DateTime instance to compare against
|
|
614
|
+
* @param unit - Unit of measurement (default: 'millisecond')
|
|
615
|
+
* @returns The difference (can be negative)
|
|
616
|
+
*/
|
|
617
|
+
public diff(other: DateTime, unit: TimeUnit = "millisecond"): number {
|
|
618
|
+
const diff = this.valueOf() - other.valueOf();
|
|
619
|
+
|
|
620
|
+
switch (unit) {
|
|
621
|
+
case "millisecond":
|
|
622
|
+
return diff;
|
|
623
|
+
case "second":
|
|
624
|
+
return diff / 1000;
|
|
625
|
+
case "minute":
|
|
626
|
+
return diff / (1000 * 60);
|
|
627
|
+
case "hour":
|
|
628
|
+
return diff / (1000 * 60 * 60);
|
|
629
|
+
case "day":
|
|
630
|
+
return diff / (1000 * 60 * 60 * 24);
|
|
631
|
+
case "week":
|
|
632
|
+
return diff / (1000 * 60 * 60 * 24 * 7);
|
|
633
|
+
case "month": {
|
|
634
|
+
// For months, we calculate based on the actual month difference
|
|
635
|
+
const thisDate = this.toDate();
|
|
636
|
+
const otherDate = other.toDate();
|
|
637
|
+
const yearDiff =
|
|
638
|
+
thisDate.getUTCFullYear() - otherDate.getUTCFullYear();
|
|
639
|
+
const monthDiff = thisDate.getUTCMonth() - otherDate.getUTCMonth();
|
|
640
|
+
return yearDiff * 12 + monthDiff;
|
|
641
|
+
}
|
|
642
|
+
case "year": {
|
|
643
|
+
// For years, we calculate based on milliseconds
|
|
644
|
+
// Average year length accounting for leap years
|
|
645
|
+
return diff / (1000 * 60 * 60 * 24 * 365.25);
|
|
646
|
+
}
|
|
647
|
+
default:
|
|
648
|
+
return diff;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Registers a plugin to extend DateTime functionality
|
|
654
|
+
* @param plugin - The plugin to register
|
|
655
|
+
* @param options - Optional plugin configuration
|
|
656
|
+
* @throws {PluginError} When plugin with same name already exists
|
|
657
|
+
*/
|
|
658
|
+
public static extend(
|
|
659
|
+
plugin: DateTimePlugin,
|
|
660
|
+
options?: Record<string, unknown>,
|
|
661
|
+
): void {
|
|
662
|
+
// Check if plugin already registered
|
|
663
|
+
if (DateTime.plugins.has(plugin.name)) {
|
|
664
|
+
throw new PluginError(
|
|
665
|
+
`Plugin ${plugin.name} is already registered`,
|
|
666
|
+
plugin.name,
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Register plugin
|
|
671
|
+
DateTime.plugins.set(plugin.name, plugin);
|
|
672
|
+
|
|
673
|
+
// Call plugin install function
|
|
674
|
+
plugin.install(DateTime as any, options);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Checks if a plugin is registered
|
|
679
|
+
* @param name - Plugin name
|
|
680
|
+
* @returns true if plugin is registered, false otherwise
|
|
681
|
+
*/
|
|
682
|
+
public static hasPlugin(name: string): boolean {
|
|
683
|
+
return DateTime.plugins.has(name);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Gets a registered plugin
|
|
688
|
+
* @param name - Plugin name
|
|
689
|
+
* @returns The plugin if found, undefined otherwise
|
|
690
|
+
*/
|
|
691
|
+
public static getPlugin(name: string): DateTimePlugin | undefined {
|
|
692
|
+
return DateTime.plugins.get(name);
|
|
693
|
+
}
|
|
694
|
+
}
|