@bereasoftware/time-guard 2.6.0 → 2.7.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/README.en.md +232 -0
- package/README.md +259 -4
- package/dist/{types/adapters → adapters}/temporal.adapter.d.ts +4 -9
- package/dist/angular/index.cjs +148 -0
- package/dist/angular/index.d.ts +2 -0
- package/dist/angular/index.es.js +143 -0
- package/dist/angular.d.ts +59 -0
- package/dist/calendars/index.cjs +357 -2
- package/dist/calendars/index.es.js +99 -68
- package/dist/chunk-Ddh6569R.cjs +36 -0
- package/dist/core-BAd7GYfw.js +1809 -0
- package/dist/core-CafqOI7W.cjs +1892 -0
- package/dist/core.d.ts +224 -0
- package/dist/index.d.ts +9 -0
- package/dist/locales/african.locale.d.ts +7 -0
- package/dist/locales/index.cjs +7 -2
- package/dist/{types/locales → locales}/index.d.ts +2 -2
- package/dist/locales/index.es.js +3 -2797
- package/dist/locales/south-american.locale.d.ts +7 -0
- package/dist/locales-C2-lOmC8.cjs +3537 -0
- package/dist/locales-CcuSu4P1.js +3514 -0
- package/dist/native/index.cjs +48 -0
- package/dist/native/index.d.ts +2 -0
- package/dist/native/index.es.js +18 -0
- package/dist/native.d.ts +9 -0
- package/dist/plugins/advanced-format.cjs +105 -2
- package/dist/plugins/advanced-format.es.js +63 -45
- package/dist/plugins/duration.cjs +258 -2
- package/dist/plugins/duration.es.js +165 -61
- package/dist/plugins/index.d.ts +10 -0
- package/dist/plugins/relative-time.cjs +166 -2
- package/dist/plugins/relative-time.es.js +74 -37
- package/dist/qwik/index.cjs +104 -0
- package/dist/qwik/index.d.ts +2 -0
- package/dist/qwik/index.es.js +100 -0
- package/dist/qwik.d.ts +32 -0
- package/dist/react/index.cjs +112 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.es.js +103 -0
- package/dist/react.d.ts +46 -0
- package/dist/solid/index.cjs +127 -0
- package/dist/solid/index.d.ts +2 -0
- package/dist/solid/index.es.js +122 -0
- package/dist/solid.d.ts +53 -0
- package/dist/svelte/index.cjs +398 -0
- package/dist/svelte/index.d.ts +2 -0
- package/dist/svelte/index.es.js +393 -0
- package/dist/svelte.d.ts +88 -0
- package/dist/time-guard.cjs +7359 -4
- package/dist/time-guard.d.ts +2 -0
- package/dist/time-guard.es.js +3625 -8073
- package/dist/time-guard.iife.js +13549 -4
- package/dist/time-guard.umd.js +13550 -4
- package/dist/types/index.d.ts +651 -128
- package/dist/vue/index.cjs +143 -0
- package/dist/vue/index.d.ts +2 -0
- package/dist/vue/index.es.js +137 -0
- package/dist/vue.d.ts +35 -0
- package/package.json +111 -17
- package/dist/calendars/index.cjs.map +0 -1
- package/dist/calendars/index.es.js.map +0 -1
- package/dist/locales/index.cjs.map +0 -1
- package/dist/locales/index.es.js.map +0 -1
- package/dist/plugins/advanced-format.cjs.map +0 -1
- package/dist/plugins/advanced-format.es.js.map +0 -1
- package/dist/plugins/duration.cjs.map +0 -1
- package/dist/plugins/duration.es.js.map +0 -1
- package/dist/plugins/relative-time.cjs.map +0 -1
- package/dist/plugins/relative-time.es.js.map +0 -1
- package/dist/time-guard.cjs.map +0 -1
- package/dist/time-guard.es.js.map +0 -1
- package/dist/time-guard.iife.js.map +0 -1
- package/dist/time-guard.umd.js.map +0 -1
- package/dist/types/plugins/index.d.ts +0 -10
- package/dist/types/types/index.d.ts +0 -586
- /package/dist/{types/calendars → calendars}/calendar.manager.d.ts +0 -0
- /package/dist/{types/calendars → calendars}/index.d.ts +0 -0
- /package/dist/{types/formatters → formatters}/date.formatter.d.ts +0 -0
- /package/dist/{types/locales → locales}/additional.locale.d.ts +0 -0
- /package/dist/{types/locales → locales}/asian.locale.d.ts +0 -0
- /package/dist/{types/locales → locales}/english.locale.d.ts +0 -0
- /package/dist/{types/locales → locales}/european.locale.d.ts +0 -0
- /package/dist/{types/locales → locales}/locale.manager.d.ts +0 -0
- /package/dist/{types/locales → locales}/middle-eastern.locale.d.ts +0 -0
- /package/dist/{types/locales → locales}/nordic.locale.d.ts +0 -0
- /package/dist/{types/locales → locales}/romance.locale.d.ts +0 -0
- /package/dist/{types/locales → locales}/slavic.locale.d.ts +0 -0
- /package/dist/{types/locales → locales}/spanish.locale.d.ts +0 -0
- /package/dist/{types/plugins → plugins}/advanced-format/index.d.ts +0 -0
- /package/dist/{types/plugins → plugins}/advanced-format.d.ts +0 -0
- /package/dist/{types/plugins → plugins}/duration/index.d.ts +0 -0
- /package/dist/{types/plugins → plugins}/duration/types.d.ts +0 -0
- /package/dist/{types/plugins → plugins}/duration.d.ts +0 -0
- /package/dist/{types/plugins → plugins}/manager.d.ts +0 -0
- /package/dist/{types/plugins → plugins}/relative-time/index.d.ts +0 -0
- /package/dist/{types/plugins → plugins}/relative-time/types.d.ts +0 -0
- /package/dist/{types/plugins → plugins}/relative-time.d.ts +0 -0
- /package/dist/{types/utils → utils}/duration-locale.d.ts +0 -0
|
@@ -0,0 +1,1892 @@
|
|
|
1
|
+
/*! time-guard v2.7.0 | (c) 2026 Berea-Soft | MIT License | https://github.com/Berea-Soft/time-guard */
|
|
2
|
+
//#region src/locales/locale.manager.ts
|
|
3
|
+
/**
|
|
4
|
+
* English locale data
|
|
5
|
+
*/
|
|
6
|
+
var EN_LOCALE = {
|
|
7
|
+
name: "en",
|
|
8
|
+
months: [
|
|
9
|
+
"January",
|
|
10
|
+
"February",
|
|
11
|
+
"March",
|
|
12
|
+
"April",
|
|
13
|
+
"May",
|
|
14
|
+
"June",
|
|
15
|
+
"July",
|
|
16
|
+
"August",
|
|
17
|
+
"September",
|
|
18
|
+
"October",
|
|
19
|
+
"November",
|
|
20
|
+
"December"
|
|
21
|
+
],
|
|
22
|
+
monthsShort: [
|
|
23
|
+
"Jan",
|
|
24
|
+
"Feb",
|
|
25
|
+
"Mar",
|
|
26
|
+
"Apr",
|
|
27
|
+
"May",
|
|
28
|
+
"Jun",
|
|
29
|
+
"Jul",
|
|
30
|
+
"Aug",
|
|
31
|
+
"Sep",
|
|
32
|
+
"Oct",
|
|
33
|
+
"Nov",
|
|
34
|
+
"Dec"
|
|
35
|
+
],
|
|
36
|
+
weekdays: [
|
|
37
|
+
"Sunday",
|
|
38
|
+
"Monday",
|
|
39
|
+
"Tuesday",
|
|
40
|
+
"Wednesday",
|
|
41
|
+
"Thursday",
|
|
42
|
+
"Friday",
|
|
43
|
+
"Saturday"
|
|
44
|
+
],
|
|
45
|
+
weekdaysShort: [
|
|
46
|
+
"Sun",
|
|
47
|
+
"Mon",
|
|
48
|
+
"Tue",
|
|
49
|
+
"Wed",
|
|
50
|
+
"Thu",
|
|
51
|
+
"Fri",
|
|
52
|
+
"Sat"
|
|
53
|
+
],
|
|
54
|
+
weekdaysMin: [
|
|
55
|
+
"Su",
|
|
56
|
+
"Mo",
|
|
57
|
+
"Tu",
|
|
58
|
+
"We",
|
|
59
|
+
"Th",
|
|
60
|
+
"Fr",
|
|
61
|
+
"Sa"
|
|
62
|
+
],
|
|
63
|
+
meridiem: {
|
|
64
|
+
am: "AM",
|
|
65
|
+
pm: "PM"
|
|
66
|
+
},
|
|
67
|
+
formats: {
|
|
68
|
+
iso: "YYYY-MM-DDTHH:mm:ss.SSSZ",
|
|
69
|
+
date: "YYYY-MM-DD",
|
|
70
|
+
time: "HH:mm:ss",
|
|
71
|
+
datetime: "YYYY-MM-DD HH:mm:ss",
|
|
72
|
+
rfc2822: "ddd, DD MMM YYYY HH:mm:ss Z"
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Spanish locale data (sample for i18n support)
|
|
77
|
+
*/
|
|
78
|
+
var ES_LOCALE = {
|
|
79
|
+
name: "es",
|
|
80
|
+
months: [
|
|
81
|
+
"Enero",
|
|
82
|
+
"Febrero",
|
|
83
|
+
"Marzo",
|
|
84
|
+
"Abril",
|
|
85
|
+
"Mayo",
|
|
86
|
+
"Junio",
|
|
87
|
+
"Julio",
|
|
88
|
+
"Agosto",
|
|
89
|
+
"Septiembre",
|
|
90
|
+
"Octubre",
|
|
91
|
+
"Noviembre",
|
|
92
|
+
"Diciembre"
|
|
93
|
+
],
|
|
94
|
+
monthsShort: [
|
|
95
|
+
"Ene",
|
|
96
|
+
"Feb",
|
|
97
|
+
"Mar",
|
|
98
|
+
"Abr",
|
|
99
|
+
"May",
|
|
100
|
+
"Jun",
|
|
101
|
+
"Jul",
|
|
102
|
+
"Ago",
|
|
103
|
+
"Sep",
|
|
104
|
+
"Oct",
|
|
105
|
+
"Nov",
|
|
106
|
+
"Dic"
|
|
107
|
+
],
|
|
108
|
+
weekdays: [
|
|
109
|
+
"Domingo",
|
|
110
|
+
"Lunes",
|
|
111
|
+
"Martes",
|
|
112
|
+
"Miércoles",
|
|
113
|
+
"Jueves",
|
|
114
|
+
"Viernes",
|
|
115
|
+
"Sábado"
|
|
116
|
+
],
|
|
117
|
+
weekdaysShort: [
|
|
118
|
+
"Dom",
|
|
119
|
+
"Lun",
|
|
120
|
+
"Mar",
|
|
121
|
+
"Mié",
|
|
122
|
+
"Jue",
|
|
123
|
+
"Vie",
|
|
124
|
+
"Sáb"
|
|
125
|
+
],
|
|
126
|
+
weekdaysMin: [
|
|
127
|
+
"Do",
|
|
128
|
+
"Lu",
|
|
129
|
+
"Ma",
|
|
130
|
+
"Mi",
|
|
131
|
+
"Ju",
|
|
132
|
+
"Vi",
|
|
133
|
+
"Sa"
|
|
134
|
+
],
|
|
135
|
+
meridiem: {
|
|
136
|
+
am: "AM",
|
|
137
|
+
pm: "PM"
|
|
138
|
+
},
|
|
139
|
+
formats: {
|
|
140
|
+
iso: "YYYY-MM-DDTHH:mm:ss.SSSZ",
|
|
141
|
+
date: "DD/MM/YYYY",
|
|
142
|
+
time: "HH:mm:ss",
|
|
143
|
+
datetime: "DD/MM/YYYY HH:mm:ss",
|
|
144
|
+
rfc2822: "ddd, DD MMM YYYY HH:mm:ss Z"
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Locale Manager - implements ILocaleManager
|
|
149
|
+
*/
|
|
150
|
+
var LocaleManager = class LocaleManager {
|
|
151
|
+
static instance;
|
|
152
|
+
locales = /* @__PURE__ */ new Map();
|
|
153
|
+
currentLocale = "en";
|
|
154
|
+
/**
|
|
155
|
+
* Singleton pattern
|
|
156
|
+
*/
|
|
157
|
+
static getInstance() {
|
|
158
|
+
if (!LocaleManager.instance) LocaleManager.instance = new LocaleManager();
|
|
159
|
+
return LocaleManager.instance;
|
|
160
|
+
}
|
|
161
|
+
constructor() {
|
|
162
|
+
this.locales.set("en", EN_LOCALE);
|
|
163
|
+
this.locales.set("es", ES_LOCALE);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Set or register a locale
|
|
167
|
+
*/
|
|
168
|
+
setLocale(locale, data) {
|
|
169
|
+
if (data) this.locales.set(locale.toLowerCase(), data);
|
|
170
|
+
this.currentLocale = locale.toLowerCase();
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get locale information
|
|
174
|
+
*/
|
|
175
|
+
getLocale(locale) {
|
|
176
|
+
const target = (locale || this.currentLocale).toLowerCase();
|
|
177
|
+
const result = this.locales.get(target);
|
|
178
|
+
if (!result) return this.locales.get("en") || EN_LOCALE;
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* List all registered locales
|
|
183
|
+
*/
|
|
184
|
+
listLocales() {
|
|
185
|
+
return Array.from(this.locales.keys());
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Get current locale
|
|
189
|
+
*/
|
|
190
|
+
getCurrentLocale() {
|
|
191
|
+
return this.currentLocale;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Load multiple locales
|
|
195
|
+
*/
|
|
196
|
+
loadLocales(locales) {
|
|
197
|
+
Object.entries(locales).forEach(([name, data]) => {
|
|
198
|
+
this.locales.set(name.toLowerCase(), data);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/adapters/temporal.adapter.ts
|
|
204
|
+
var temporalCache = null;
|
|
205
|
+
/**
|
|
206
|
+
* Get Temporal from globalThis or imported module
|
|
207
|
+
*/
|
|
208
|
+
function useTemporal() {
|
|
209
|
+
if (temporalCache) return temporalCache;
|
|
210
|
+
const TemporalLoaded = globalThis.Temporal;
|
|
211
|
+
if (!TemporalLoaded) throw new Error("Temporal API not loaded. Make sure @js-temporal/polyfill is imported in your app.");
|
|
212
|
+
temporalCache = TemporalLoaded;
|
|
213
|
+
return TemporalLoaded;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Adapter for Temporal date/time operations
|
|
217
|
+
*/
|
|
218
|
+
var TemporalAdapter = class {
|
|
219
|
+
/**
|
|
220
|
+
* Parse various input formats to Temporal.PlainDateTime
|
|
221
|
+
*/
|
|
222
|
+
static parseToPlainDateTime(input) {
|
|
223
|
+
const Temporal = useTemporal();
|
|
224
|
+
if (this.isPlainDateTime(input)) return input;
|
|
225
|
+
if (this.isZonedDateTime(input)) return input.toPlainDateTime();
|
|
226
|
+
if (this.isPlainDate(input)) return input.toPlainDateTime({
|
|
227
|
+
hour: 0,
|
|
228
|
+
minute: 0,
|
|
229
|
+
second: 0,
|
|
230
|
+
millisecond: 0
|
|
231
|
+
});
|
|
232
|
+
if (this.isPlainTime(input)) return Temporal.Now.plainDateTimeISO().with(input);
|
|
233
|
+
if (input !== null && typeof input === "object" && "getTime" in input && typeof input.getTime === "function") return this.fromDate(input);
|
|
234
|
+
if (typeof input === "number") return this.fromUnix(input);
|
|
235
|
+
if (typeof input === "string") return this.parseISOString(input);
|
|
236
|
+
if (typeof input === "object" && input !== null) return this.fromObject(input);
|
|
237
|
+
return Temporal.Now.plainDateTimeISO();
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Convert JavaScript Date to Temporal.PlainDateTime
|
|
241
|
+
*/
|
|
242
|
+
static fromDate(date) {
|
|
243
|
+
const Temporal = useTemporal();
|
|
244
|
+
const [datePart, timePart] = date.toISOString().split("T");
|
|
245
|
+
return Temporal.PlainDateTime.from(datePart + "T" + timePart.slice(0, -1));
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Convert Unix timestamp (milliseconds) to Temporal.PlainDateTime
|
|
249
|
+
*/
|
|
250
|
+
static fromUnix(timestamp) {
|
|
251
|
+
return this.fromDate(new Date(timestamp));
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Parse ISO string to Temporal.PlainDateTime
|
|
255
|
+
*/
|
|
256
|
+
static parseISOString(iso) {
|
|
257
|
+
const Temporal = useTemporal();
|
|
258
|
+
try {
|
|
259
|
+
if (iso.includes("T") || / \d{2}:\d{2}/.test(iso)) {
|
|
260
|
+
const normalized = iso.replace(" ", "T");
|
|
261
|
+
return Temporal.PlainDateTime.from(normalized);
|
|
262
|
+
}
|
|
263
|
+
return Temporal.PlainDate.from(iso).toPlainDateTime({ hour: 0 });
|
|
264
|
+
} catch {
|
|
265
|
+
return Temporal.Now.plainDateTimeISO();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Create from object with date components
|
|
270
|
+
*/
|
|
271
|
+
static fromObject(obj) {
|
|
272
|
+
const Temporal = useTemporal();
|
|
273
|
+
const year = obj.year || Temporal.Now.plainDateISO().year;
|
|
274
|
+
const month = obj.month || 1;
|
|
275
|
+
const day = obj.day || 1;
|
|
276
|
+
const hour = obj.hour || 0;
|
|
277
|
+
const minute = obj.minute || 0;
|
|
278
|
+
const second = obj.second || 0;
|
|
279
|
+
const millisecond = obj.millisecond || 0;
|
|
280
|
+
return Temporal.PlainDateTime.from({
|
|
281
|
+
year: this.toFiniteInteger(year),
|
|
282
|
+
month: this.toFiniteInteger(month),
|
|
283
|
+
day: this.toFiniteInteger(day),
|
|
284
|
+
hour: this.toFiniteInteger(hour),
|
|
285
|
+
minute: this.toFiniteInteger(minute),
|
|
286
|
+
second: this.toFiniteInteger(second),
|
|
287
|
+
millisecond: this.toFiniteInteger(millisecond)
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Convert Temporal.PlainDateTime to JavaScript Date
|
|
292
|
+
*/
|
|
293
|
+
static toDate(temporal) {
|
|
294
|
+
const plainDT = this.toPlainDateTime(temporal);
|
|
295
|
+
return new Date(plainDT.toString());
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Convert to Unix timestamp (milliseconds)
|
|
299
|
+
*/
|
|
300
|
+
static toUnix(temporal) {
|
|
301
|
+
const plainDT = this.toPlainDateTime(temporal);
|
|
302
|
+
return Date.UTC(plainDT.year, plainDT.month - 1, plainDT.day, plainDT.hour, plainDT.minute, plainDT.second, plainDT.millisecond);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Convert to ISO string
|
|
306
|
+
*/
|
|
307
|
+
static toISOString(temporal) {
|
|
308
|
+
return this.toPlainDateTime(temporal).toString() + "Z";
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Ensure we have a PlainDateTime
|
|
312
|
+
*/
|
|
313
|
+
static toPlainDateTime(temporal) {
|
|
314
|
+
if (this.isPlainDateTime(temporal)) return temporal;
|
|
315
|
+
return temporal.toPlainDateTime();
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Type guards
|
|
319
|
+
*/
|
|
320
|
+
static isPlainDateTime(obj) {
|
|
321
|
+
return obj !== null && typeof obj === "object" && "year" in obj && "month" in obj && "day" in obj && "hour" in obj;
|
|
322
|
+
}
|
|
323
|
+
static isZonedDateTime(obj) {
|
|
324
|
+
return obj !== null && typeof obj === "object" && "timeZone" in obj;
|
|
325
|
+
}
|
|
326
|
+
static isPlainDate(obj) {
|
|
327
|
+
return obj !== null && typeof obj === "object" && "year" in obj && "month" in obj && "day" in obj && !("hour" in obj) && "toPlainDateTime" in obj;
|
|
328
|
+
}
|
|
329
|
+
static isPlainTime(obj) {
|
|
330
|
+
return obj !== null && typeof obj === "object" && "hour" in obj && !("year" in obj);
|
|
331
|
+
}
|
|
332
|
+
static toFiniteInteger(value) {
|
|
333
|
+
const num = Number(value);
|
|
334
|
+
if (!Number.isFinite(num)) throw new Error(`Temporal error: Expected finite integer, got ${value}`);
|
|
335
|
+
return Math.trunc(num);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Validate that a PlainDateTime has finite integer components
|
|
339
|
+
*/
|
|
340
|
+
static validatePlainDateTime(dt) {
|
|
341
|
+
for (const field of [
|
|
342
|
+
"year",
|
|
343
|
+
"month",
|
|
344
|
+
"day",
|
|
345
|
+
"hour",
|
|
346
|
+
"minute",
|
|
347
|
+
"second",
|
|
348
|
+
"millisecond"
|
|
349
|
+
]) {
|
|
350
|
+
const value = dt[field];
|
|
351
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value)) {
|
|
352
|
+
if (typeof console !== "undefined" && typeof console.error === "function") console.error("TemporalAdapter.validatePlainDateTime: campo inválido", field, value, dt);
|
|
353
|
+
throw new Error(`Temporal error: Expected finite integer for ${field}, got ${value}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Get current time as PlainDateTime
|
|
359
|
+
*/
|
|
360
|
+
static now() {
|
|
361
|
+
return useTemporal().Now.plainDateTimeISO();
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Get current time as ZonedDateTime with timezone
|
|
365
|
+
*/
|
|
366
|
+
static nowInTimezone(timezone) {
|
|
367
|
+
return useTemporal().Now.zonedDateTimeISO(timezone);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Compare two Temporal.PlainDateTime objects
|
|
371
|
+
* Returns: -1 if dt1 < dt2, 0 if equal, 1 if dt1 > dt2
|
|
372
|
+
* Uses ISO string comparison as fallback for polyfills that don't have Temporal.PlainDateTime.compare
|
|
373
|
+
*/
|
|
374
|
+
static compare(dt1, dt2) {
|
|
375
|
+
const Temporal = useTemporal();
|
|
376
|
+
this.validatePlainDateTime(dt1);
|
|
377
|
+
this.validatePlainDateTime(dt2);
|
|
378
|
+
if (Temporal.PlainDateTime && typeof Temporal.PlainDateTime.compare === "function") return Temporal.PlainDateTime.compare(dt1, dt2);
|
|
379
|
+
const iso1 = dt1.toString();
|
|
380
|
+
const iso2 = dt2.toString();
|
|
381
|
+
if (iso1 < iso2) return -1;
|
|
382
|
+
if (iso1 > iso2) return 1;
|
|
383
|
+
return 0;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/formatters/date.formatter.ts
|
|
388
|
+
/**
|
|
389
|
+
* Date Formatter implementation
|
|
390
|
+
*/
|
|
391
|
+
var DateFormatter = class {
|
|
392
|
+
localeManager;
|
|
393
|
+
constructor() {
|
|
394
|
+
this.localeManager = LocaleManager.getInstance();
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Format date with pattern and locale
|
|
398
|
+
*/
|
|
399
|
+
format(date, pattern, locale) {
|
|
400
|
+
const localeData = this.localeManager.getLocale(locale);
|
|
401
|
+
const escapedParts = [];
|
|
402
|
+
let escapedPattern = pattern.replace(/\[([^\]]+)\]/g, (_match, content) => {
|
|
403
|
+
escapedParts.push(content);
|
|
404
|
+
return `~${escapedParts.length - 1}~`;
|
|
405
|
+
});
|
|
406
|
+
escapedPattern = escapedPattern.replace(/"([^"]*)"/g, (_match, content) => {
|
|
407
|
+
escapedParts.push(content);
|
|
408
|
+
return `~${escapedParts.length - 1}~`;
|
|
409
|
+
});
|
|
410
|
+
let result = escapedPattern.replace(/Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|SSS/g, (match) => {
|
|
411
|
+
switch (match) {
|
|
412
|
+
case "YYYY": return String(date.year).padStart(4, "0");
|
|
413
|
+
case "YY": return String(date.year).slice(-2);
|
|
414
|
+
case "Y": return String(date.year);
|
|
415
|
+
case "MMMM": return localeData.months[date.month - 1];
|
|
416
|
+
case "MMM": return localeData.monthsShort[date.month - 1];
|
|
417
|
+
case "MM": return String(date.month).padStart(2, "0");
|
|
418
|
+
case "M": return String(date.month);
|
|
419
|
+
case "DD": return String(date.day).padStart(2, "0");
|
|
420
|
+
case "D": return String(date.day);
|
|
421
|
+
case "dddd": return localeData.weekdays[date.dayOfWeek % 7];
|
|
422
|
+
case "ddd": return localeData.weekdaysShort[date.dayOfWeek % 7];
|
|
423
|
+
case "dd": return localeData.weekdaysMin[date.dayOfWeek % 7];
|
|
424
|
+
case "d": return String(date.dayOfWeek);
|
|
425
|
+
case "HH": return String(date.hour).padStart(2, "0");
|
|
426
|
+
case "H": return String(date.hour);
|
|
427
|
+
case "hh": return String(date.hour % 12 || 12).padStart(2, "0");
|
|
428
|
+
case "h": return String(date.hour % 12 || 12);
|
|
429
|
+
case "mm": return String(date.minute).padStart(2, "0");
|
|
430
|
+
case "m": return String(date.minute);
|
|
431
|
+
case "ss": return String(date.second).padStart(2, "0");
|
|
432
|
+
case "s": return String(date.second);
|
|
433
|
+
case "SSS": return String(date.millisecond).padStart(3, "0");
|
|
434
|
+
case "a":
|
|
435
|
+
case "A":
|
|
436
|
+
const meridiem = localeData.meridiem || {
|
|
437
|
+
am: "am",
|
|
438
|
+
pm: "pm"
|
|
439
|
+
};
|
|
440
|
+
const value = date.hour >= 12 ? meridiem.pm : meridiem.am;
|
|
441
|
+
return match === "a" ? value.toLowerCase() : value.toUpperCase();
|
|
442
|
+
default: return match;
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
escapedParts.forEach((content, index) => {
|
|
446
|
+
result = result.replace(`~${index}~`, content);
|
|
447
|
+
});
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Get format pattern for preset
|
|
452
|
+
*/
|
|
453
|
+
getPreset(preset) {
|
|
454
|
+
const presets = {
|
|
455
|
+
iso: "YYYY-MM-DDTHH:mm:ss.SSSZ",
|
|
456
|
+
date: "YYYY-MM-DD",
|
|
457
|
+
time: "HH:mm:ss",
|
|
458
|
+
datetime: "YYYY-MM-DD HH:mm:ss",
|
|
459
|
+
rfc2822: "ddd, DD MMM YYYY HH:mm:ss Z",
|
|
460
|
+
rfc3339: "YYYY-MM-DDTHH:mm:ssZ",
|
|
461
|
+
utc: "YYYY-MM-DDTHH:mm:ss[Z]"
|
|
462
|
+
};
|
|
463
|
+
return presets[preset] || presets.iso;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Format with preset
|
|
467
|
+
*/
|
|
468
|
+
formatPreset(date, preset, locale) {
|
|
469
|
+
const pattern = this.getPreset(preset);
|
|
470
|
+
return this.format(date, pattern, locale);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
//#endregion
|
|
474
|
+
//#region src/utils/duration-locale.ts
|
|
475
|
+
/**
|
|
476
|
+
* Shared locale utilities for duration formatting
|
|
477
|
+
* Extracted from DiffResult and DurationResult to avoid duplication
|
|
478
|
+
*/
|
|
479
|
+
/**
|
|
480
|
+
* Duration unit labels for different locales
|
|
481
|
+
* Format: [singular, plural]
|
|
482
|
+
*/
|
|
483
|
+
var DURATION_UNIT_LABELS = {
|
|
484
|
+
en: {
|
|
485
|
+
year: ["year", "years"],
|
|
486
|
+
month: ["month", "months"],
|
|
487
|
+
week: ["week", "weeks"],
|
|
488
|
+
day: ["day", "days"],
|
|
489
|
+
hour: ["hour", "hours"],
|
|
490
|
+
minute: ["minute", "minutes"],
|
|
491
|
+
second: ["second", "seconds"],
|
|
492
|
+
millisecond: ["millisecond", "milliseconds"]
|
|
493
|
+
},
|
|
494
|
+
es: {
|
|
495
|
+
year: ["año", "años"],
|
|
496
|
+
month: ["mes", "meses"],
|
|
497
|
+
week: ["semana", "semanas"],
|
|
498
|
+
day: ["día", "días"],
|
|
499
|
+
hour: ["hora", "horas"],
|
|
500
|
+
minute: ["minuto", "minutos"],
|
|
501
|
+
second: ["segundo", "segundos"],
|
|
502
|
+
millisecond: ["milisegundo", "milisegundos"]
|
|
503
|
+
},
|
|
504
|
+
fr: {
|
|
505
|
+
year: ["année", "années"],
|
|
506
|
+
month: ["mois", "mois"],
|
|
507
|
+
week: ["semaine", "semaines"],
|
|
508
|
+
day: ["jour", "jours"],
|
|
509
|
+
hour: ["heure", "heures"],
|
|
510
|
+
minute: ["minute", "minutes"],
|
|
511
|
+
second: ["seconde", "secondes"],
|
|
512
|
+
millisecond: ["milliseconde", "millisecondes"]
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
/**
|
|
516
|
+
* Conjunction words for joining duration parts
|
|
517
|
+
*/
|
|
518
|
+
var CONJUNCTION_LABELS = {
|
|
519
|
+
en: "and",
|
|
520
|
+
es: "y",
|
|
521
|
+
fr: "et",
|
|
522
|
+
de: "und",
|
|
523
|
+
it: "e",
|
|
524
|
+
pt: "e"
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* Get duration unit label for a specific locale
|
|
528
|
+
*/
|
|
529
|
+
function getDurationUnitLabel(unit, locale, value) {
|
|
530
|
+
return ((DURATION_UNIT_LABELS[locale] || DURATION_UNIT_LABELS.en)[unit] || [unit, unit + "s"])[value === 1 ? 0 : 1];
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Get conjunction word for a specific locale
|
|
534
|
+
*/
|
|
535
|
+
function getConjunctionLabel(locale) {
|
|
536
|
+
return CONJUNCTION_LABELS[locale] || "and";
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Join formatted duration parts with locale-appropriate conjunctions
|
|
540
|
+
*/
|
|
541
|
+
function joinDurationParts(parts, locale) {
|
|
542
|
+
if (parts.length === 0) return "";
|
|
543
|
+
if (parts.length === 1) return parts[0];
|
|
544
|
+
const conjunction = getConjunctionLabel(locale);
|
|
545
|
+
if (parts.length === 2) return `${parts[0]} ${conjunction} ${parts[1]}`;
|
|
546
|
+
return parts.slice(0, -1).join(", ") + ` ${conjunction} ${parts[parts.length - 1]}`;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Format a zero duration for display
|
|
550
|
+
*/
|
|
551
|
+
function formatZeroDuration(locale) {
|
|
552
|
+
return {
|
|
553
|
+
en: "0 seconds",
|
|
554
|
+
es: "0 segundos",
|
|
555
|
+
fr: "0 secondes"
|
|
556
|
+
}[locale] || "0 seconds";
|
|
557
|
+
}
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region src/calendars/calendar.manager.ts
|
|
560
|
+
/**
|
|
561
|
+
* Gregorian Calendar System (ISO 8601 default)
|
|
562
|
+
*/
|
|
563
|
+
var GregorianCalendar = class {
|
|
564
|
+
id = "gregory";
|
|
565
|
+
name = "Gregorian Calendar";
|
|
566
|
+
locale = "en";
|
|
567
|
+
monthNames = [
|
|
568
|
+
"January",
|
|
569
|
+
"February",
|
|
570
|
+
"March",
|
|
571
|
+
"April",
|
|
572
|
+
"May",
|
|
573
|
+
"June",
|
|
574
|
+
"July",
|
|
575
|
+
"August",
|
|
576
|
+
"September",
|
|
577
|
+
"October",
|
|
578
|
+
"November",
|
|
579
|
+
"December"
|
|
580
|
+
];
|
|
581
|
+
monthNamesShort = [
|
|
582
|
+
"Jan",
|
|
583
|
+
"Feb",
|
|
584
|
+
"Mar",
|
|
585
|
+
"Apr",
|
|
586
|
+
"May",
|
|
587
|
+
"Jun",
|
|
588
|
+
"Jul",
|
|
589
|
+
"Aug",
|
|
590
|
+
"Sep",
|
|
591
|
+
"Oct",
|
|
592
|
+
"Nov",
|
|
593
|
+
"Dec"
|
|
594
|
+
];
|
|
595
|
+
weekdayNames = [
|
|
596
|
+
"Sunday",
|
|
597
|
+
"Monday",
|
|
598
|
+
"Tuesday",
|
|
599
|
+
"Wednesday",
|
|
600
|
+
"Thursday",
|
|
601
|
+
"Friday",
|
|
602
|
+
"Saturday"
|
|
603
|
+
];
|
|
604
|
+
weekdayNamesShort = [
|
|
605
|
+
"Sun",
|
|
606
|
+
"Mon",
|
|
607
|
+
"Tue",
|
|
608
|
+
"Wed",
|
|
609
|
+
"Thu",
|
|
610
|
+
"Fri",
|
|
611
|
+
"Sat"
|
|
612
|
+
];
|
|
613
|
+
getMonthName(month, short = false) {
|
|
614
|
+
return (short ? this.monthNamesShort : this.monthNames)[Math.max(0, Math.min(11, month - 1))];
|
|
615
|
+
}
|
|
616
|
+
getWeekdayName(day, short = false) {
|
|
617
|
+
return (short ? this.weekdayNamesShort : this.weekdayNames)[Math.max(0, Math.min(6, day - 1))];
|
|
618
|
+
}
|
|
619
|
+
isLeapYear(year) {
|
|
620
|
+
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
621
|
+
}
|
|
622
|
+
daysInMonth(year, month) {
|
|
623
|
+
const daysPerMonth = [
|
|
624
|
+
31,
|
|
625
|
+
28,
|
|
626
|
+
31,
|
|
627
|
+
30,
|
|
628
|
+
31,
|
|
629
|
+
30,
|
|
630
|
+
31,
|
|
631
|
+
31,
|
|
632
|
+
30,
|
|
633
|
+
31,
|
|
634
|
+
30,
|
|
635
|
+
31
|
|
636
|
+
];
|
|
637
|
+
if (month === 2 && this.isLeapYear(year)) return 29;
|
|
638
|
+
return daysPerMonth[Math.max(0, Math.min(11, month - 1))];
|
|
639
|
+
}
|
|
640
|
+
daysInYear(year) {
|
|
641
|
+
return this.isLeapYear(year) ? 366 : 365;
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
/**
|
|
645
|
+
* Calendar Manager - Singleton for managing calendar systems
|
|
646
|
+
*/
|
|
647
|
+
var CalendarManager = class CalendarManager {
|
|
648
|
+
static instance;
|
|
649
|
+
calendars = /* @__PURE__ */ new Map();
|
|
650
|
+
defaultCalendar = "gregory";
|
|
651
|
+
constructor() {
|
|
652
|
+
this.register(new GregorianCalendar());
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Get singleton instance
|
|
656
|
+
*/
|
|
657
|
+
static getInstance() {
|
|
658
|
+
if (!CalendarManager.instance) CalendarManager.instance = new CalendarManager();
|
|
659
|
+
return CalendarManager.instance;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Register a new calendar system
|
|
663
|
+
*/
|
|
664
|
+
register(calendar) {
|
|
665
|
+
this.calendars.set(calendar.id, calendar);
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Get calendar by ID
|
|
669
|
+
*/
|
|
670
|
+
get(id) {
|
|
671
|
+
return this.calendars.get(id);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* List all available calendars
|
|
675
|
+
*/
|
|
676
|
+
list() {
|
|
677
|
+
return Array.from(this.calendars.keys());
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Set default calendar
|
|
681
|
+
*/
|
|
682
|
+
setDefault(id) {
|
|
683
|
+
if (this.calendars.has(id)) this.defaultCalendar = id;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Get default calendar
|
|
687
|
+
*/
|
|
688
|
+
getDefault() {
|
|
689
|
+
const calendar = this.calendars.get(this.defaultCalendar);
|
|
690
|
+
if (!calendar) throw new Error(`Default calendar '${this.defaultCalendar}' not found`);
|
|
691
|
+
return calendar;
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
var calendarManager = CalendarManager.getInstance();
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region src/plugins/manager.ts
|
|
697
|
+
/**
|
|
698
|
+
* Plugin Manager - handles plugin registration and initialization
|
|
699
|
+
* Uses Singleton pattern to ensure single instance across application
|
|
700
|
+
*/
|
|
701
|
+
var PluginManager = class PluginManager {
|
|
702
|
+
static instance;
|
|
703
|
+
plugins = /* @__PURE__ */ new Map();
|
|
704
|
+
/**
|
|
705
|
+
* Get singleton instance
|
|
706
|
+
*/
|
|
707
|
+
static getInstance() {
|
|
708
|
+
if (!PluginManager.instance) PluginManager.instance = new PluginManager();
|
|
709
|
+
return PluginManager.instance;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Register a plugin
|
|
713
|
+
* @param plugin - The plugin to register
|
|
714
|
+
* @param timeGuardClass - Reference to TimeGuard class
|
|
715
|
+
* @param config - Optional plugin configuration
|
|
716
|
+
*/
|
|
717
|
+
static use(plugin, timeGuardClass, config) {
|
|
718
|
+
PluginManager.getInstance().register(plugin, timeGuardClass, config);
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Register multiple plugins at once
|
|
722
|
+
* @param plugins - Array of plugins to register
|
|
723
|
+
* @param timeGuardClass - Reference to TimeGuard class
|
|
724
|
+
* @param config - Optional plugin configuration
|
|
725
|
+
*/
|
|
726
|
+
static useMultiple(plugins, timeGuardClass, config) {
|
|
727
|
+
const manager = PluginManager.getInstance();
|
|
728
|
+
plugins.forEach((plugin) => manager.register(plugin, timeGuardClass, config));
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Get registered plugin by name
|
|
732
|
+
* @param name - Plugin name
|
|
733
|
+
* @returns Plugin instance or undefined
|
|
734
|
+
*/
|
|
735
|
+
static getPlugin(name) {
|
|
736
|
+
return PluginManager.getInstance().plugins.get(name);
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Check if plugin is registered
|
|
740
|
+
* @param name - Plugin name
|
|
741
|
+
* @returns True if plugin is registered
|
|
742
|
+
*/
|
|
743
|
+
static hasPlugin(name) {
|
|
744
|
+
return PluginManager.getInstance().plugins.has(name);
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Get all registered plugins
|
|
748
|
+
* @returns Array of registered plugin names
|
|
749
|
+
*/
|
|
750
|
+
static listPlugins() {
|
|
751
|
+
const manager = PluginManager.getInstance();
|
|
752
|
+
return Array.from(manager.plugins.keys());
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Unregister a plugin
|
|
756
|
+
* @param name - Plugin name
|
|
757
|
+
*/
|
|
758
|
+
static unuse(name) {
|
|
759
|
+
return PluginManager.getInstance().plugins.delete(name);
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Clear all plugins
|
|
763
|
+
*/
|
|
764
|
+
static clear() {
|
|
765
|
+
PluginManager.getInstance().plugins.clear();
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Internal register method
|
|
769
|
+
*/
|
|
770
|
+
register(plugin, timeGuardClass, config) {
|
|
771
|
+
if (this.plugins.has(plugin.name)) {
|
|
772
|
+
console.warn(`Plugin "${plugin.name}" is already registered. Skipping...`);
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
try {
|
|
776
|
+
plugin.install(timeGuardClass, config);
|
|
777
|
+
this.plugins.set(plugin.name, plugin);
|
|
778
|
+
if (process.env.NODE_ENV !== "production") console.warn(`Plugin "${plugin.name}" v${plugin.version} registered successfully`);
|
|
779
|
+
} catch (error) {
|
|
780
|
+
console.error(`Failed to register plugin "${plugin.name}":`, error);
|
|
781
|
+
throw new Error(`Failed to register plugin "${plugin.name}": ${error instanceof Error ? error.message : String(error)}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
//#endregion
|
|
786
|
+
//#region src/core.ts
|
|
787
|
+
/**
|
|
788
|
+
* TimeGuard - Modern date/time library using Temporal API
|
|
789
|
+
* Core Implementation (zero-dependency, polyfill-erased type-space)
|
|
790
|
+
*
|
|
791
|
+
* @author Berea-Soft
|
|
792
|
+
* @license MIT
|
|
793
|
+
*/
|
|
794
|
+
var MS_PER_SECOND = 1e3;
|
|
795
|
+
var MS_PER_MINUTE = MS_PER_SECOND * 60;
|
|
796
|
+
var MS_PER_HOUR = MS_PER_MINUTE * 60;
|
|
797
|
+
var MS_PER_DAY = MS_PER_HOUR * 24;
|
|
798
|
+
var MS_PER_WEEK = MS_PER_DAY * 7;
|
|
799
|
+
var DAYS_PER_YEAR = 365.25;
|
|
800
|
+
var MS_PER_MONTH = DAYS_PER_YEAR / 12 * MS_PER_DAY;
|
|
801
|
+
var MS_PER_YEAR = DAYS_PER_YEAR * MS_PER_DAY;
|
|
802
|
+
var ZERO_DIFF_DAYS = " days";
|
|
803
|
+
/**
|
|
804
|
+
* Calculate total milliseconds from duration parts using hoisted constants
|
|
805
|
+
*/
|
|
806
|
+
function calculateTotalMs(duration) {
|
|
807
|
+
return (duration.years || 0) * MS_PER_YEAR + (duration.months || 0) * MS_PER_MONTH + (duration.weeks || 0) * MS_PER_WEEK + (duration.days || 0) * MS_PER_DAY + (duration.hours || 0) * MS_PER_HOUR + (duration.minutes || 0) * MS_PER_MINUTE + (duration.seconds || 0) * MS_PER_SECOND + (duration.milliseconds || 0);
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Diff result object that allows chaining with .as()
|
|
811
|
+
*
|
|
812
|
+
* Supports two modes:
|
|
813
|
+
* - 'exact': Returns precise time differences
|
|
814
|
+
* - 'calendar': Returns calendar-aware breakdown
|
|
815
|
+
*/
|
|
816
|
+
var DiffResult = class {
|
|
817
|
+
_value;
|
|
818
|
+
_tg1;
|
|
819
|
+
_tg2;
|
|
820
|
+
_mode;
|
|
821
|
+
_breakdownData;
|
|
822
|
+
_locale;
|
|
823
|
+
constructor(value, tg1, tg2, mode = "exact", breakdownData, locale) {
|
|
824
|
+
this._value = value;
|
|
825
|
+
this._tg1 = tg1;
|
|
826
|
+
this._tg2 = tg2;
|
|
827
|
+
this._mode = mode;
|
|
828
|
+
this._breakdownData = breakdownData || null;
|
|
829
|
+
this._locale = locale || "en";
|
|
830
|
+
}
|
|
831
|
+
as(unit) {
|
|
832
|
+
return this._tg1.diff(this._tg2, unit);
|
|
833
|
+
}
|
|
834
|
+
breakdown() {
|
|
835
|
+
return this._breakdownData;
|
|
836
|
+
}
|
|
837
|
+
format(locale) {
|
|
838
|
+
const l = locale || this._locale;
|
|
839
|
+
if (!this._breakdownData) return `${Math.abs(this._tg1.diff(this._tg2, "day"))}${ZERO_DIFF_DAYS}`;
|
|
840
|
+
const parts = [];
|
|
841
|
+
const bd = this._breakdownData;
|
|
842
|
+
if (bd.years !== 0) parts.push(`${bd.years} ${getDurationUnitLabel("year", l, bd.years)}`);
|
|
843
|
+
if (bd.months !== 0) parts.push(`${bd.months} ${getDurationUnitLabel("month", l, bd.months)}`);
|
|
844
|
+
if (bd.weeks !== 0) parts.push(`${bd.weeks} ${getDurationUnitLabel("week", l, bd.weeks)}`);
|
|
845
|
+
if (bd.days !== 0) parts.push(`${bd.days} ${getDurationUnitLabel("day", l, bd.days)}`);
|
|
846
|
+
if (bd.hours !== 0) parts.push(`${bd.hours} ${getDurationUnitLabel("hour", l, bd.hours)}`);
|
|
847
|
+
if (bd.minutes !== 0) parts.push(`${bd.minutes} ${getDurationUnitLabel("minute", l, bd.minutes)}`);
|
|
848
|
+
if (bd.seconds !== 0) parts.push(`${bd.seconds} ${getDurationUnitLabel("second", l, bd.seconds)}`);
|
|
849
|
+
if (parts.length === 0) return formatZeroDuration(l);
|
|
850
|
+
return joinDurationParts(parts, l);
|
|
851
|
+
}
|
|
852
|
+
getMode() {
|
|
853
|
+
return this._mode;
|
|
854
|
+
}
|
|
855
|
+
valueOf() {
|
|
856
|
+
return this._value;
|
|
857
|
+
}
|
|
858
|
+
toString() {
|
|
859
|
+
if (this._mode === "calendar" && this._breakdownData) return this.format();
|
|
860
|
+
return this._value.toString();
|
|
861
|
+
}
|
|
862
|
+
toJSON() {
|
|
863
|
+
return this._value;
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
/**
|
|
867
|
+
* DurationResult class - Represents a duration breakdown with humanize support
|
|
868
|
+
* Returned by until(), since(), and between() methods
|
|
869
|
+
*/
|
|
870
|
+
var DurationResult = class {
|
|
871
|
+
years;
|
|
872
|
+
months;
|
|
873
|
+
weeks;
|
|
874
|
+
days;
|
|
875
|
+
hours;
|
|
876
|
+
minutes;
|
|
877
|
+
seconds;
|
|
878
|
+
milliseconds;
|
|
879
|
+
_locale;
|
|
880
|
+
_startDate;
|
|
881
|
+
_endDate;
|
|
882
|
+
_steps = [];
|
|
883
|
+
_mode = "exact";
|
|
884
|
+
_leapYearFlags = [];
|
|
885
|
+
_calculationTimeMs = 0;
|
|
886
|
+
constructor(parts, locale = "en", metadata) {
|
|
887
|
+
this.years = parts.years;
|
|
888
|
+
this.months = parts.months;
|
|
889
|
+
this.weeks = parts.weeks;
|
|
890
|
+
this.days = parts.days;
|
|
891
|
+
this.hours = parts.hours;
|
|
892
|
+
this.minutes = parts.minutes;
|
|
893
|
+
this.seconds = parts.seconds;
|
|
894
|
+
this.milliseconds = parts.milliseconds;
|
|
895
|
+
this._locale = locale;
|
|
896
|
+
if (metadata) {
|
|
897
|
+
this._startDate = metadata.startDate;
|
|
898
|
+
this._endDate = metadata.endDate;
|
|
899
|
+
this._steps = metadata.steps || [];
|
|
900
|
+
this._mode = metadata.mode || "exact";
|
|
901
|
+
this._leapYearFlags = metadata.leapYearFlags || [];
|
|
902
|
+
this._calculationTimeMs = metadata.calculationTimeMs || 0;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
humanize(options) {
|
|
906
|
+
const locale = options?.locale || this._locale;
|
|
907
|
+
const fullBreakdown = options?.fullBreakdown ?? false;
|
|
908
|
+
const numeric = options?.numeric ?? "always";
|
|
909
|
+
const nonZeroParts = [
|
|
910
|
+
{
|
|
911
|
+
unit: "year",
|
|
912
|
+
value: this.years
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
unit: "month",
|
|
916
|
+
value: this.months
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
unit: "week",
|
|
920
|
+
value: this.weeks
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
unit: "day",
|
|
924
|
+
value: this.days
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
unit: "hour",
|
|
928
|
+
value: this.hours
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
unit: "minute",
|
|
932
|
+
value: this.minutes
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
unit: "second",
|
|
936
|
+
value: this.seconds
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
unit: "millisecond",
|
|
940
|
+
value: this.milliseconds
|
|
941
|
+
}
|
|
942
|
+
].filter((p) => p.value > 0);
|
|
943
|
+
if (nonZeroParts.length === 0) return "0 seconds";
|
|
944
|
+
if (!fullBreakdown || nonZeroParts.length === 1) {
|
|
945
|
+
const largest = nonZeroParts[0];
|
|
946
|
+
try {
|
|
947
|
+
return new Intl.RelativeTimeFormat(locale, {
|
|
948
|
+
numeric,
|
|
949
|
+
style: "long"
|
|
950
|
+
}).format(largest.value, largest.unit);
|
|
951
|
+
} catch {
|
|
952
|
+
return `${largest.value} ${this.pluralizeUnit(largest.unit, largest.value, locale)}`;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return joinDurationParts(nonZeroParts.map((p) => {
|
|
956
|
+
const label = getDurationUnitLabel(p.unit, locale, p.value);
|
|
957
|
+
return `${p.value} ${label}`;
|
|
958
|
+
}), locale);
|
|
959
|
+
}
|
|
960
|
+
total(unit) {
|
|
961
|
+
let totalMs = 0;
|
|
962
|
+
totalMs += this.years * MS_PER_YEAR;
|
|
963
|
+
totalMs += this.months * MS_PER_MONTH;
|
|
964
|
+
totalMs += this.weeks * MS_PER_WEEK;
|
|
965
|
+
totalMs += this.days * MS_PER_DAY;
|
|
966
|
+
totalMs += this.hours * MS_PER_HOUR;
|
|
967
|
+
totalMs += this.minutes * MS_PER_MINUTE;
|
|
968
|
+
totalMs += this.seconds * MS_PER_SECOND;
|
|
969
|
+
totalMs += this.milliseconds;
|
|
970
|
+
switch (unit) {
|
|
971
|
+
case "millisecond": return totalMs;
|
|
972
|
+
case "second": return totalMs / MS_PER_SECOND;
|
|
973
|
+
case "minute": return totalMs / MS_PER_MINUTE;
|
|
974
|
+
case "hour": return totalMs / MS_PER_HOUR;
|
|
975
|
+
case "day": return totalMs / MS_PER_DAY;
|
|
976
|
+
case "week": return totalMs / MS_PER_WEEK;
|
|
977
|
+
case "month": return totalMs / MS_PER_MONTH;
|
|
978
|
+
case "year": return totalMs / MS_PER_YEAR;
|
|
979
|
+
case "microsecond": return totalMs * 1e3;
|
|
980
|
+
case "nanosecond": return totalMs * 1e6;
|
|
981
|
+
default: throw new Error(`Unsupported unit: ${unit}`);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
toString() {
|
|
985
|
+
return this.humanize({ fullBreakdown: true });
|
|
986
|
+
}
|
|
987
|
+
toJSON() {
|
|
988
|
+
return {
|
|
989
|
+
years: this.years,
|
|
990
|
+
months: this.months,
|
|
991
|
+
weeks: this.weeks,
|
|
992
|
+
days: this.days,
|
|
993
|
+
hours: this.hours,
|
|
994
|
+
minutes: this.minutes,
|
|
995
|
+
seconds: this.seconds,
|
|
996
|
+
milliseconds: this.milliseconds
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
explain() {
|
|
1000
|
+
let steps = [...this._steps];
|
|
1001
|
+
if (steps.length === 0) steps = this.generateExplanationSteps();
|
|
1002
|
+
const explanationParts = [];
|
|
1003
|
+
if (this._startDate && this._endDate) explanationParts.push(`Calculated duration from ${this._startDate} to ${this._endDate}`);
|
|
1004
|
+
if (this._leapYearFlags && this._leapYearFlags.length > 0) {
|
|
1005
|
+
const leapYears = this._leapYearFlags.filter((f) => f.isLeap).map((f) => f.year);
|
|
1006
|
+
if (leapYears.length > 0) explanationParts.push(`Leap year(s) detected: ${leapYears.join(", ")}`);
|
|
1007
|
+
}
|
|
1008
|
+
explanationParts.push(`Breakdown: ${this.years} year(s), ${this.months} month(s), ${this.days} day(s)`);
|
|
1009
|
+
explanationParts.push(`Mode: ${this._mode} calculation`);
|
|
1010
|
+
return {
|
|
1011
|
+
input: this._startDate && this._endDate ? [this._startDate, this._endDate] : [],
|
|
1012
|
+
steps,
|
|
1013
|
+
breakdown: {
|
|
1014
|
+
years: this.years,
|
|
1015
|
+
months: this.months,
|
|
1016
|
+
weeks: this.weeks,
|
|
1017
|
+
days: this.days,
|
|
1018
|
+
hours: this.hours,
|
|
1019
|
+
minutes: this.minutes,
|
|
1020
|
+
seconds: this.seconds,
|
|
1021
|
+
milliseconds: this.milliseconds
|
|
1022
|
+
},
|
|
1023
|
+
mode: this._mode,
|
|
1024
|
+
explanation: explanationParts.join(". "),
|
|
1025
|
+
locale: this._locale,
|
|
1026
|
+
leapYearFlags: this._leapYearFlags.length > 0 ? this._leapYearFlags : void 0,
|
|
1027
|
+
metadata: {
|
|
1028
|
+
calculationTimeMs: this._calculationTimeMs,
|
|
1029
|
+
precision: "day"
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
generateExplanationSteps() {
|
|
1034
|
+
const steps = [];
|
|
1035
|
+
if (this._startDate && this._endDate) steps.push(`Input: ${this._startDate} to ${this._endDate}`);
|
|
1036
|
+
else steps.push("Duration calculation started");
|
|
1037
|
+
if (this.years > 0) steps.push(`Years: ${this.years}`);
|
|
1038
|
+
if (this.months > 0) steps.push(`Months: ${this.months}`);
|
|
1039
|
+
if (this.weeks > 0) steps.push(`Weeks: ${this.weeks}`);
|
|
1040
|
+
if (this.days > 0) steps.push(`Days: ${this.days}`);
|
|
1041
|
+
if (this.hours > 0) steps.push(`Hours: ${this.hours}`);
|
|
1042
|
+
if (this.minutes > 0) steps.push(`Minutes: ${this.minutes}`);
|
|
1043
|
+
if (this.seconds > 0) steps.push(`Seconds: ${this.seconds}`);
|
|
1044
|
+
if (this.milliseconds > 0) steps.push(`Milliseconds: ${this.milliseconds}`);
|
|
1045
|
+
if (this._leapYearFlags && this._leapYearFlags.length > 0) {
|
|
1046
|
+
for (const flag of this._leapYearFlags) if (flag.isLeap) steps.push(`${flag.year} is a leap year (February has ${flag.daysInFebruary} days)`);
|
|
1047
|
+
}
|
|
1048
|
+
const totalParts = [
|
|
1049
|
+
this.years > 0 ? `${this.years}y` : "",
|
|
1050
|
+
this.months > 0 ? `${this.months}m` : "",
|
|
1051
|
+
this.days > 0 ? `${this.days}d` : "",
|
|
1052
|
+
this.hours > 0 ? `${this.hours}h` : "",
|
|
1053
|
+
this.minutes > 0 ? `${this.minutes}min` : "",
|
|
1054
|
+
this.seconds > 0 ? `${this.seconds}s` : ""
|
|
1055
|
+
].filter((p) => p.length > 0).join(" ");
|
|
1056
|
+
steps.push(`Total: ${totalParts || "0"}`);
|
|
1057
|
+
return steps;
|
|
1058
|
+
}
|
|
1059
|
+
pluralizeUnit(unit, count, locale) {
|
|
1060
|
+
return getDurationUnitLabel(unit, locale, count);
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
/**
|
|
1064
|
+
* TimeRange - Fluent API for date range operations
|
|
1065
|
+
*
|
|
1066
|
+
* @example
|
|
1067
|
+
* TimeGuard.range('2024-01-15', '2024-03-20')
|
|
1068
|
+
* .toDuration().humanize() // "2 months and 5 days"
|
|
1069
|
+
* .inMonths() // 2.1355 (precise decimal)
|
|
1070
|
+
*/
|
|
1071
|
+
var TimeRange = class TimeRange {
|
|
1072
|
+
_start;
|
|
1073
|
+
_end;
|
|
1074
|
+
constructor(start, end) {
|
|
1075
|
+
this._start = start;
|
|
1076
|
+
this._end = end;
|
|
1077
|
+
}
|
|
1078
|
+
get start() {
|
|
1079
|
+
return this._start;
|
|
1080
|
+
}
|
|
1081
|
+
get end() {
|
|
1082
|
+
return this._end;
|
|
1083
|
+
}
|
|
1084
|
+
getOrdered() {
|
|
1085
|
+
const t1 = TemporalAdapter.toPlainDateTime(this._start.toTemporal());
|
|
1086
|
+
const t2 = TemporalAdapter.toPlainDateTime(this._end.toTemporal());
|
|
1087
|
+
return TemporalAdapter.compare(t1, t2) <= 0 ? [t1, t2] : [t2, t1];
|
|
1088
|
+
}
|
|
1089
|
+
toDuration() {
|
|
1090
|
+
return TimeGuard.between(this._start, this._end);
|
|
1091
|
+
}
|
|
1092
|
+
inMonths() {
|
|
1093
|
+
return this.toDuration().total("month");
|
|
1094
|
+
}
|
|
1095
|
+
humanize(options) {
|
|
1096
|
+
return this.toDuration().humanize(options);
|
|
1097
|
+
}
|
|
1098
|
+
in(unit) {
|
|
1099
|
+
return this.toDuration().total(unit);
|
|
1100
|
+
}
|
|
1101
|
+
contains(date) {
|
|
1102
|
+
const tg = date instanceof TimeGuard ? date : new TimeGuard(date);
|
|
1103
|
+
const t = TemporalAdapter.toPlainDateTime(tg.toTemporal());
|
|
1104
|
+
const [startT, endT] = this.getOrdered();
|
|
1105
|
+
return TemporalAdapter.compare(t, startT) >= 0 && TemporalAdapter.compare(t, endT) <= 0;
|
|
1106
|
+
}
|
|
1107
|
+
overlaps(other) {
|
|
1108
|
+
const [s1, e1] = this.getOrdered();
|
|
1109
|
+
const [s2, e2] = other.getOrdered();
|
|
1110
|
+
return TemporalAdapter.compare(s1, e2) <= 0 && TemporalAdapter.compare(s2, e1) <= 0;
|
|
1111
|
+
}
|
|
1112
|
+
intersect(other) {
|
|
1113
|
+
if (!this.overlaps(other)) return null;
|
|
1114
|
+
const [s1, e1] = this.getOrdered();
|
|
1115
|
+
const [s2, e2] = other.getOrdered();
|
|
1116
|
+
const startT = TemporalAdapter.compare(s1, s2) >= 0 ? s1 : s2;
|
|
1117
|
+
const endT = TemporalAdapter.compare(e1, e2) <= 0 ? e1 : e2;
|
|
1118
|
+
return new TimeRange(TimeGuard.fromTemporal(startT, { locale: this._start.locale() }), TimeGuard.fromTemporal(endT, { locale: this._end.locale() }));
|
|
1119
|
+
}
|
|
1120
|
+
union(other) {
|
|
1121
|
+
const [s1, e1] = this.getOrdered();
|
|
1122
|
+
const [s2, e2] = other.getOrdered();
|
|
1123
|
+
const startT = TemporalAdapter.compare(s1, s2) <= 0 ? s1 : s2;
|
|
1124
|
+
const endT = TemporalAdapter.compare(e1, e2) >= 0 ? e1 : e2;
|
|
1125
|
+
return new TimeRange(TimeGuard.fromTemporal(startT, { locale: this._start.locale() }), TimeGuard.fromTemporal(endT, { locale: this._end.locale() }));
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
/**
|
|
1129
|
+
* TimeGuard implementation - Main facade class
|
|
1130
|
+
*/
|
|
1131
|
+
var TimeGuard = class TimeGuard {
|
|
1132
|
+
temporal;
|
|
1133
|
+
config;
|
|
1134
|
+
formatterInstance;
|
|
1135
|
+
static holidays = /* @__PURE__ */ new Set();
|
|
1136
|
+
static registerHolidays(dates) {
|
|
1137
|
+
dates.forEach((d) => {
|
|
1138
|
+
try {
|
|
1139
|
+
const tg = new TimeGuard(d);
|
|
1140
|
+
this.holidays.add(tg.format("YYYY-MM-DD"));
|
|
1141
|
+
} catch {}
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
static clearHolidays() {
|
|
1145
|
+
this.holidays.clear();
|
|
1146
|
+
}
|
|
1147
|
+
static getRegisteredHolidays() {
|
|
1148
|
+
return Array.from(this.holidays).sort();
|
|
1149
|
+
}
|
|
1150
|
+
static ZERO_DURATION = {
|
|
1151
|
+
years: 0,
|
|
1152
|
+
months: 0,
|
|
1153
|
+
weeks: 0,
|
|
1154
|
+
days: 0,
|
|
1155
|
+
hours: 0,
|
|
1156
|
+
minutes: 0,
|
|
1157
|
+
seconds: 0,
|
|
1158
|
+
milliseconds: 0
|
|
1159
|
+
};
|
|
1160
|
+
static isLeapYearValue(year) {
|
|
1161
|
+
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
1162
|
+
}
|
|
1163
|
+
static toDurationParts(duration) {
|
|
1164
|
+
return {
|
|
1165
|
+
years: Math.floor(duration.years || 0),
|
|
1166
|
+
months: Math.floor(duration.months || 0),
|
|
1167
|
+
weeks: Math.floor(duration.weeks || 0),
|
|
1168
|
+
days: Math.floor(duration.days || 0),
|
|
1169
|
+
hours: Math.floor(duration.hours || 0),
|
|
1170
|
+
minutes: Math.floor(duration.minutes || 0),
|
|
1171
|
+
seconds: Math.floor(duration.seconds || 0),
|
|
1172
|
+
milliseconds: Math.floor(duration.milliseconds || 0)
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
constructor(input, config) {
|
|
1176
|
+
this.formatterInstance = new DateFormatter();
|
|
1177
|
+
this.config = {
|
|
1178
|
+
locale: config?.locale || "en",
|
|
1179
|
+
timezone: config?.timezone || "UTC",
|
|
1180
|
+
strict: config?.strict ?? false
|
|
1181
|
+
};
|
|
1182
|
+
this.temporal = TemporalAdapter.parseToPlainDateTime(input);
|
|
1183
|
+
if (this.config.timezone && this.config.timezone !== "UTC") try {
|
|
1184
|
+
const zoned = this.temporal.toZonedDateTime(this.config.timezone);
|
|
1185
|
+
this.temporal = zoned;
|
|
1186
|
+
} catch {}
|
|
1187
|
+
}
|
|
1188
|
+
static now(config) {
|
|
1189
|
+
return new TimeGuard(void 0, config);
|
|
1190
|
+
}
|
|
1191
|
+
static from(input, config) {
|
|
1192
|
+
return new TimeGuard(input, config);
|
|
1193
|
+
}
|
|
1194
|
+
static fromTemporal(temporal, config) {
|
|
1195
|
+
const instance = new TimeGuard(void 0, config);
|
|
1196
|
+
instance.temporal = temporal;
|
|
1197
|
+
return instance;
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Calculate duration between two dates - always returns positive duration
|
|
1201
|
+
*
|
|
1202
|
+
* @example
|
|
1203
|
+
* TimeGuard.between(start, end).humanize() // "2 months and 5 days"
|
|
1204
|
+
* TimeGuard.between(end, start).humanize() // "2 months and 5 days" (still positive)
|
|
1205
|
+
*/
|
|
1206
|
+
static between(date1, date2) {
|
|
1207
|
+
const t1 = TemporalAdapter.toPlainDateTime(date1.temporal);
|
|
1208
|
+
const t2 = TemporalAdapter.toPlainDateTime(date2.temporal);
|
|
1209
|
+
const [earlier, later] = TemporalAdapter.compare(t1, t2) <= 0 ? [date1, date2] : [date2, date1];
|
|
1210
|
+
return earlier.until(later);
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Create a TimeRange for fluent duration calculations
|
|
1214
|
+
*
|
|
1215
|
+
* @example
|
|
1216
|
+
* TimeGuard.range("2024-01-15", "2024-03-20").humanize() // "2 months and 5 days"
|
|
1217
|
+
*/
|
|
1218
|
+
static range(start, end) {
|
|
1219
|
+
return new TimeRange(start instanceof TimeGuard ? start : new TimeGuard(start), end instanceof TimeGuard ? end : new TimeGuard(end));
|
|
1220
|
+
}
|
|
1221
|
+
toTemporal() {
|
|
1222
|
+
return this.temporal;
|
|
1223
|
+
}
|
|
1224
|
+
toDate() {
|
|
1225
|
+
return TemporalAdapter.toDate(this.temporal);
|
|
1226
|
+
}
|
|
1227
|
+
toISOString() {
|
|
1228
|
+
return TemporalAdapter.toISOString(this.temporal);
|
|
1229
|
+
}
|
|
1230
|
+
valueOf() {
|
|
1231
|
+
return TemporalAdapter.toUnix(this.temporal);
|
|
1232
|
+
}
|
|
1233
|
+
unix() {
|
|
1234
|
+
return Math.floor(this.valueOf() / 1e3);
|
|
1235
|
+
}
|
|
1236
|
+
toJSON() {
|
|
1237
|
+
return this.toISOString();
|
|
1238
|
+
}
|
|
1239
|
+
[Symbol.toPrimitive](hint) {
|
|
1240
|
+
if (hint === "number") return this.valueOf();
|
|
1241
|
+
if (hint === "string") return this.toISOString();
|
|
1242
|
+
return this.toISOString();
|
|
1243
|
+
}
|
|
1244
|
+
toString() {
|
|
1245
|
+
return this.format("YYYY-MM-DD HH:mm:ss");
|
|
1246
|
+
}
|
|
1247
|
+
locale(locale) {
|
|
1248
|
+
if (locale === void 0) return this.config.locale;
|
|
1249
|
+
const cloned = this.clone();
|
|
1250
|
+
cloned.config.locale = locale;
|
|
1251
|
+
return cloned;
|
|
1252
|
+
}
|
|
1253
|
+
timezone(timezone) {
|
|
1254
|
+
if (timezone === void 0) return this.config.timezone;
|
|
1255
|
+
const cloned = this.clone();
|
|
1256
|
+
cloned.config.timezone = timezone;
|
|
1257
|
+
try {
|
|
1258
|
+
cloned.temporal = TemporalAdapter.toPlainDateTime(cloned.temporal).toZonedDateTime(timezone);
|
|
1259
|
+
} catch {}
|
|
1260
|
+
return cloned;
|
|
1261
|
+
}
|
|
1262
|
+
format(pattern) {
|
|
1263
|
+
const plainDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1264
|
+
const formatPattern = [
|
|
1265
|
+
"iso",
|
|
1266
|
+
"date",
|
|
1267
|
+
"time",
|
|
1268
|
+
"datetime",
|
|
1269
|
+
"rfc2822",
|
|
1270
|
+
"rfc3339",
|
|
1271
|
+
"utc"
|
|
1272
|
+
].includes(pattern) ? this.formatterInstance.getPreset(pattern) : pattern;
|
|
1273
|
+
return this.formatterInstance.format(plainDT, formatPattern, this.config.locale);
|
|
1274
|
+
}
|
|
1275
|
+
get(component) {
|
|
1276
|
+
const plainDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1277
|
+
const unitMap = {
|
|
1278
|
+
year: "year",
|
|
1279
|
+
month: "month",
|
|
1280
|
+
week: "year",
|
|
1281
|
+
day: "day",
|
|
1282
|
+
hour: "hour",
|
|
1283
|
+
minute: "minute",
|
|
1284
|
+
second: "second",
|
|
1285
|
+
millisecond: "millisecond",
|
|
1286
|
+
microsecond: "microsecond",
|
|
1287
|
+
nanosecond: "nanosecond"
|
|
1288
|
+
};
|
|
1289
|
+
if (component === "week") return plainDT.weekOfYear;
|
|
1290
|
+
return plainDT[unitMap[component]];
|
|
1291
|
+
}
|
|
1292
|
+
/** Map from singular Unit to plural Temporal polyfill key */
|
|
1293
|
+
static UNIT_TO_PLURAL = {
|
|
1294
|
+
year: "years",
|
|
1295
|
+
month: "months",
|
|
1296
|
+
week: "weeks",
|
|
1297
|
+
day: "days",
|
|
1298
|
+
hour: "hours",
|
|
1299
|
+
minute: "minutes",
|
|
1300
|
+
second: "seconds",
|
|
1301
|
+
millisecond: "milliseconds",
|
|
1302
|
+
microsecond: "microseconds",
|
|
1303
|
+
nanosecond: "nanoseconds",
|
|
1304
|
+
years: "years",
|
|
1305
|
+
months: "months",
|
|
1306
|
+
weeks: "weeks",
|
|
1307
|
+
days: "days",
|
|
1308
|
+
hours: "hours",
|
|
1309
|
+
minutes: "minutes",
|
|
1310
|
+
seconds: "seconds",
|
|
1311
|
+
milliseconds: "milliseconds",
|
|
1312
|
+
microseconds: "microseconds",
|
|
1313
|
+
nanoseconds: "nanoseconds"
|
|
1314
|
+
};
|
|
1315
|
+
add(units) {
|
|
1316
|
+
let plainDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1317
|
+
const duration = {
|
|
1318
|
+
years: 0,
|
|
1319
|
+
months: 0,
|
|
1320
|
+
weeks: 0,
|
|
1321
|
+
days: 0,
|
|
1322
|
+
hours: 0,
|
|
1323
|
+
minutes: 0,
|
|
1324
|
+
seconds: 0,
|
|
1325
|
+
milliseconds: 0,
|
|
1326
|
+
microseconds: 0,
|
|
1327
|
+
nanoseconds: 0
|
|
1328
|
+
};
|
|
1329
|
+
let hasValidFields = false;
|
|
1330
|
+
Object.entries(units).forEach(([unit, value]) => {
|
|
1331
|
+
if (value !== void 0 && value !== 0) {
|
|
1332
|
+
const num = Number(value);
|
|
1333
|
+
if (Number.isFinite(num)) {
|
|
1334
|
+
const targetKey = TimeGuard.UNIT_TO_PLURAL[unit] || unit;
|
|
1335
|
+
duration[targetKey] = Math.trunc(num);
|
|
1336
|
+
hasValidFields = true;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
if (!hasValidFields) return this;
|
|
1341
|
+
plainDT = plainDT.add(duration);
|
|
1342
|
+
return TimeGuard.fromTemporal(plainDT, this.config);
|
|
1343
|
+
}
|
|
1344
|
+
subtract(units) {
|
|
1345
|
+
const negated = {};
|
|
1346
|
+
Object.entries(units).forEach(([unit, value]) => {
|
|
1347
|
+
negated[unit] = value ? -value : 0;
|
|
1348
|
+
});
|
|
1349
|
+
return this.add(negated);
|
|
1350
|
+
}
|
|
1351
|
+
diff(other, unitOrOptions) {
|
|
1352
|
+
const plainDT1 = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1353
|
+
const plainDT2 = TemporalAdapter.toPlainDateTime(other.temporal);
|
|
1354
|
+
const unitMap = {
|
|
1355
|
+
year: "years",
|
|
1356
|
+
month: "months",
|
|
1357
|
+
week: "weeks",
|
|
1358
|
+
day: "days",
|
|
1359
|
+
hour: "hours",
|
|
1360
|
+
minute: "minutes",
|
|
1361
|
+
second: "seconds",
|
|
1362
|
+
millisecond: "milliseconds",
|
|
1363
|
+
microsecond: "microseconds",
|
|
1364
|
+
nanosecond: "nanoseconds"
|
|
1365
|
+
};
|
|
1366
|
+
if (typeof unitOrOptions === "string") {
|
|
1367
|
+
const unit = unitOrOptions;
|
|
1368
|
+
const mappedUnit = unitMap[unit];
|
|
1369
|
+
const durationObj = plainDT1.since(plainDT2, { smallestUnit: unit });
|
|
1370
|
+
return Math.round(durationObj[mappedUnit] || 0);
|
|
1371
|
+
}
|
|
1372
|
+
const options = unitOrOptions || {};
|
|
1373
|
+
const mode = options.mode || "exact";
|
|
1374
|
+
const unit = options.unit || "millisecond";
|
|
1375
|
+
const locale = options.locale || this.config.locale;
|
|
1376
|
+
if (mode === "exact") {
|
|
1377
|
+
const mappedUnit = unitMap[unit];
|
|
1378
|
+
const duration = plainDT1.since(plainDT2, { smallestUnit: unit });
|
|
1379
|
+
const result = Math.round(duration[mappedUnit] || 0);
|
|
1380
|
+
if (unitOrOptions === void 0 || typeof unitOrOptions === "object") {
|
|
1381
|
+
const totalMs = calculateTotalMs(duration);
|
|
1382
|
+
return new DiffResult(Math.round(totalMs), this, other, mode, void 0, locale);
|
|
1383
|
+
}
|
|
1384
|
+
return result;
|
|
1385
|
+
}
|
|
1386
|
+
if (mode === "calendar") {
|
|
1387
|
+
const duration = plainDT1.since(plainDT2, {
|
|
1388
|
+
largestUnit: "month",
|
|
1389
|
+
smallestUnit: "millisecond"
|
|
1390
|
+
});
|
|
1391
|
+
const breakdownData = {
|
|
1392
|
+
years: Math.floor(duration.years || 0),
|
|
1393
|
+
months: Math.floor(duration.months || 0),
|
|
1394
|
+
weeks: Math.floor(duration.weeks || 0),
|
|
1395
|
+
days: Math.floor(duration.days || 0),
|
|
1396
|
+
hours: Math.floor(duration.hours || 0),
|
|
1397
|
+
minutes: Math.floor(duration.minutes || 0),
|
|
1398
|
+
seconds: Math.floor(duration.seconds || 0),
|
|
1399
|
+
milliseconds: Math.floor(duration.milliseconds || 0)
|
|
1400
|
+
};
|
|
1401
|
+
const totalMs = calculateTotalMs(duration);
|
|
1402
|
+
return new DiffResult(Math.round(totalMs), this, other, mode, breakdownData, locale);
|
|
1403
|
+
}
|
|
1404
|
+
const totalMs = calculateTotalMs(plainDT1.since(plainDT2, { smallestUnit: "millisecond" }));
|
|
1405
|
+
return new DiffResult(Math.round(totalMs), this, other, mode, void 0, locale);
|
|
1406
|
+
}
|
|
1407
|
+
isBefore(other) {
|
|
1408
|
+
const plainDT1 = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1409
|
+
const plainDT2 = TemporalAdapter.toPlainDateTime(other.temporal);
|
|
1410
|
+
return TemporalAdapter.compare(plainDT1, plainDT2) < 0;
|
|
1411
|
+
}
|
|
1412
|
+
isAfter(other) {
|
|
1413
|
+
const plainDT1 = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1414
|
+
const plainDT2 = TemporalAdapter.toPlainDateTime(other.temporal);
|
|
1415
|
+
return TemporalAdapter.compare(plainDT1, plainDT2) > 0;
|
|
1416
|
+
}
|
|
1417
|
+
isSame(other, unit) {
|
|
1418
|
+
if (!unit) return TemporalAdapter.compare(TemporalAdapter.toPlainDateTime(this.temporal), TemporalAdapter.toPlainDateTime(other.temporal)) === 0;
|
|
1419
|
+
const plainDT1 = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1420
|
+
const plainDT2 = TemporalAdapter.toPlainDateTime(other.temporal);
|
|
1421
|
+
switch (unit) {
|
|
1422
|
+
case "year": return plainDT1.year === plainDT2.year;
|
|
1423
|
+
case "month": return plainDT1.year === plainDT2.year && plainDT1.month === plainDT2.month;
|
|
1424
|
+
case "day": return plainDT1.year === plainDT2.year && plainDT1.month === plainDT2.month && plainDT1.day === plainDT2.day;
|
|
1425
|
+
case "hour": return plainDT1.year === plainDT2.year && plainDT1.month === plainDT2.month && plainDT1.day === plainDT2.day && plainDT1.hour === plainDT2.hour;
|
|
1426
|
+
case "minute": return plainDT1.year === plainDT2.year && plainDT1.month === plainDT2.month && plainDT1.day === plainDT2.day && plainDT1.hour === plainDT2.hour && plainDT1.minute === plainDT2.minute;
|
|
1427
|
+
case "second": return plainDT1.year === plainDT2.year && plainDT1.month === plainDT2.month && plainDT1.day === plainDT2.day && plainDT1.hour === plainDT2.hour && plainDT1.minute === plainDT2.minute && plainDT1.second === plainDT2.second;
|
|
1428
|
+
default: return false;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
isBetween(start, end, unit, inclusivity = "[]") {
|
|
1432
|
+
const plainDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1433
|
+
const plainStart = TemporalAdapter.toPlainDateTime(start.temporal);
|
|
1434
|
+
const plainEnd = TemporalAdapter.toPlainDateTime(end.temporal);
|
|
1435
|
+
let afterStart = true;
|
|
1436
|
+
let beforeEnd = true;
|
|
1437
|
+
if (unit) {
|
|
1438
|
+
const startCopy = this.clone().startOf(unit);
|
|
1439
|
+
const endCopy = this.clone().endOf(unit);
|
|
1440
|
+
const plainCopy = TemporalAdapter.toPlainDateTime(startCopy.temporal);
|
|
1441
|
+
const plainEndCopy = TemporalAdapter.toPlainDateTime(endCopy.temporal);
|
|
1442
|
+
afterStart = TemporalAdapter.compare(plainCopy, plainStart) >= 0;
|
|
1443
|
+
beforeEnd = TemporalAdapter.compare(plainEndCopy, plainEnd) <= 0;
|
|
1444
|
+
} else {
|
|
1445
|
+
afterStart = TemporalAdapter.compare(plainDT, plainStart) >= 0;
|
|
1446
|
+
beforeEnd = TemporalAdapter.compare(plainDT, plainEnd) <= 0;
|
|
1447
|
+
}
|
|
1448
|
+
const hasStartBracket = inclusivity[0] === "[";
|
|
1449
|
+
const hasEndBracket = inclusivity[1] === "]";
|
|
1450
|
+
return (hasStartBracket ? afterStart : TemporalAdapter.compare(plainDT, plainStart) > 0) && (hasEndBracket ? beforeEnd : TemporalAdapter.compare(plainDT, plainEnd) < 0);
|
|
1451
|
+
}
|
|
1452
|
+
clone() {
|
|
1453
|
+
return new TimeGuard(this.toDate(), this.config);
|
|
1454
|
+
}
|
|
1455
|
+
startOf(unit) {
|
|
1456
|
+
const plainDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1457
|
+
const values = {};
|
|
1458
|
+
switch (unit) {
|
|
1459
|
+
case "year":
|
|
1460
|
+
values.month = 1;
|
|
1461
|
+
values.day = 1;
|
|
1462
|
+
values.hour = 0;
|
|
1463
|
+
values.minute = 0;
|
|
1464
|
+
values.second = 0;
|
|
1465
|
+
values.millisecond = 0;
|
|
1466
|
+
break;
|
|
1467
|
+
case "month":
|
|
1468
|
+
values.day = 1;
|
|
1469
|
+
values.hour = 0;
|
|
1470
|
+
values.minute = 0;
|
|
1471
|
+
values.second = 0;
|
|
1472
|
+
values.millisecond = 0;
|
|
1473
|
+
break;
|
|
1474
|
+
case "week":
|
|
1475
|
+
case "day":
|
|
1476
|
+
values.hour = 0;
|
|
1477
|
+
values.minute = 0;
|
|
1478
|
+
values.second = 0;
|
|
1479
|
+
values.millisecond = 0;
|
|
1480
|
+
break;
|
|
1481
|
+
case "hour":
|
|
1482
|
+
values.minute = 0;
|
|
1483
|
+
values.second = 0;
|
|
1484
|
+
values.millisecond = 0;
|
|
1485
|
+
break;
|
|
1486
|
+
case "minute":
|
|
1487
|
+
values.second = 0;
|
|
1488
|
+
values.millisecond = 0;
|
|
1489
|
+
break;
|
|
1490
|
+
case "second":
|
|
1491
|
+
values.millisecond = 0;
|
|
1492
|
+
break;
|
|
1493
|
+
}
|
|
1494
|
+
const updated = plainDT.with(values);
|
|
1495
|
+
return TimeGuard.fromTemporal(updated, this.config);
|
|
1496
|
+
}
|
|
1497
|
+
endOf(unit) {
|
|
1498
|
+
return this.startOf(unit).add({ [unit]: 1 }).subtract({ millisecond: 1 });
|
|
1499
|
+
}
|
|
1500
|
+
set(values) {
|
|
1501
|
+
const updated = TemporalAdapter.toPlainDateTime(this.temporal).with(values);
|
|
1502
|
+
return TimeGuard.fromTemporal(updated, this.config);
|
|
1503
|
+
}
|
|
1504
|
+
year() {
|
|
1505
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).year;
|
|
1506
|
+
}
|
|
1507
|
+
month() {
|
|
1508
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).month;
|
|
1509
|
+
}
|
|
1510
|
+
day() {
|
|
1511
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).day;
|
|
1512
|
+
}
|
|
1513
|
+
hour() {
|
|
1514
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).hour;
|
|
1515
|
+
}
|
|
1516
|
+
minute() {
|
|
1517
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).minute;
|
|
1518
|
+
}
|
|
1519
|
+
second() {
|
|
1520
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).second;
|
|
1521
|
+
}
|
|
1522
|
+
millisecond() {
|
|
1523
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).millisecond;
|
|
1524
|
+
}
|
|
1525
|
+
dayOfWeek() {
|
|
1526
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).dayOfWeek;
|
|
1527
|
+
}
|
|
1528
|
+
dayOfYear() {
|
|
1529
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).dayOfYear;
|
|
1530
|
+
}
|
|
1531
|
+
weekOfYear() {
|
|
1532
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).weekOfYear;
|
|
1533
|
+
}
|
|
1534
|
+
daysInMonth() {
|
|
1535
|
+
return TemporalAdapter.toPlainDateTime(this.temporal).add({ months: 1 }).with({ day: 1 }).subtract({ days: 1 }).day;
|
|
1536
|
+
}
|
|
1537
|
+
daysInYear() {
|
|
1538
|
+
const year = this.year();
|
|
1539
|
+
return TimeGuard.isLeapYearValue(year) ? 366 : 365;
|
|
1540
|
+
}
|
|
1541
|
+
inLeapYear() {
|
|
1542
|
+
const plainDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1543
|
+
return TimeGuard.isLeapYearValue(plainDT.year);
|
|
1544
|
+
}
|
|
1545
|
+
until(other, options) {
|
|
1546
|
+
const plainDT1 = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1547
|
+
const plainDT2 = TemporalAdapter.toPlainDateTime(other.temporal);
|
|
1548
|
+
const startTime = performance.now();
|
|
1549
|
+
try {
|
|
1550
|
+
const temporalOptions = {};
|
|
1551
|
+
if (options?.largestUnit) temporalOptions.largestUnit = options.largestUnit;
|
|
1552
|
+
if (options?.smallestUnit) temporalOptions.smallestUnit = options.smallestUnit;
|
|
1553
|
+
const duration = plainDT2.since(plainDT1, Object.keys(temporalOptions).length > 0 ? temporalOptions : void 0);
|
|
1554
|
+
const parts = TimeGuard.toDurationParts(duration);
|
|
1555
|
+
const startYear = plainDT1.year;
|
|
1556
|
+
const endYear = plainDT2.year;
|
|
1557
|
+
const leapYearFlags = [];
|
|
1558
|
+
for (let year = startYear; year <= endYear; year++) {
|
|
1559
|
+
const isLeap = TimeGuard.isLeapYearValue(year);
|
|
1560
|
+
leapYearFlags.push({
|
|
1561
|
+
year,
|
|
1562
|
+
isLeap,
|
|
1563
|
+
daysInFebruary: isLeap ? 29 : 28
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
const steps = this.generateUntilSteps(plainDT1, plainDT2, parts, leapYearFlags);
|
|
1567
|
+
const calculationTimeMs = performance.now() - startTime;
|
|
1568
|
+
return new DurationResult(parts, this.config.locale, {
|
|
1569
|
+
startDate: plainDT1.toString(),
|
|
1570
|
+
endDate: plainDT2.toString(),
|
|
1571
|
+
steps,
|
|
1572
|
+
mode: "exact",
|
|
1573
|
+
leapYearFlags: leapYearFlags.filter((f) => f.isLeap),
|
|
1574
|
+
calculationTimeMs
|
|
1575
|
+
});
|
|
1576
|
+
} catch {
|
|
1577
|
+
return new DurationResult(TimeGuard.ZERO_DURATION, this.config.locale, {
|
|
1578
|
+
startDate: plainDT1.toString(),
|
|
1579
|
+
endDate: plainDT2.toString(),
|
|
1580
|
+
steps: ["Calculation failed, returning zero duration"],
|
|
1581
|
+
mode: "estimated"
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
generateUntilSteps(start, end, parts, leapYearFlags) {
|
|
1586
|
+
const steps = [];
|
|
1587
|
+
steps.push(`Parsed dates: ${start.year}-${String(start.month).padStart(2, "0")}-${String(start.day).padStart(2, "0")} (day ${start.dayOfYear} of ${TimeGuard.isLeapYearValue(start.year) ? 366 : 365})`);
|
|
1588
|
+
steps.push(`to ${end.year}-${String(end.month).padStart(2, "0")}-${String(end.day).padStart(2, "0")} (day ${end.dayOfYear} of ${TimeGuard.isLeapYearValue(end.year) ? 366 : 365})`);
|
|
1589
|
+
const leapYearsInRange = leapYearFlags.filter((f) => f.isLeap);
|
|
1590
|
+
if (leapYearsInRange.length > 0) for (const flag of leapYearsInRange) steps.push(`${flag.year} is a leap year (February has ${flag.daysInFebruary} days)`);
|
|
1591
|
+
if (parts.years > 0) steps.push(`Years: ${parts.years}`);
|
|
1592
|
+
if (parts.months > 0) steps.push(`Months: ${parts.months}`);
|
|
1593
|
+
if (parts.days > 0) steps.push(`Days: ${parts.days}`);
|
|
1594
|
+
if (parts.hours > 0 || parts.minutes > 0 || parts.seconds > 0) {
|
|
1595
|
+
const timeComponents = [];
|
|
1596
|
+
if (parts.hours > 0) timeComponents.push(`${parts.hours}h`);
|
|
1597
|
+
if (parts.minutes > 0) timeComponents.push(`${parts.minutes}m`);
|
|
1598
|
+
if (parts.seconds > 0) timeComponents.push(`${parts.seconds}s`);
|
|
1599
|
+
steps.push(`Time: ${timeComponents.join(" ")}`);
|
|
1600
|
+
}
|
|
1601
|
+
const totalDays = parts.years * 365.25 + parts.months * 30.4375 + parts.days + parts.hours / 24;
|
|
1602
|
+
steps.push(`Total: approximately ${totalDays.toFixed(2)} days`);
|
|
1603
|
+
return steps;
|
|
1604
|
+
}
|
|
1605
|
+
round(options = {}) {
|
|
1606
|
+
const { smallestUnit = "millisecond", roundingMode = "halfExpand" } = options;
|
|
1607
|
+
const polyfillDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1608
|
+
const unitValues = {
|
|
1609
|
+
year: polyfillDT.year,
|
|
1610
|
+
month: polyfillDT.month,
|
|
1611
|
+
day: polyfillDT.day,
|
|
1612
|
+
hour: polyfillDT.hour,
|
|
1613
|
+
minute: polyfillDT.minute,
|
|
1614
|
+
second: polyfillDT.second,
|
|
1615
|
+
millisecond: polyfillDT.millisecond,
|
|
1616
|
+
microsecond: polyfillDT.microsecond || 0,
|
|
1617
|
+
nanosecond: polyfillDT.nanosecond || 0,
|
|
1618
|
+
week: 0
|
|
1619
|
+
};
|
|
1620
|
+
const unitOrder = [
|
|
1621
|
+
"year",
|
|
1622
|
+
"month",
|
|
1623
|
+
"day",
|
|
1624
|
+
"hour",
|
|
1625
|
+
"minute",
|
|
1626
|
+
"second",
|
|
1627
|
+
"millisecond",
|
|
1628
|
+
"microsecond",
|
|
1629
|
+
"nanosecond"
|
|
1630
|
+
];
|
|
1631
|
+
const smallestIndex = unitOrder.indexOf(smallestUnit);
|
|
1632
|
+
if (smallestIndex === -1) return this.clone();
|
|
1633
|
+
const roundedValues = {};
|
|
1634
|
+
for (let i = 0; i < smallestIndex; i++) {
|
|
1635
|
+
const unit = unitOrder[i];
|
|
1636
|
+
roundedValues[unit] = unitValues[unit];
|
|
1637
|
+
}
|
|
1638
|
+
const unit = unitOrder[smallestIndex];
|
|
1639
|
+
let value = unitValues[unit];
|
|
1640
|
+
if (smallestIndex + 1 < unitOrder.length) {
|
|
1641
|
+
const nextUnitValue = unitValues[unitOrder[smallestIndex + 1]];
|
|
1642
|
+
const shouldRoundUp = (mode, nextVal) => {
|
|
1643
|
+
switch (mode) {
|
|
1644
|
+
case "ceil": return nextVal > 0;
|
|
1645
|
+
case "floor":
|
|
1646
|
+
case "trunc": return false;
|
|
1647
|
+
case "halfExpand":
|
|
1648
|
+
case "halfFloor":
|
|
1649
|
+
case "halfCeil": return nextVal >= 5;
|
|
1650
|
+
case "expand": return nextVal > 0;
|
|
1651
|
+
default: return nextVal >= 5;
|
|
1652
|
+
}
|
|
1653
|
+
};
|
|
1654
|
+
if (shouldRoundUp(roundingMode, nextUnitValue)) value += 1;
|
|
1655
|
+
}
|
|
1656
|
+
roundedValues[unit] = value;
|
|
1657
|
+
for (let i = smallestIndex + 1; i < unitOrder.length; i++) roundedValues[unitOrder[i]] = 0;
|
|
1658
|
+
return this.set(roundedValues);
|
|
1659
|
+
}
|
|
1660
|
+
toPlainDate() {
|
|
1661
|
+
const plainDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1662
|
+
return {
|
|
1663
|
+
year: plainDT.year,
|
|
1664
|
+
month: plainDT.month,
|
|
1665
|
+
day: plainDT.day,
|
|
1666
|
+
dayOfWeek: plainDT.dayOfWeek
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
toPlainTime() {
|
|
1670
|
+
const plainDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1671
|
+
return {
|
|
1672
|
+
hour: plainDT.hour,
|
|
1673
|
+
minute: plainDT.minute,
|
|
1674
|
+
second: plainDT.second,
|
|
1675
|
+
millisecond: plainDT.millisecond
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
withDate(year, month, day) {
|
|
1679
|
+
return this.set({
|
|
1680
|
+
year,
|
|
1681
|
+
month,
|
|
1682
|
+
day
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
withTime(hour, minute = 0, second = 0, millisecond = 0) {
|
|
1686
|
+
return this.set({
|
|
1687
|
+
hour,
|
|
1688
|
+
minute,
|
|
1689
|
+
second,
|
|
1690
|
+
millisecond
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
getOffset() {
|
|
1694
|
+
return this.temporal?.offset || "Z";
|
|
1695
|
+
}
|
|
1696
|
+
getOffsetNanoseconds() {
|
|
1697
|
+
return this.temporal?.offsetNanoseconds || 0;
|
|
1698
|
+
}
|
|
1699
|
+
getTimeZoneId() {
|
|
1700
|
+
return this.temporal?.timeZoneId || null;
|
|
1701
|
+
}
|
|
1702
|
+
startOfDay() {
|
|
1703
|
+
return this.startOf("day");
|
|
1704
|
+
}
|
|
1705
|
+
endOfDay() {
|
|
1706
|
+
return this.endOf("day");
|
|
1707
|
+
}
|
|
1708
|
+
since(other, options) {
|
|
1709
|
+
const plainDT1 = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1710
|
+
const plainDT2 = TemporalAdapter.toPlainDateTime(other.temporal);
|
|
1711
|
+
const startTime = performance.now();
|
|
1712
|
+
try {
|
|
1713
|
+
const temporalOptions = {};
|
|
1714
|
+
if (options?.largestUnit) temporalOptions.largestUnit = options.largestUnit;
|
|
1715
|
+
if (options?.smallestUnit) temporalOptions.smallestUnit = options.smallestUnit;
|
|
1716
|
+
const duration = plainDT1.since(plainDT2, Object.keys(temporalOptions).length > 0 ? temporalOptions : void 0);
|
|
1717
|
+
const parts = TimeGuard.toDurationParts(duration);
|
|
1718
|
+
const startYear = plainDT2.year;
|
|
1719
|
+
const endYear = plainDT1.year;
|
|
1720
|
+
const leapYearFlags = [];
|
|
1721
|
+
for (let year = startYear; year <= endYear; year++) {
|
|
1722
|
+
const isLeap = TimeGuard.isLeapYearValue(year);
|
|
1723
|
+
leapYearFlags.push({
|
|
1724
|
+
year,
|
|
1725
|
+
isLeap,
|
|
1726
|
+
daysInFebruary: isLeap ? 29 : 28
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
const steps = this.generateUntilSteps(plainDT2, plainDT1, parts, leapYearFlags);
|
|
1730
|
+
const calculationTimeMs = performance.now() - startTime;
|
|
1731
|
+
return new DurationResult(parts, this.config.locale, {
|
|
1732
|
+
startDate: plainDT2.toString(),
|
|
1733
|
+
endDate: plainDT1.toString(),
|
|
1734
|
+
steps,
|
|
1735
|
+
mode: "exact",
|
|
1736
|
+
leapYearFlags: leapYearFlags.filter((f) => f.isLeap),
|
|
1737
|
+
calculationTimeMs
|
|
1738
|
+
});
|
|
1739
|
+
} catch {
|
|
1740
|
+
return new DurationResult(TimeGuard.ZERO_DURATION, this.config.locale, {
|
|
1741
|
+
startDate: plainDT2.toString(),
|
|
1742
|
+
endDate: plainDT1.toString(),
|
|
1743
|
+
steps: ["Calculation failed, returning zero duration"],
|
|
1744
|
+
mode: "estimated"
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
toDurationString(other) {
|
|
1749
|
+
const duration = other ? this.until(other) : this.until(TimeGuard.now());
|
|
1750
|
+
const parts = [];
|
|
1751
|
+
if (duration.years) parts.push(`${duration.years}Y`);
|
|
1752
|
+
if (duration.months) parts.push(`${duration.months}M`);
|
|
1753
|
+
if (duration.days) parts.push(`${duration.days}D`);
|
|
1754
|
+
const timeParts = [];
|
|
1755
|
+
if (duration.hours) timeParts.push(`${duration.hours}H`);
|
|
1756
|
+
if (duration.minutes) timeParts.push(`${duration.minutes}M`);
|
|
1757
|
+
if (duration.seconds) timeParts.push(`${duration.seconds}S`);
|
|
1758
|
+
const result = `P${parts.join("")}${timeParts.length > 0 ? "T" + timeParts.join("") : ""}`;
|
|
1759
|
+
return result === "P" ? "PT0S" : result;
|
|
1760
|
+
}
|
|
1761
|
+
isPast() {
|
|
1762
|
+
return this.isBefore(TimeGuard.now());
|
|
1763
|
+
}
|
|
1764
|
+
isFuture() {
|
|
1765
|
+
return this.isAfter(TimeGuard.now());
|
|
1766
|
+
}
|
|
1767
|
+
isToday() {
|
|
1768
|
+
return this.isSame(TimeGuard.now(), "day");
|
|
1769
|
+
}
|
|
1770
|
+
isTomorrow() {
|
|
1771
|
+
return this.isSame(TimeGuard.now().add({ day: 1 }), "day");
|
|
1772
|
+
}
|
|
1773
|
+
isYesterday() {
|
|
1774
|
+
return this.isSame(TimeGuard.now().subtract({ day: 1 }), "day");
|
|
1775
|
+
}
|
|
1776
|
+
isWeekend() {
|
|
1777
|
+
const plainDT = TemporalAdapter.toPlainDateTime(this.temporal);
|
|
1778
|
+
return plainDT.dayOfWeek === 6 || plainDT.dayOfWeek === 7;
|
|
1779
|
+
}
|
|
1780
|
+
isHoliday() {
|
|
1781
|
+
const dateStr = this.format("YYYY-MM-DD");
|
|
1782
|
+
return TimeGuard.holidays.has(dateStr);
|
|
1783
|
+
}
|
|
1784
|
+
isBusinessDay() {
|
|
1785
|
+
return !this.isWeekend() && !this.isHoliday();
|
|
1786
|
+
}
|
|
1787
|
+
addBusinessDays(days) {
|
|
1788
|
+
const num = Math.trunc(days);
|
|
1789
|
+
if (!Number.isFinite(num) || num === 0) return this;
|
|
1790
|
+
let cloned = this.clone();
|
|
1791
|
+
let remaining = Math.abs(num);
|
|
1792
|
+
const step = num > 0 ? 1 : -1;
|
|
1793
|
+
while (remaining > 0) {
|
|
1794
|
+
cloned = cloned.add({ day: step });
|
|
1795
|
+
if (cloned.isBusinessDay()) remaining--;
|
|
1796
|
+
}
|
|
1797
|
+
return cloned;
|
|
1798
|
+
}
|
|
1799
|
+
subtractBusinessDays(days) {
|
|
1800
|
+
return this.addBusinessDays(-days);
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
function timeGuard(input, config) {
|
|
1804
|
+
return new TimeGuard(input, config);
|
|
1805
|
+
}
|
|
1806
|
+
/** The current version of TimeGuard */
|
|
1807
|
+
var version = "2.7.0";
|
|
1808
|
+
//#endregion
|
|
1809
|
+
Object.defineProperty(exports, "CalendarManager", {
|
|
1810
|
+
enumerable: true,
|
|
1811
|
+
get: function() {
|
|
1812
|
+
return CalendarManager;
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
Object.defineProperty(exports, "DateFormatter", {
|
|
1816
|
+
enumerable: true,
|
|
1817
|
+
get: function() {
|
|
1818
|
+
return DateFormatter;
|
|
1819
|
+
}
|
|
1820
|
+
});
|
|
1821
|
+
Object.defineProperty(exports, "DurationResult", {
|
|
1822
|
+
enumerable: true,
|
|
1823
|
+
get: function() {
|
|
1824
|
+
return DurationResult;
|
|
1825
|
+
}
|
|
1826
|
+
});
|
|
1827
|
+
Object.defineProperty(exports, "EN_LOCALE", {
|
|
1828
|
+
enumerable: true,
|
|
1829
|
+
get: function() {
|
|
1830
|
+
return EN_LOCALE;
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
Object.defineProperty(exports, "ES_LOCALE", {
|
|
1834
|
+
enumerable: true,
|
|
1835
|
+
get: function() {
|
|
1836
|
+
return ES_LOCALE;
|
|
1837
|
+
}
|
|
1838
|
+
});
|
|
1839
|
+
Object.defineProperty(exports, "GregorianCalendar", {
|
|
1840
|
+
enumerable: true,
|
|
1841
|
+
get: function() {
|
|
1842
|
+
return GregorianCalendar;
|
|
1843
|
+
}
|
|
1844
|
+
});
|
|
1845
|
+
Object.defineProperty(exports, "LocaleManager", {
|
|
1846
|
+
enumerable: true,
|
|
1847
|
+
get: function() {
|
|
1848
|
+
return LocaleManager;
|
|
1849
|
+
}
|
|
1850
|
+
});
|
|
1851
|
+
Object.defineProperty(exports, "PluginManager", {
|
|
1852
|
+
enumerable: true,
|
|
1853
|
+
get: function() {
|
|
1854
|
+
return PluginManager;
|
|
1855
|
+
}
|
|
1856
|
+
});
|
|
1857
|
+
Object.defineProperty(exports, "TemporalAdapter", {
|
|
1858
|
+
enumerable: true,
|
|
1859
|
+
get: function() {
|
|
1860
|
+
return TemporalAdapter;
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
Object.defineProperty(exports, "TimeGuard", {
|
|
1864
|
+
enumerable: true,
|
|
1865
|
+
get: function() {
|
|
1866
|
+
return TimeGuard;
|
|
1867
|
+
}
|
|
1868
|
+
});
|
|
1869
|
+
Object.defineProperty(exports, "TimeRange", {
|
|
1870
|
+
enumerable: true,
|
|
1871
|
+
get: function() {
|
|
1872
|
+
return TimeRange;
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
Object.defineProperty(exports, "calendarManager", {
|
|
1876
|
+
enumerable: true,
|
|
1877
|
+
get: function() {
|
|
1878
|
+
return calendarManager;
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
Object.defineProperty(exports, "timeGuard", {
|
|
1882
|
+
enumerable: true,
|
|
1883
|
+
get: function() {
|
|
1884
|
+
return timeGuard;
|
|
1885
|
+
}
|
|
1886
|
+
});
|
|
1887
|
+
Object.defineProperty(exports, "version", {
|
|
1888
|
+
enumerable: true,
|
|
1889
|
+
get: function() {
|
|
1890
|
+
return version;
|
|
1891
|
+
}
|
|
1892
|
+
});
|