chronos-ts 1.1.0 → 2.0.1
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.md +249 -443
- package/dist/core/chronos.d.ts +460 -0
- package/dist/core/chronos.js +1259 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +19 -0
- package/dist/core/interval.d.ts +289 -0
- package/dist/core/interval.js +689 -0
- package/dist/core/period-collection.d.ts +205 -0
- package/dist/core/period-collection.js +562 -0
- package/dist/core/period.d.ts +428 -0
- package/dist/core/period.js +1007 -0
- package/dist/core/timezone.d.ts +289 -0
- package/dist/core/timezone.js +671 -0
- package/dist/index.d.ts +50 -4
- package/dist/index.js +148 -22
- package/dist/locales/index.d.ts +66 -0
- package/dist/locales/index.js +847 -0
- package/dist/types/index.d.ts +428 -0
- package/dist/types/index.js +71 -0
- package/dist/utils/index.d.ts +127 -0
- package/dist/utils/index.js +656 -0
- package/package.json +19 -3
- package/dist/interval.d.ts +0 -61
- package/dist/interval.js +0 -82
- package/dist/period.d.ts +0 -196
- package/dist/period.js +0 -365
- package/dist/precision.d.ts +0 -24
- package/dist/precision.js +0 -46
- package/dist/utils.d.ts +0 -190
- package/dist/utils.js +0 -374
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for Chronos
|
|
4
|
+
* @module utils
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MILLISECONDS_PER_YEAR = exports.MILLISECONDS_PER_MONTH = exports.MILLISECONDS_PER_WEEK = exports.MILLISECONDS_PER_DAY = exports.MILLISECONDS_PER_HOUR = exports.MILLISECONDS_PER_MINUTE = exports.MILLISECONDS_PER_SECOND = void 0;
|
|
8
|
+
exports.isDate = isDate;
|
|
9
|
+
exports.isValidDateInput = isValidDateInput;
|
|
10
|
+
exports.isChronosLike = isChronosLike;
|
|
11
|
+
exports.isDuration = isDuration;
|
|
12
|
+
exports.isISODuration = isISODuration;
|
|
13
|
+
exports.isLeapYear = isLeapYear;
|
|
14
|
+
exports.normalizeUnit = normalizeUnit;
|
|
15
|
+
exports.pluralizeUnit = pluralizeUnit;
|
|
16
|
+
exports.getMillisecondsPerUnit = getMillisecondsPerUnit;
|
|
17
|
+
exports.getDaysInMonth = getDaysInMonth;
|
|
18
|
+
exports.getDaysInYear = getDaysInYear;
|
|
19
|
+
exports.getDayOfYear = getDayOfYear;
|
|
20
|
+
exports.getISOWeek = getISOWeek;
|
|
21
|
+
exports.getISOWeekYear = getISOWeekYear;
|
|
22
|
+
exports.getQuarter = getQuarter;
|
|
23
|
+
exports.startOf = startOf;
|
|
24
|
+
exports.endOf = endOf;
|
|
25
|
+
exports.addDuration = addDuration;
|
|
26
|
+
exports.subtractDuration = subtractDuration;
|
|
27
|
+
exports.addUnits = addUnits;
|
|
28
|
+
exports.diffInUnits = diffInUnits;
|
|
29
|
+
exports.parseISODuration = parseISODuration;
|
|
30
|
+
exports.durationToISO = durationToISO;
|
|
31
|
+
exports.compareAtGranularity = compareAtGranularity;
|
|
32
|
+
exports.isSameAt = isSameAt;
|
|
33
|
+
exports.cloneDate = cloneDate;
|
|
34
|
+
exports.isValidDate = isValidDate;
|
|
35
|
+
exports.clamp = clamp;
|
|
36
|
+
exports.ordinalSuffix = ordinalSuffix;
|
|
37
|
+
exports.padStart = padStart;
|
|
38
|
+
const types_1 = require("../types");
|
|
39
|
+
Object.defineProperty(exports, "MILLISECONDS_PER_SECOND", { enumerable: true, get: function () { return types_1.MILLISECONDS_PER_SECOND; } });
|
|
40
|
+
Object.defineProperty(exports, "MILLISECONDS_PER_MINUTE", { enumerable: true, get: function () { return types_1.MILLISECONDS_PER_MINUTE; } });
|
|
41
|
+
Object.defineProperty(exports, "MILLISECONDS_PER_HOUR", { enumerable: true, get: function () { return types_1.MILLISECONDS_PER_HOUR; } });
|
|
42
|
+
Object.defineProperty(exports, "MILLISECONDS_PER_DAY", { enumerable: true, get: function () { return types_1.MILLISECONDS_PER_DAY; } });
|
|
43
|
+
Object.defineProperty(exports, "MILLISECONDS_PER_WEEK", { enumerable: true, get: function () { return types_1.MILLISECONDS_PER_WEEK; } });
|
|
44
|
+
Object.defineProperty(exports, "MILLISECONDS_PER_MONTH", { enumerable: true, get: function () { return types_1.MILLISECONDS_PER_MONTH; } });
|
|
45
|
+
Object.defineProperty(exports, "MILLISECONDS_PER_YEAR", { enumerable: true, get: function () { return types_1.MILLISECONDS_PER_YEAR; } });
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// Type Guards
|
|
48
|
+
// ============================================================================
|
|
49
|
+
/**
|
|
50
|
+
* Check if value is a Date object
|
|
51
|
+
*/
|
|
52
|
+
function isDate(value) {
|
|
53
|
+
return value instanceof Date && !isNaN(value.getTime());
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if value is a valid date input
|
|
57
|
+
*/
|
|
58
|
+
function isValidDateInput(value) {
|
|
59
|
+
if (value === null || value === undefined)
|
|
60
|
+
return true;
|
|
61
|
+
if (typeof value === 'string' || typeof value === 'number')
|
|
62
|
+
return true;
|
|
63
|
+
if (isDate(value))
|
|
64
|
+
return true;
|
|
65
|
+
if (isChronosLike(value))
|
|
66
|
+
return true;
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if value implements ChronosLike interface
|
|
71
|
+
*/
|
|
72
|
+
function isChronosLike(value) {
|
|
73
|
+
if (!value || typeof value !== 'object')
|
|
74
|
+
return false;
|
|
75
|
+
const obj = value;
|
|
76
|
+
return typeof obj.toDate === 'function' && typeof obj.valueOf === 'function';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check if value is a Duration object
|
|
80
|
+
*/
|
|
81
|
+
function isDuration(value) {
|
|
82
|
+
if (!value || typeof value !== 'object')
|
|
83
|
+
return false;
|
|
84
|
+
const obj = value;
|
|
85
|
+
const keys = [
|
|
86
|
+
'years',
|
|
87
|
+
'months',
|
|
88
|
+
'weeks',
|
|
89
|
+
'days',
|
|
90
|
+
'hours',
|
|
91
|
+
'minutes',
|
|
92
|
+
'seconds',
|
|
93
|
+
'milliseconds',
|
|
94
|
+
];
|
|
95
|
+
return keys.some((key) => typeof obj[key] === 'number');
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check if value is a valid ISO 8601 duration string
|
|
99
|
+
*/
|
|
100
|
+
function isISODuration(value) {
|
|
101
|
+
if (typeof value !== 'string')
|
|
102
|
+
return false;
|
|
103
|
+
const pattern = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/;
|
|
104
|
+
return pattern.test(value);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if a year is a leap year
|
|
108
|
+
*/
|
|
109
|
+
function isLeapYear(year) {
|
|
110
|
+
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
111
|
+
}
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// Time Unit Utilities
|
|
114
|
+
// ============================================================================
|
|
115
|
+
/**
|
|
116
|
+
* Unit aliases mapping
|
|
117
|
+
*/
|
|
118
|
+
const UNIT_ALIASES = {
|
|
119
|
+
// Singular
|
|
120
|
+
millisecond: 'millisecond',
|
|
121
|
+
second: 'second',
|
|
122
|
+
minute: 'minute',
|
|
123
|
+
hour: 'hour',
|
|
124
|
+
day: 'day',
|
|
125
|
+
week: 'week',
|
|
126
|
+
month: 'month',
|
|
127
|
+
quarter: 'quarter',
|
|
128
|
+
year: 'year',
|
|
129
|
+
decade: 'decade',
|
|
130
|
+
century: 'century',
|
|
131
|
+
millennium: 'millennium',
|
|
132
|
+
// Plural
|
|
133
|
+
milliseconds: 'millisecond',
|
|
134
|
+
seconds: 'second',
|
|
135
|
+
minutes: 'minute',
|
|
136
|
+
hours: 'hour',
|
|
137
|
+
days: 'day',
|
|
138
|
+
weeks: 'week',
|
|
139
|
+
months: 'month',
|
|
140
|
+
quarters: 'quarter',
|
|
141
|
+
years: 'year',
|
|
142
|
+
decades: 'decade',
|
|
143
|
+
centuries: 'century',
|
|
144
|
+
millennia: 'millennium',
|
|
145
|
+
// Short
|
|
146
|
+
ms: 'millisecond',
|
|
147
|
+
s: 'second',
|
|
148
|
+
m: 'minute',
|
|
149
|
+
h: 'hour',
|
|
150
|
+
d: 'day',
|
|
151
|
+
w: 'week',
|
|
152
|
+
M: 'month',
|
|
153
|
+
Q: 'quarter',
|
|
154
|
+
y: 'year',
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Normalize a time unit to its canonical form
|
|
158
|
+
*/
|
|
159
|
+
function normalizeUnit(unit) {
|
|
160
|
+
var _a;
|
|
161
|
+
const normalized = (_a = UNIT_ALIASES[unit.toLowerCase()]) !== null && _a !== void 0 ? _a : UNIT_ALIASES[unit];
|
|
162
|
+
if (!normalized) {
|
|
163
|
+
throw new Error(`Invalid time unit: ${unit}`);
|
|
164
|
+
}
|
|
165
|
+
return normalized;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get the plural form of a time unit
|
|
169
|
+
*/
|
|
170
|
+
function pluralizeUnit(unit, count) {
|
|
171
|
+
const plurals = {
|
|
172
|
+
millisecond: 'milliseconds',
|
|
173
|
+
second: 'seconds',
|
|
174
|
+
minute: 'minutes',
|
|
175
|
+
hour: 'hours',
|
|
176
|
+
day: 'days',
|
|
177
|
+
week: 'weeks',
|
|
178
|
+
month: 'months',
|
|
179
|
+
quarter: 'quarters',
|
|
180
|
+
year: 'years',
|
|
181
|
+
decade: 'decades',
|
|
182
|
+
century: 'centuries',
|
|
183
|
+
millennium: 'millennia',
|
|
184
|
+
};
|
|
185
|
+
return Math.abs(count) === 1 ? unit : plurals[unit];
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Get milliseconds for a given time unit
|
|
189
|
+
*/
|
|
190
|
+
function getMillisecondsPerUnit(unit) {
|
|
191
|
+
switch (unit) {
|
|
192
|
+
case 'millisecond':
|
|
193
|
+
return 1;
|
|
194
|
+
case 'second':
|
|
195
|
+
return types_1.MILLISECONDS_PER_SECOND;
|
|
196
|
+
case 'minute':
|
|
197
|
+
return types_1.MILLISECONDS_PER_MINUTE;
|
|
198
|
+
case 'hour':
|
|
199
|
+
return types_1.MILLISECONDS_PER_HOUR;
|
|
200
|
+
case 'day':
|
|
201
|
+
return types_1.MILLISECONDS_PER_DAY;
|
|
202
|
+
case 'week':
|
|
203
|
+
return types_1.MILLISECONDS_PER_WEEK;
|
|
204
|
+
case 'month':
|
|
205
|
+
return types_1.MILLISECONDS_PER_MONTH;
|
|
206
|
+
case 'quarter':
|
|
207
|
+
return types_1.MILLISECONDS_PER_MONTH * 3;
|
|
208
|
+
case 'year':
|
|
209
|
+
return types_1.MILLISECONDS_PER_YEAR;
|
|
210
|
+
case 'decade':
|
|
211
|
+
return types_1.MILLISECONDS_PER_YEAR * 10;
|
|
212
|
+
case 'century':
|
|
213
|
+
return types_1.MILLISECONDS_PER_YEAR * 100;
|
|
214
|
+
case 'millennium':
|
|
215
|
+
return types_1.MILLISECONDS_PER_YEAR * 1000;
|
|
216
|
+
default:
|
|
217
|
+
throw new Error(`Unknown unit: ${unit}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// ============================================================================
|
|
221
|
+
// Date Utilities
|
|
222
|
+
// ============================================================================
|
|
223
|
+
/**
|
|
224
|
+
* Get the number of days in a specific month
|
|
225
|
+
*/
|
|
226
|
+
function getDaysInMonth(year, month) {
|
|
227
|
+
// Create date for first day of next month, then subtract one day
|
|
228
|
+
return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Get the number of days in a year
|
|
232
|
+
*/
|
|
233
|
+
function getDaysInYear(year) {
|
|
234
|
+
return isLeapYear(year) ? 366 : 365;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get the day of year (1-366)
|
|
238
|
+
*/
|
|
239
|
+
function getDayOfYear(date) {
|
|
240
|
+
const start = new Date(Date.UTC(date.getFullYear(), 0, 0));
|
|
241
|
+
const diff = date.getTime() - start.getTime();
|
|
242
|
+
return Math.floor(diff / types_1.MILLISECONDS_PER_DAY);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get the ISO week number (1-53)
|
|
246
|
+
*/
|
|
247
|
+
function getISOWeek(date) {
|
|
248
|
+
const target = new Date(date.valueOf());
|
|
249
|
+
// Set to nearest Thursday: current date + 4 - current day number (make Sunday=7)
|
|
250
|
+
const dayNr = (date.getDay() + 6) % 7;
|
|
251
|
+
target.setDate(target.getDate() - dayNr + 3);
|
|
252
|
+
// Store first Thursday of year
|
|
253
|
+
const firstThursday = target.valueOf();
|
|
254
|
+
// Set to start of year
|
|
255
|
+
target.setMonth(0, 1);
|
|
256
|
+
// If not Thursday, set to first Thursday of year
|
|
257
|
+
if (target.getDay() !== 4) {
|
|
258
|
+
target.setMonth(0, 1 + ((4 - target.getDay() + 7) % 7));
|
|
259
|
+
}
|
|
260
|
+
// Calculate week number
|
|
261
|
+
return (1 + Math.ceil((firstThursday - target.valueOf()) / types_1.MILLISECONDS_PER_WEEK));
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Get the ISO week year
|
|
265
|
+
*/
|
|
266
|
+
function getISOWeekYear(date) {
|
|
267
|
+
const target = new Date(date.valueOf());
|
|
268
|
+
target.setDate(target.getDate() + 3 - ((date.getDay() + 6) % 7));
|
|
269
|
+
return target.getFullYear();
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get the quarter (1-4)
|
|
273
|
+
*/
|
|
274
|
+
function getQuarter(date) {
|
|
275
|
+
return Math.floor(date.getMonth() / 3) + 1;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get start of a time unit
|
|
279
|
+
*/
|
|
280
|
+
function startOf(date, unit) {
|
|
281
|
+
const result = new Date(date);
|
|
282
|
+
switch (unit) {
|
|
283
|
+
case 'second':
|
|
284
|
+
result.setMilliseconds(0);
|
|
285
|
+
break;
|
|
286
|
+
case 'minute':
|
|
287
|
+
result.setSeconds(0, 0);
|
|
288
|
+
break;
|
|
289
|
+
case 'hour':
|
|
290
|
+
result.setMinutes(0, 0, 0);
|
|
291
|
+
break;
|
|
292
|
+
case 'day':
|
|
293
|
+
result.setHours(0, 0, 0, 0);
|
|
294
|
+
break;
|
|
295
|
+
case 'week':
|
|
296
|
+
result.setHours(0, 0, 0, 0);
|
|
297
|
+
result.setDate(result.getDate() - result.getDay());
|
|
298
|
+
break;
|
|
299
|
+
case 'month':
|
|
300
|
+
result.setHours(0, 0, 0, 0);
|
|
301
|
+
result.setDate(1);
|
|
302
|
+
break;
|
|
303
|
+
case 'quarter':
|
|
304
|
+
result.setHours(0, 0, 0, 0);
|
|
305
|
+
result.setMonth(Math.floor(result.getMonth() / 3) * 3, 1);
|
|
306
|
+
break;
|
|
307
|
+
case 'year':
|
|
308
|
+
result.setHours(0, 0, 0, 0);
|
|
309
|
+
result.setMonth(0, 1);
|
|
310
|
+
break;
|
|
311
|
+
case 'decade':
|
|
312
|
+
result.setHours(0, 0, 0, 0);
|
|
313
|
+
result.setMonth(0, 1);
|
|
314
|
+
result.setFullYear(Math.floor(result.getFullYear() / 10) * 10);
|
|
315
|
+
break;
|
|
316
|
+
case 'century':
|
|
317
|
+
result.setHours(0, 0, 0, 0);
|
|
318
|
+
result.setMonth(0, 1);
|
|
319
|
+
result.setFullYear(Math.floor(result.getFullYear() / 100) * 100);
|
|
320
|
+
break;
|
|
321
|
+
case 'millennium':
|
|
322
|
+
result.setHours(0, 0, 0, 0);
|
|
323
|
+
result.setMonth(0, 1);
|
|
324
|
+
result.setFullYear(Math.floor(result.getFullYear() / 1000) * 1000);
|
|
325
|
+
break;
|
|
326
|
+
case 'millisecond':
|
|
327
|
+
default:
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get end of a time unit
|
|
334
|
+
*/
|
|
335
|
+
function endOf(date, unit) {
|
|
336
|
+
const result = new Date(date);
|
|
337
|
+
switch (unit) {
|
|
338
|
+
case 'second':
|
|
339
|
+
result.setMilliseconds(999);
|
|
340
|
+
break;
|
|
341
|
+
case 'minute':
|
|
342
|
+
result.setSeconds(59, 999);
|
|
343
|
+
break;
|
|
344
|
+
case 'hour':
|
|
345
|
+
result.setMinutes(59, 59, 999);
|
|
346
|
+
break;
|
|
347
|
+
case 'day':
|
|
348
|
+
result.setHours(23, 59, 59, 999);
|
|
349
|
+
break;
|
|
350
|
+
case 'week':
|
|
351
|
+
result.setHours(23, 59, 59, 999);
|
|
352
|
+
result.setDate(result.getDate() + (6 - result.getDay()));
|
|
353
|
+
break;
|
|
354
|
+
case 'month':
|
|
355
|
+
result.setHours(23, 59, 59, 999);
|
|
356
|
+
result.setMonth(result.getMonth() + 1, 0);
|
|
357
|
+
break;
|
|
358
|
+
case 'quarter':
|
|
359
|
+
result.setHours(23, 59, 59, 999);
|
|
360
|
+
result.setMonth(Math.floor(result.getMonth() / 3) * 3 + 3, 0);
|
|
361
|
+
break;
|
|
362
|
+
case 'year':
|
|
363
|
+
result.setHours(23, 59, 59, 999);
|
|
364
|
+
result.setMonth(11, 31);
|
|
365
|
+
break;
|
|
366
|
+
case 'decade':
|
|
367
|
+
result.setHours(23, 59, 59, 999);
|
|
368
|
+
result.setFullYear(Math.floor(result.getFullYear() / 10) * 10 + 9, 11, 31);
|
|
369
|
+
break;
|
|
370
|
+
case 'century':
|
|
371
|
+
result.setHours(23, 59, 59, 999);
|
|
372
|
+
result.setFullYear(Math.floor(result.getFullYear() / 100) * 100 + 99, 11, 31);
|
|
373
|
+
break;
|
|
374
|
+
case 'millennium':
|
|
375
|
+
result.setHours(23, 59, 59, 999);
|
|
376
|
+
result.setFullYear(Math.floor(result.getFullYear() / 1000) * 1000 + 999, 11, 31);
|
|
377
|
+
break;
|
|
378
|
+
case 'millisecond':
|
|
379
|
+
default:
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
return result;
|
|
383
|
+
}
|
|
384
|
+
// ============================================================================
|
|
385
|
+
// Arithmetic Utilities
|
|
386
|
+
// ============================================================================
|
|
387
|
+
/**
|
|
388
|
+
* Add a duration to a date
|
|
389
|
+
* Handles month overflow by clamping to the last day of the month
|
|
390
|
+
*/
|
|
391
|
+
function addDuration(date, duration) {
|
|
392
|
+
const result = new Date(date);
|
|
393
|
+
if (duration.years) {
|
|
394
|
+
const originalDay = result.getDate();
|
|
395
|
+
result.setFullYear(result.getFullYear() + duration.years);
|
|
396
|
+
// Handle year overflow (e.g., Feb 29 in a leap year to Feb 28 in non-leap year)
|
|
397
|
+
if (result.getDate() !== originalDay) {
|
|
398
|
+
result.setDate(0); // Go to last day of previous month
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (duration.months) {
|
|
402
|
+
const originalDay = result.getDate();
|
|
403
|
+
result.setMonth(result.getMonth() + duration.months);
|
|
404
|
+
// Handle month overflow (e.g., Jan 31 + 1 month = Feb 28/29, not March 2/3)
|
|
405
|
+
if (result.getDate() !== originalDay) {
|
|
406
|
+
result.setDate(0); // Go to last day of previous month
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (duration.weeks) {
|
|
410
|
+
result.setDate(result.getDate() + duration.weeks * 7);
|
|
411
|
+
}
|
|
412
|
+
if (duration.days) {
|
|
413
|
+
result.setDate(result.getDate() + duration.days);
|
|
414
|
+
}
|
|
415
|
+
if (duration.hours) {
|
|
416
|
+
result.setHours(result.getHours() + duration.hours);
|
|
417
|
+
}
|
|
418
|
+
if (duration.minutes) {
|
|
419
|
+
result.setMinutes(result.getMinutes() + duration.minutes);
|
|
420
|
+
}
|
|
421
|
+
if (duration.seconds) {
|
|
422
|
+
result.setSeconds(result.getSeconds() + duration.seconds);
|
|
423
|
+
}
|
|
424
|
+
if (duration.milliseconds) {
|
|
425
|
+
result.setMilliseconds(result.getMilliseconds() + duration.milliseconds);
|
|
426
|
+
}
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Subtract a duration from a date
|
|
431
|
+
*/
|
|
432
|
+
function subtractDuration(date, duration) {
|
|
433
|
+
const negated = {};
|
|
434
|
+
for (const [key, value] of Object.entries(duration)) {
|
|
435
|
+
if (typeof value === 'number') {
|
|
436
|
+
negated[key] = -value;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return addDuration(date, negated);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Add a specific number of units to a date
|
|
443
|
+
*/
|
|
444
|
+
function addUnits(date, amount, unit) {
|
|
445
|
+
const duration = {};
|
|
446
|
+
switch (unit) {
|
|
447
|
+
case 'millisecond':
|
|
448
|
+
duration.milliseconds = amount;
|
|
449
|
+
break;
|
|
450
|
+
case 'second':
|
|
451
|
+
duration.seconds = amount;
|
|
452
|
+
break;
|
|
453
|
+
case 'minute':
|
|
454
|
+
duration.minutes = amount;
|
|
455
|
+
break;
|
|
456
|
+
case 'hour':
|
|
457
|
+
duration.hours = amount;
|
|
458
|
+
break;
|
|
459
|
+
case 'day':
|
|
460
|
+
duration.days = amount;
|
|
461
|
+
break;
|
|
462
|
+
case 'week':
|
|
463
|
+
duration.weeks = amount;
|
|
464
|
+
break;
|
|
465
|
+
case 'month':
|
|
466
|
+
duration.months = amount;
|
|
467
|
+
break;
|
|
468
|
+
case 'quarter':
|
|
469
|
+
duration.months = amount * 3;
|
|
470
|
+
break;
|
|
471
|
+
case 'year':
|
|
472
|
+
duration.years = amount;
|
|
473
|
+
break;
|
|
474
|
+
case 'decade':
|
|
475
|
+
duration.years = amount * 10;
|
|
476
|
+
break;
|
|
477
|
+
case 'century':
|
|
478
|
+
duration.years = amount * 100;
|
|
479
|
+
break;
|
|
480
|
+
case 'millennium':
|
|
481
|
+
duration.years = amount * 1000;
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
return addDuration(date, duration);
|
|
485
|
+
}
|
|
486
|
+
// ============================================================================
|
|
487
|
+
// Difference Utilities
|
|
488
|
+
// ============================================================================
|
|
489
|
+
/**
|
|
490
|
+
* Calculate the difference between two dates in a specific unit
|
|
491
|
+
*/
|
|
492
|
+
function diffInUnits(date1, date2, unit) {
|
|
493
|
+
const diff = date1.getTime() - date2.getTime();
|
|
494
|
+
switch (unit) {
|
|
495
|
+
case 'millisecond':
|
|
496
|
+
return diff;
|
|
497
|
+
case 'second':
|
|
498
|
+
return Math.floor(diff / types_1.MILLISECONDS_PER_SECOND);
|
|
499
|
+
case 'minute':
|
|
500
|
+
return Math.floor(diff / types_1.MILLISECONDS_PER_MINUTE);
|
|
501
|
+
case 'hour':
|
|
502
|
+
return Math.floor(diff / types_1.MILLISECONDS_PER_HOUR);
|
|
503
|
+
case 'day':
|
|
504
|
+
return Math.floor(diff / types_1.MILLISECONDS_PER_DAY);
|
|
505
|
+
case 'week':
|
|
506
|
+
return Math.floor(diff / types_1.MILLISECONDS_PER_WEEK);
|
|
507
|
+
case 'month':
|
|
508
|
+
return monthDiff(date2, date1);
|
|
509
|
+
case 'quarter':
|
|
510
|
+
return Math.floor(monthDiff(date2, date1) / 3);
|
|
511
|
+
case 'year':
|
|
512
|
+
return date1.getFullYear() - date2.getFullYear();
|
|
513
|
+
case 'decade':
|
|
514
|
+
return Math.floor((date1.getFullYear() - date2.getFullYear()) / 10);
|
|
515
|
+
case 'century':
|
|
516
|
+
return Math.floor((date1.getFullYear() - date2.getFullYear()) / 100);
|
|
517
|
+
case 'millennium':
|
|
518
|
+
return Math.floor((date1.getFullYear() - date2.getFullYear()) / 1000);
|
|
519
|
+
default:
|
|
520
|
+
throw new Error(`Unknown unit: ${unit}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Calculate month difference between two dates
|
|
525
|
+
*/
|
|
526
|
+
function monthDiff(from, to) {
|
|
527
|
+
const years = to.getFullYear() - from.getFullYear();
|
|
528
|
+
const months = to.getMonth() - from.getMonth();
|
|
529
|
+
const days = to.getDate() - from.getDate();
|
|
530
|
+
let diff = years * 12 + months;
|
|
531
|
+
// Adjust for day difference
|
|
532
|
+
if (days < 0) {
|
|
533
|
+
diff--;
|
|
534
|
+
}
|
|
535
|
+
return diff;
|
|
536
|
+
}
|
|
537
|
+
// ============================================================================
|
|
538
|
+
// Parsing Utilities
|
|
539
|
+
// ============================================================================
|
|
540
|
+
/**
|
|
541
|
+
* Parse an ISO 8601 duration string
|
|
542
|
+
*/
|
|
543
|
+
function parseISODuration(duration) {
|
|
544
|
+
const pattern = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/;
|
|
545
|
+
const match = duration.match(pattern);
|
|
546
|
+
if (!match) {
|
|
547
|
+
throw new Error(`Invalid ISO 8601 duration: ${duration}`);
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
years: match[1] ? parseInt(match[1], 10) : undefined,
|
|
551
|
+
months: match[2] ? parseInt(match[2], 10) : undefined,
|
|
552
|
+
weeks: match[3] ? parseInt(match[3], 10) : undefined,
|
|
553
|
+
days: match[4] ? parseInt(match[4], 10) : undefined,
|
|
554
|
+
hours: match[5] ? parseInt(match[5], 10) : undefined,
|
|
555
|
+
minutes: match[6] ? parseInt(match[6], 10) : undefined,
|
|
556
|
+
seconds: match[7] ? parseFloat(match[7]) : undefined,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Convert a duration to ISO 8601 format
|
|
561
|
+
*/
|
|
562
|
+
function durationToISO(duration) {
|
|
563
|
+
var _a, _b;
|
|
564
|
+
let result = 'P';
|
|
565
|
+
if (duration.years)
|
|
566
|
+
result += `${duration.years}Y`;
|
|
567
|
+
if (duration.months)
|
|
568
|
+
result += `${duration.months}M`;
|
|
569
|
+
if (duration.weeks)
|
|
570
|
+
result += `${duration.weeks}W`;
|
|
571
|
+
if (duration.days)
|
|
572
|
+
result += `${duration.days}D`;
|
|
573
|
+
const hasTime = duration.hours ||
|
|
574
|
+
duration.minutes ||
|
|
575
|
+
duration.seconds ||
|
|
576
|
+
duration.milliseconds;
|
|
577
|
+
if (hasTime) {
|
|
578
|
+
result += 'T';
|
|
579
|
+
if (duration.hours)
|
|
580
|
+
result += `${duration.hours}H`;
|
|
581
|
+
if (duration.minutes)
|
|
582
|
+
result += `${duration.minutes}M`;
|
|
583
|
+
const seconds = ((_a = duration.seconds) !== null && _a !== void 0 ? _a : 0) + ((_b = duration.milliseconds) !== null && _b !== void 0 ? _b : 0) / 1000;
|
|
584
|
+
if (seconds)
|
|
585
|
+
result += `${seconds}S`;
|
|
586
|
+
}
|
|
587
|
+
return result === 'P' ? 'PT0S' : result;
|
|
588
|
+
}
|
|
589
|
+
// ============================================================================
|
|
590
|
+
// Comparison Utilities
|
|
591
|
+
// ============================================================================
|
|
592
|
+
/**
|
|
593
|
+
* Compare two dates at a specific granularity
|
|
594
|
+
*/
|
|
595
|
+
function compareAtGranularity(date1, date2, unit) {
|
|
596
|
+
const d1 = startOf(date1, unit);
|
|
597
|
+
const d2 = startOf(date2, unit);
|
|
598
|
+
if (d1.getTime() < d2.getTime())
|
|
599
|
+
return -1;
|
|
600
|
+
if (d1.getTime() > d2.getTime())
|
|
601
|
+
return 1;
|
|
602
|
+
return 0;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Check if two dates are the same at a specific granularity
|
|
606
|
+
*/
|
|
607
|
+
function isSameAt(date1, date2, unit) {
|
|
608
|
+
return compareAtGranularity(date1, date2, unit) === 0;
|
|
609
|
+
}
|
|
610
|
+
// ============================================================================
|
|
611
|
+
// Cloning Utilities
|
|
612
|
+
// ============================================================================
|
|
613
|
+
/**
|
|
614
|
+
* Create a deep clone of a date
|
|
615
|
+
*/
|
|
616
|
+
function cloneDate(date) {
|
|
617
|
+
return new Date(date.getTime());
|
|
618
|
+
}
|
|
619
|
+
// ============================================================================
|
|
620
|
+
// Validation Utilities
|
|
621
|
+
// ============================================================================
|
|
622
|
+
/**
|
|
623
|
+
* Check if a date is valid
|
|
624
|
+
*/
|
|
625
|
+
function isValidDate(date) {
|
|
626
|
+
return date instanceof Date && !isNaN(date.getTime());
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Ensure a value is within bounds
|
|
630
|
+
*/
|
|
631
|
+
function clamp(value, min, max) {
|
|
632
|
+
return Math.min(Math.max(value, min), max);
|
|
633
|
+
}
|
|
634
|
+
// ============================================================================
|
|
635
|
+
// Ordinal Utilities
|
|
636
|
+
// ============================================================================
|
|
637
|
+
/**
|
|
638
|
+
* Get ordinal suffix for a number (1st, 2nd, 3rd, etc.)
|
|
639
|
+
*/
|
|
640
|
+
function ordinalSuffix(n) {
|
|
641
|
+
const s = ['th', 'st', 'nd', 'rd'];
|
|
642
|
+
const v = n % 100;
|
|
643
|
+
return n + (s[(v - 20) % 10] || s[v] || s[0]);
|
|
644
|
+
}
|
|
645
|
+
// ============================================================================
|
|
646
|
+
// Padding Utilities
|
|
647
|
+
// ============================================================================
|
|
648
|
+
/**
|
|
649
|
+
* Pad a number with leading zeros
|
|
650
|
+
*/
|
|
651
|
+
function padStart(value, length, char = '0') {
|
|
652
|
+
const str = String(value);
|
|
653
|
+
if (str.length >= length)
|
|
654
|
+
return str;
|
|
655
|
+
return char.repeat(length - str.length) + str;
|
|
656
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chronos-ts",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "A comprehensive TypeScript
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "A comprehensive TypeScript library for date and time manipulation, inspired by Carbon PHP. Features immutable API, intervals, periods, timezones, and i18n support.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
7
15
|
"scripts": {
|
|
8
16
|
"build": "tsc",
|
|
9
17
|
"test": "jest",
|
|
18
|
+
"test:coverage": "jest --coverage",
|
|
10
19
|
"format": "prettier --write 'src/**/*.{ts,js,json,md}'",
|
|
11
20
|
"lint": "eslint 'src/**/*.{ts,js}'",
|
|
12
21
|
"prepare": "npm run build",
|
|
@@ -33,7 +42,14 @@
|
|
|
33
42
|
"time-period",
|
|
34
43
|
"date-interval",
|
|
35
44
|
"time-arithmetic",
|
|
36
|
-
"date-arithmetic"
|
|
45
|
+
"date-arithmetic",
|
|
46
|
+
"carbon",
|
|
47
|
+
"moment",
|
|
48
|
+
"dayjs",
|
|
49
|
+
"timezone",
|
|
50
|
+
"i18n",
|
|
51
|
+
"localization",
|
|
52
|
+
"immutable"
|
|
37
53
|
],
|
|
38
54
|
"files": [
|
|
39
55
|
"dist/**",
|