@timestamp-js/core 0.1.0-rc.0 → 0.1.0-rc.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 +12 -11
- package/dist/calendar.d.ts +101 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +69 -0
- package/dist/calendar.js.map +1 -0
- package/dist/index.d.ts +439 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +646 -87
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { formatCalendarDate, gregorianCalendar } from './calendar.js';
|
|
2
|
+
export { formatCalendarDate, gregorianCalendar } from './calendar.js';
|
|
1
3
|
/**
|
|
2
4
|
* Matches supported date and date-time input.
|
|
3
5
|
*
|
|
@@ -132,7 +134,7 @@ export const SECONDS_IN_DAY = TIME_CONSTANTS.SECONDS_IN.DAY;
|
|
|
132
134
|
* instead of mutating this shared default.
|
|
133
135
|
*/
|
|
134
136
|
export const Timestamp = freezeTimestamp({
|
|
135
|
-
date:
|
|
137
|
+
date: '',
|
|
136
138
|
hasDay: false,
|
|
137
139
|
year: 0,
|
|
138
140
|
month: 0,
|
|
@@ -161,17 +163,46 @@ function freezeTimestamp(timestamp) {
|
|
|
161
163
|
function cloneTimestamp(timestamp) {
|
|
162
164
|
return { ...timestamp };
|
|
163
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Extracts calendar date fields from a timestamp-shaped value.
|
|
168
|
+
*
|
|
169
|
+
* @param timestamp Timestamp or timestamp-like object to read.
|
|
170
|
+
* @returns Plain calendar date fields.
|
|
171
|
+
* @category calendar
|
|
172
|
+
*/
|
|
173
|
+
export function toCalendarDateParts(timestamp) {
|
|
174
|
+
return {
|
|
175
|
+
year: timestamp.year,
|
|
176
|
+
month: timestamp.month,
|
|
177
|
+
day: timestamp.day,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function getCalendarTimestampOptions(timestamp) {
|
|
181
|
+
return {
|
|
182
|
+
hasTime: timestamp.hasTime,
|
|
183
|
+
hour: timestamp.hour,
|
|
184
|
+
minute: timestamp.minute,
|
|
185
|
+
second: timestamp.second,
|
|
186
|
+
millisecond: timestamp.millisecond,
|
|
187
|
+
timezone: timestamp.timezone,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function getCalendarWorkWeek(date, calendar) {
|
|
191
|
+
const firstWeekday = calendar.getWeekday({ year: date.year, month: 1, day: 1 });
|
|
192
|
+
return Math.floor((calendar.getDayOfYear(date) + firstWeekday - 1) / 7) + 1;
|
|
193
|
+
}
|
|
164
194
|
function parseMillisecond(value) {
|
|
165
|
-
return value === undefined ? undefined : parseInt(value.padEnd(3,
|
|
195
|
+
return value === undefined ? undefined : parseInt(value.padEnd(3, '0'), 10);
|
|
166
196
|
}
|
|
167
197
|
/**
|
|
168
198
|
* Validates whether an input string matches the supported timestamp grammar.
|
|
169
199
|
*
|
|
170
200
|
* @param {string} input A string in the form `YYYY-MM-DD`, `YYYY-MM-DD HH:mm`, or a full ISO-like date time.
|
|
171
201
|
* @returns {boolean} True if parseable
|
|
202
|
+
* @category validation
|
|
172
203
|
*/
|
|
173
204
|
export function validateTimestamp(input) {
|
|
174
|
-
if (typeof input !==
|
|
205
|
+
if (typeof input !== 'string')
|
|
175
206
|
return false;
|
|
176
207
|
return PARSE_DATETIME.test(input);
|
|
177
208
|
}
|
|
@@ -184,18 +215,19 @@ export function validateTimestamp(input) {
|
|
|
184
215
|
*
|
|
185
216
|
* @param {string} input In the form `YYYY-MM-DD`, `YYYY-MM-DD HH:mm:ss`, or an ISO-like date time with optional milliseconds and timezone suffix.
|
|
186
217
|
* @returns {Timestamp} Minimal Timestamp object, or `null` when the input cannot be parsed.
|
|
218
|
+
* @category parsing
|
|
187
219
|
*/
|
|
188
220
|
export function parsed(input) {
|
|
189
|
-
if (typeof input !==
|
|
221
|
+
if (typeof input !== 'string')
|
|
190
222
|
return null;
|
|
191
223
|
const parts = PARSE_DATETIME.exec(input);
|
|
192
224
|
if (!parts || !parts[1] || !parts[2])
|
|
193
225
|
return null;
|
|
194
226
|
const year = parseInt(parts[1], 10);
|
|
195
227
|
const month = parseInt(parts[2], 10);
|
|
196
|
-
const day = parseInt(parts[3] ||
|
|
197
|
-
const hour = parseInt(parts[4] ||
|
|
198
|
-
const minute = parseInt(parts[5] ||
|
|
228
|
+
const day = parseInt(parts[3] || '1', 10);
|
|
229
|
+
const hour = parseInt(parts[4] || '0', 10);
|
|
230
|
+
const minute = parseInt(parts[5] || '0', 10);
|
|
199
231
|
const second = parts[6] === undefined ? undefined : parseInt(parts[6], 10);
|
|
200
232
|
const millisecond = parseMillisecond(parts[7]);
|
|
201
233
|
const timestamp = {
|
|
@@ -232,17 +264,17 @@ function parseDateByMode(date, utc) {
|
|
|
232
264
|
return null;
|
|
233
265
|
if (Number.isNaN(date.getTime()))
|
|
234
266
|
return null;
|
|
235
|
-
const UTC = utc ?
|
|
267
|
+
const UTC = utc ? 'UTC' : '';
|
|
236
268
|
const second = date[`get${UTC}Seconds`]();
|
|
237
269
|
const millisecond = date[`get${UTC}Milliseconds`]();
|
|
238
270
|
const timestamp = {
|
|
239
271
|
date: padNumber(date[`get${UTC}FullYear`](), 4) +
|
|
240
|
-
|
|
272
|
+
'-' +
|
|
241
273
|
padNumber(date[`get${UTC}Month`]() + 1, 2) +
|
|
242
|
-
|
|
274
|
+
'-' +
|
|
243
275
|
padNumber(date[`get${UTC}Date`](), 2),
|
|
244
276
|
time: padNumber(date[`get${UTC}Hours`]() || 0, 2) +
|
|
245
|
-
|
|
277
|
+
':' +
|
|
246
278
|
padNumber(date[`get${UTC}Minutes`]() || 0, 2),
|
|
247
279
|
year: date[`get${UTC}FullYear`](),
|
|
248
280
|
month: date[`get${UTC}Month`]() + 1,
|
|
@@ -275,6 +307,7 @@ function parseDateByMode(date, utc) {
|
|
|
275
307
|
*
|
|
276
308
|
* @param {Date} date JavaScript Date to convert.
|
|
277
309
|
* @returns {Timestamp} Formatted Timestamp object, or `null` for invalid input.
|
|
310
|
+
* @category parsing
|
|
278
311
|
*/
|
|
279
312
|
export function parseDate(date) {
|
|
280
313
|
return parseDateByMode(date, false);
|
|
@@ -287,6 +320,7 @@ export function parseDate(date) {
|
|
|
287
320
|
*
|
|
288
321
|
* @param {Date} date JavaScript Date to convert.
|
|
289
322
|
* @returns {Timestamp} Formatted Timestamp object, or `null` for invalid input.
|
|
323
|
+
* @category parsing
|
|
290
324
|
*/
|
|
291
325
|
export function parseDateUTC(date) {
|
|
292
326
|
return parseDateByMode(date, true);
|
|
@@ -298,11 +332,12 @@ export function parseDateUTC(date) {
|
|
|
298
332
|
* @param {number} x The number to pad
|
|
299
333
|
* @param {number} length The length of the required number as a string
|
|
300
334
|
* @returns {string} The padded number (as a string). (ie: 5 = '05')
|
|
335
|
+
* @category formatting
|
|
301
336
|
*/
|
|
302
337
|
export function padNumber(x, length) {
|
|
303
338
|
let padded = String(x);
|
|
304
339
|
while (padded.length < length) {
|
|
305
|
-
padded =
|
|
340
|
+
padded = '0' + padded;
|
|
306
341
|
}
|
|
307
342
|
return padded;
|
|
308
343
|
}
|
|
@@ -310,34 +345,35 @@ export function padNumber(x, length) {
|
|
|
310
345
|
* Returns if the passed year is a leap year
|
|
311
346
|
* @param {number} year The year to check (ie: 1999, 2020)
|
|
312
347
|
* @returns {boolean} True if the year is a leap year
|
|
348
|
+
* @category calendar
|
|
313
349
|
*/
|
|
314
350
|
export function isLeapYear(year) {
|
|
315
|
-
|
|
316
|
-
// but not by 100, unless it is also divisible by 400.
|
|
317
|
-
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
351
|
+
return gregorianCalendar.isLeapYear(year);
|
|
318
352
|
}
|
|
319
353
|
/**
|
|
320
354
|
* Returns the days of the specified month in a year
|
|
321
355
|
* @param {number} year The year (ie: 1999, 2020)
|
|
322
356
|
* @param {number} month The Gregorian month number, where January is `1`
|
|
323
357
|
* @returns {number} The number of days in the month (corrected for leap years)
|
|
358
|
+
* @category calendar
|
|
324
359
|
*/
|
|
325
360
|
export function daysInMonth(year, month) {
|
|
326
|
-
return (
|
|
361
|
+
return gregorianCalendar.daysInMonth(year, month);
|
|
327
362
|
}
|
|
328
363
|
/**
|
|
329
364
|
* Returns a new Timestamp for the next calendar day.
|
|
330
365
|
*
|
|
331
366
|
* @param {Timestamp} timestamp Base Timestamp object.
|
|
332
367
|
* @returns {Timestamp} New Timestamp representing the next day.
|
|
368
|
+
* @category arithmetic
|
|
333
369
|
*/
|
|
334
370
|
export function nextDay(timestamp) {
|
|
335
|
-
const date =
|
|
371
|
+
const date = gregorianCalendar.nextDay(toCalendarDateParts(timestamp));
|
|
336
372
|
return updateFormatted(normalizeTimestamp({
|
|
337
373
|
...timestamp,
|
|
338
|
-
year: date.
|
|
339
|
-
month: date.
|
|
340
|
-
day: date.
|
|
374
|
+
year: date.year,
|
|
375
|
+
month: date.month,
|
|
376
|
+
day: date.day,
|
|
341
377
|
}));
|
|
342
378
|
}
|
|
343
379
|
/**
|
|
@@ -345,14 +381,15 @@ export function nextDay(timestamp) {
|
|
|
345
381
|
*
|
|
346
382
|
* @param {Timestamp} timestamp Base Timestamp object.
|
|
347
383
|
* @returns {Timestamp} New Timestamp representing the previous day.
|
|
384
|
+
* @category arithmetic
|
|
348
385
|
*/
|
|
349
386
|
export function prevDay(timestamp) {
|
|
350
|
-
const date =
|
|
387
|
+
const date = gregorianCalendar.prevDay(toCalendarDateParts(timestamp));
|
|
351
388
|
return updateFormatted(normalizeTimestamp({
|
|
352
389
|
...timestamp,
|
|
353
|
-
year: date.
|
|
354
|
-
month: date.
|
|
355
|
-
day: date.
|
|
390
|
+
year: date.year,
|
|
391
|
+
month: date.month,
|
|
392
|
+
day: date.day,
|
|
356
393
|
}));
|
|
357
394
|
}
|
|
358
395
|
/**
|
|
@@ -363,10 +400,11 @@ export function prevDay(timestamp) {
|
|
|
363
400
|
* wants a stable UTC calendar date instead.
|
|
364
401
|
*
|
|
365
402
|
* @returns {string} Date string in the form `YYYY-MM-DD`
|
|
403
|
+
* @category state
|
|
366
404
|
*/
|
|
367
405
|
export function today() {
|
|
368
406
|
const d = new Date(), month = d.getMonth() + 1, day = d.getDate(), year = d.getFullYear();
|
|
369
|
-
return [year, padNumber(month, 2), padNumber(day, 2)].join(
|
|
407
|
+
return [year, padNumber(month, 2), padNumber(day, 2)].join('-');
|
|
370
408
|
}
|
|
371
409
|
/**
|
|
372
410
|
* Returns today's date using UTC calendar fields.
|
|
@@ -377,13 +415,14 @@ export function today() {
|
|
|
377
415
|
*
|
|
378
416
|
* @param {Date} date Date source to read. Defaults to the current Date.
|
|
379
417
|
* @returns {string} UTC date string in the form `YYYY-MM-DD`
|
|
418
|
+
* @category state
|
|
380
419
|
*/
|
|
381
420
|
export function todayUTC(date = new Date()) {
|
|
382
421
|
return [
|
|
383
422
|
padNumber(date.getUTCFullYear(), 4),
|
|
384
423
|
padNumber(date.getUTCMonth() + 1, 2),
|
|
385
424
|
padNumber(date.getUTCDate(), 2),
|
|
386
|
-
].join(
|
|
425
|
+
].join('-');
|
|
387
426
|
}
|
|
388
427
|
/**
|
|
389
428
|
* Returns the current date-time as an immutable Timestamp using UTC fields.
|
|
@@ -394,6 +433,7 @@ export function todayUTC(date = new Date()) {
|
|
|
394
433
|
*
|
|
395
434
|
* @param {Date} date Date source to read. Defaults to the current Date.
|
|
396
435
|
* @returns {Timestamp} Immutable Timestamp built from UTC fields.
|
|
436
|
+
* @category state
|
|
397
437
|
*/
|
|
398
438
|
export function nowUTC(date = new Date()) {
|
|
399
439
|
return parseDateUTC(date);
|
|
@@ -402,6 +442,7 @@ export function nowUTC(date = new Date()) {
|
|
|
402
442
|
* Takes a date string ('YYYY-MM-DD') and validates if it is today's date
|
|
403
443
|
* @param {string} date Date string in the form 'YYYY-MM-DD'
|
|
404
444
|
* @returns {boolean} True if the date is today's date
|
|
445
|
+
* @category comparison
|
|
405
446
|
*/
|
|
406
447
|
export function isToday(date) {
|
|
407
448
|
return date === today();
|
|
@@ -415,6 +456,7 @@ export function isToday(date) {
|
|
|
415
456
|
* @param {string} date Date string in the form `YYYY-MM-DD`.
|
|
416
457
|
* @param {Date} now Date source to read. Defaults to the current Date.
|
|
417
458
|
* @returns {boolean} True when the date matches the UTC date.
|
|
459
|
+
* @category comparison
|
|
418
460
|
*/
|
|
419
461
|
export function isTodayUTC(date, now = new Date()) {
|
|
420
462
|
return date === todayUTC(now);
|
|
@@ -426,6 +468,7 @@ export function isTodayUTC(date, now = new Date()) {
|
|
|
426
468
|
* @param {number[]} weekdays The array is [0,1,2,3,4,5,6] where 0=Sunday and 6=Saturday
|
|
427
469
|
* @param {Timestamp=} today Current timestamp used to update relative information
|
|
428
470
|
* @returns {Timestamp} A new Timestamp representing the start of the week
|
|
471
|
+
* @category ranges
|
|
429
472
|
*/
|
|
430
473
|
export function getStartOfWeek(timestamp, weekdays, today) {
|
|
431
474
|
let start = cloneTimestamp(timestamp);
|
|
@@ -451,6 +494,7 @@ export function getStartOfWeek(timestamp, weekdays, today) {
|
|
|
451
494
|
* @param {number[]} weekdays The array is [0,1,2,3,4,5,6] where 0=Sunday and 6=Saturday
|
|
452
495
|
* @param {Timestamp=} today Current timestamp used to update relative information
|
|
453
496
|
* @returns {Timestamp} A new Timestamp representing the end of the week
|
|
497
|
+
* @category ranges
|
|
454
498
|
*/
|
|
455
499
|
export function getEndOfWeek(timestamp, weekdays, today) {
|
|
456
500
|
let end = cloneTimestamp(timestamp);
|
|
@@ -475,6 +519,7 @@ export function getEndOfWeek(timestamp, weekdays, today) {
|
|
|
475
519
|
* Finds the start of the month based on the passed in Timestamp
|
|
476
520
|
* @param {Timestamp} timestamp The Timestamp to use to find the start of the month
|
|
477
521
|
* @returns {Timestamp} A Timestamp of the start of the month
|
|
522
|
+
* @category ranges
|
|
478
523
|
*/
|
|
479
524
|
export function getStartOfMonth(timestamp) {
|
|
480
525
|
let start = cloneTimestamp(timestamp);
|
|
@@ -486,6 +531,7 @@ export function getStartOfMonth(timestamp) {
|
|
|
486
531
|
* Finds the end of the month based on the passed in Timestamp
|
|
487
532
|
* @param {Timestamp} timestamp The Timestamp to use to find the end of the month
|
|
488
533
|
* @returns {Timestamp} A Timestamp of the end of the month
|
|
534
|
+
* @category ranges
|
|
489
535
|
*/
|
|
490
536
|
export function getEndOfMonth(timestamp) {
|
|
491
537
|
let end = cloneTimestamp(timestamp);
|
|
@@ -501,29 +547,30 @@ export function getEndOfMonth(timestamp) {
|
|
|
501
547
|
*
|
|
502
548
|
* @param input - Minutes since midnight, a time string, or an object with hour and minute fields.
|
|
503
549
|
* @returns Minutes since midnight, or `false` when the input cannot be parsed.
|
|
550
|
+
* @category parsing
|
|
504
551
|
*/
|
|
505
552
|
export function parseTime(input) {
|
|
506
553
|
const type = Object.prototype.toString.call(input);
|
|
507
554
|
switch (type) {
|
|
508
|
-
case
|
|
555
|
+
case '[object Number]':
|
|
509
556
|
// when a number is given, it's minutes since 12:00am
|
|
510
557
|
return input;
|
|
511
|
-
case
|
|
558
|
+
case '[object String]': {
|
|
512
559
|
// when a string is given, it's a hh:mm:ss format where seconds are optional, but not used for minute math
|
|
513
560
|
const parts = PARSE_TIME.exec(input);
|
|
514
561
|
if (!parts) {
|
|
515
562
|
return false;
|
|
516
563
|
}
|
|
517
|
-
return parseInt(parts[1], 10) * 60 + parseInt(parts[2] ||
|
|
564
|
+
return parseInt(parts[1], 10) * 60 + parseInt(parts[2] || '0', 10);
|
|
518
565
|
}
|
|
519
|
-
case
|
|
566
|
+
case '[object Object]':
|
|
520
567
|
// when an object is given, it must have hour and minute
|
|
521
|
-
if (typeof input !==
|
|
522
|
-
typeof input.hour !==
|
|
523
|
-
typeof input.minute !==
|
|
568
|
+
if (typeof input !== 'object' ||
|
|
569
|
+
typeof input.hour !== 'number' ||
|
|
570
|
+
typeof input.minute !== 'number') {
|
|
524
571
|
return false;
|
|
525
572
|
}
|
|
526
|
-
if (typeof input ===
|
|
573
|
+
if (typeof input === 'object' && 'hour' in input && 'minute' in input) {
|
|
527
574
|
return input.hour * 60 + input.minute;
|
|
528
575
|
}
|
|
529
576
|
return false;
|
|
@@ -536,6 +583,7 @@ export function parseTime(input) {
|
|
|
536
583
|
* @param {Timestamp} ts1 First Timestamp object.
|
|
537
584
|
* @param {Timestamp} ts2 Second Timestamp object.
|
|
538
585
|
* @returns {boolean} True when both timestamps match exactly.
|
|
586
|
+
* @category comparison
|
|
539
587
|
*/
|
|
540
588
|
export function compareTimestamps(ts1, ts2) {
|
|
541
589
|
if (!ts1 || !ts2)
|
|
@@ -555,6 +603,7 @@ export function compareTimestamps(ts1, ts2) {
|
|
|
555
603
|
* @param {Timestamp} ts1 First Timestamp object.
|
|
556
604
|
* @param {Timestamp} ts2 Second Timestamp object.
|
|
557
605
|
* @returns {boolean} True when both dates are the same.
|
|
606
|
+
* @category comparison
|
|
558
607
|
*/
|
|
559
608
|
export function compareDate(ts1, ts2) {
|
|
560
609
|
return getDate(ts1) === getDate(ts2);
|
|
@@ -565,6 +614,7 @@ export function compareDate(ts1, ts2) {
|
|
|
565
614
|
* @param {Timestamp} ts1 First Timestamp object.
|
|
566
615
|
* @param {Timestamp} ts2 Second Timestamp object.
|
|
567
616
|
* @returns {boolean} True when both times are the same.
|
|
617
|
+
* @category comparison
|
|
568
618
|
*/
|
|
569
619
|
export function compareTime(ts1, ts2) {
|
|
570
620
|
return getTime(ts1) === getTime(ts2);
|
|
@@ -575,6 +625,7 @@ export function compareTime(ts1, ts2) {
|
|
|
575
625
|
* @param {Timestamp} ts1 First Timestamp object.
|
|
576
626
|
* @param {Timestamp} ts2 Second Timestamp object.
|
|
577
627
|
* @returns {boolean} True when both date-time values are the same.
|
|
628
|
+
* @category comparison
|
|
578
629
|
*/
|
|
579
630
|
export function compareDateTime(ts1, ts2) {
|
|
580
631
|
return getDateTime(ts1) === getDateTime(ts2);
|
|
@@ -588,6 +639,7 @@ export function compareDateTime(ts1, ts2) {
|
|
|
588
639
|
* @param {string} input Date or date-time string, such as `YYYY-MM-DD`, `YYYY-MM-DD HH:mm:ss`, or an ISO-like value with optional milliseconds and timezone suffix.
|
|
589
640
|
* @param {Timestamp} now Optional Timestamp used to calculate relative flags.
|
|
590
641
|
* @returns {Timestamp} Formatted Timestamp object, or `null` when the input cannot be parsed.
|
|
642
|
+
* @category parsing
|
|
591
643
|
*/
|
|
592
644
|
export function parseTimestamp(input, now = null) {
|
|
593
645
|
let timestamp = parsed(input);
|
|
@@ -604,17 +656,418 @@ export function parseTimestamp(input, now = null) {
|
|
|
604
656
|
*
|
|
605
657
|
* @param {Timestamp} timestamp Timestamp object to read.
|
|
606
658
|
* @returns {number} Numeric date identifier.
|
|
659
|
+
* @category conversion
|
|
607
660
|
*/
|
|
608
661
|
export function getDayIdentifier(timestamp) {
|
|
609
662
|
return ((timestamp.year ?? 0) * 100000000 +
|
|
610
663
|
(timestamp.month ?? 0) * 1000000 +
|
|
611
664
|
(timestamp.day ?? 0) * 10000);
|
|
612
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* Converts a Timestamp date into a stable serial day for a calendar system.
|
|
668
|
+
*
|
|
669
|
+
* The default Gregorian value is the number of UTC days since 1970-01-01.
|
|
670
|
+
* Alternate calendar adapters should map their year/month/day fields to the
|
|
671
|
+
* same serial day space so ranges and comparisons can be calendar-agnostic.
|
|
672
|
+
*
|
|
673
|
+
* @param {Timestamp} timestamp Timestamp object to read.
|
|
674
|
+
* @param {CalendarSystem=} calendar Calendar implementation to use.
|
|
675
|
+
* @returns {number} Stable serial day.
|
|
676
|
+
* @category conversion
|
|
677
|
+
*/
|
|
678
|
+
export function getEpochDay(timestamp, calendar = gregorianCalendar) {
|
|
679
|
+
return calendar.toEpochDay(toCalendarDateParts(timestamp));
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Converts a Timestamp date into a stable serial-day identifier for a calendar system.
|
|
683
|
+
*
|
|
684
|
+
* This is an alias-friendly helper for calendar views that need sorted keys,
|
|
685
|
+
* range comparisons, or virtualized day rows across different calendar adapters.
|
|
686
|
+
*
|
|
687
|
+
* @param timestamp Timestamp object to read.
|
|
688
|
+
* @param calendar Calendar implementation to use.
|
|
689
|
+
* @returns Stable serial day.
|
|
690
|
+
* @category calendar
|
|
691
|
+
*/
|
|
692
|
+
export function getCalendarDayIdentifier(timestamp, calendar = gregorianCalendar) {
|
|
693
|
+
return getEpochDay(timestamp, calendar);
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Returns true when calendar date fields are valid for a calendar system.
|
|
697
|
+
*
|
|
698
|
+
* @param date Calendar date fields to validate.
|
|
699
|
+
* @param calendar Calendar implementation to use.
|
|
700
|
+
* @returns True when year, month, and day can exist in the calendar.
|
|
701
|
+
* @category calendar
|
|
702
|
+
*/
|
|
703
|
+
export function isValidCalendarDate(date, calendar = gregorianCalendar) {
|
|
704
|
+
if (date.year < 1)
|
|
705
|
+
return false;
|
|
706
|
+
if (date.month < 1 || date.month > calendar.monthsInYear(date.year))
|
|
707
|
+
return false;
|
|
708
|
+
return date.day >= 1 && date.day <= calendar.daysInMonth(date.year, date.month);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Creates an immutable Timestamp-shaped value from calendar adapter fields.
|
|
712
|
+
*
|
|
713
|
+
* The returned timestamp uses adapter year/month/day fields, is tagged with
|
|
714
|
+
* `calendarId`, and derives weekday/day-of-year values from the adapter.
|
|
715
|
+
*
|
|
716
|
+
* @param date Calendar date fields.
|
|
717
|
+
* @param calendar Calendar implementation to use.
|
|
718
|
+
* @param options Optional time and relative comparison settings.
|
|
719
|
+
* @returns Timestamp-shaped calendar value.
|
|
720
|
+
* @category calendar
|
|
721
|
+
*/
|
|
722
|
+
export function createCalendarTimestamp(date, calendar = gregorianCalendar, options = {}) {
|
|
723
|
+
const timestamp = {
|
|
724
|
+
calendarId: calendar.id,
|
|
725
|
+
date: formatCalendarDate(date),
|
|
726
|
+
hasDay: true,
|
|
727
|
+
year: date.year,
|
|
728
|
+
month: date.month,
|
|
729
|
+
day: date.day,
|
|
730
|
+
hasTime: options.hasTime ?? true,
|
|
731
|
+
hour: options.hour ?? 0,
|
|
732
|
+
minute: options.minute ?? 0,
|
|
733
|
+
past: false,
|
|
734
|
+
current: false,
|
|
735
|
+
future: false,
|
|
736
|
+
disabled: false,
|
|
737
|
+
weekday: calendar.getWeekday(date),
|
|
738
|
+
doy: calendar.getDayOfYear(date),
|
|
739
|
+
workweek: getCalendarWorkWeek(date, calendar),
|
|
740
|
+
};
|
|
741
|
+
if (options.second !== undefined) {
|
|
742
|
+
timestamp.second = options.second;
|
|
743
|
+
}
|
|
744
|
+
if (options.millisecond !== undefined) {
|
|
745
|
+
timestamp.millisecond = options.millisecond;
|
|
746
|
+
}
|
|
747
|
+
if (options.timezone !== undefined) {
|
|
748
|
+
timestamp.timezone = options.timezone;
|
|
749
|
+
}
|
|
750
|
+
timestamp.time = getTime(timestamp);
|
|
751
|
+
const formatted = freezeTimestamp(timestamp);
|
|
752
|
+
return options.now
|
|
753
|
+
? updateCalendarRelative(formatted, options.now, calendar, formatted.hasTime)
|
|
754
|
+
: formatted;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Parses a date or date-time string as calendar adapter fields.
|
|
758
|
+
*
|
|
759
|
+
* Unlike parseTimestamp(), this helper validates the date against the supplied
|
|
760
|
+
* calendar and derives weekday/day-of-year values through the adapter.
|
|
761
|
+
*
|
|
762
|
+
* @param input Date or date-time string.
|
|
763
|
+
* @param calendar Calendar implementation to use.
|
|
764
|
+
* @param now Optional comparison timestamp from the same calendar system.
|
|
765
|
+
* @returns Calendar timestamp, or `null` when the input cannot be parsed or validated.
|
|
766
|
+
* @category calendar
|
|
767
|
+
*/
|
|
768
|
+
export function parseCalendarTimestamp(input, calendar = gregorianCalendar, now = null) {
|
|
769
|
+
const timestamp = parsed(input);
|
|
770
|
+
if (timestamp === null)
|
|
771
|
+
return null;
|
|
772
|
+
const date = toCalendarDateParts(timestamp);
|
|
773
|
+
if (isValidCalendarDate(date, calendar) === false)
|
|
774
|
+
return null;
|
|
775
|
+
return createCalendarTimestamp(date, calendar, {
|
|
776
|
+
...getCalendarTimestampOptions(timestamp),
|
|
777
|
+
now,
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Creates a calendar timestamp from a stable serial day.
|
|
782
|
+
*
|
|
783
|
+
* @param epochDay Stable serial day.
|
|
784
|
+
* @param calendar Calendar implementation to use.
|
|
785
|
+
* @param options Optional time and relative comparison settings.
|
|
786
|
+
* @returns Timestamp-shaped calendar value.
|
|
787
|
+
* @category calendar
|
|
788
|
+
*/
|
|
789
|
+
export function createCalendarTimestampFromEpochDay(epochDay, calendar = gregorianCalendar, options = {}) {
|
|
790
|
+
return createCalendarTimestamp(calendar.fromEpochDay(epochDay), calendar, options);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Updates formatted calendar metadata on a timestamp-shaped value.
|
|
794
|
+
*
|
|
795
|
+
* @param timestamp Timestamp object to transform.
|
|
796
|
+
* @param calendar Calendar implementation to use.
|
|
797
|
+
* @returns Timestamp with adapter date formatting and metadata.
|
|
798
|
+
* @category calendar
|
|
799
|
+
*/
|
|
800
|
+
export function updateCalendarFormatted(timestamp, calendar = gregorianCalendar) {
|
|
801
|
+
return createCalendarTimestamp(toCalendarDateParts(timestamp), calendar, getCalendarTimestampOptions(timestamp));
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Returns a timestamp with relative flags compared through a calendar adapter.
|
|
805
|
+
*
|
|
806
|
+
* @param timestamp Timestamp object to update.
|
|
807
|
+
* @param now Timestamp representing the comparison point in the same calendar.
|
|
808
|
+
* @param calendar Calendar implementation to use.
|
|
809
|
+
* @param time Include time-of-day in the comparison when true.
|
|
810
|
+
* @returns New Timestamp object with relative flags.
|
|
811
|
+
* @category calendar
|
|
812
|
+
*/
|
|
813
|
+
export function updateCalendarRelative(timestamp, now, calendar = gregorianCalendar, time = false) {
|
|
814
|
+
const ts = cloneTimestamp(timestamp);
|
|
815
|
+
let a = getCalendarDayIdentifier(now, calendar);
|
|
816
|
+
let b = getCalendarDayIdentifier(ts, calendar);
|
|
817
|
+
let current = a === b;
|
|
818
|
+
if (ts.hasTime && time && current) {
|
|
819
|
+
a = getTimeComparisonValue(now);
|
|
820
|
+
b = getTimeComparisonValue(ts);
|
|
821
|
+
current = a === b;
|
|
822
|
+
}
|
|
823
|
+
ts.past = b < a;
|
|
824
|
+
ts.current = current;
|
|
825
|
+
ts.future = b > a;
|
|
826
|
+
ts.currentWeekday = ts.weekday === now.weekday;
|
|
827
|
+
return freezeTimestamp(ts);
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Returns a calendar timestamp for the next day.
|
|
831
|
+
*
|
|
832
|
+
* @param timestamp Base timestamp.
|
|
833
|
+
* @param calendar Calendar implementation to use.
|
|
834
|
+
* @returns New timestamp for the next adapter day.
|
|
835
|
+
* @category calendar
|
|
836
|
+
*/
|
|
837
|
+
export function nextCalendarDay(timestamp, calendar = gregorianCalendar) {
|
|
838
|
+
return createCalendarTimestamp(calendar.nextDay(toCalendarDateParts(timestamp)), calendar, getCalendarTimestampOptions(timestamp));
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Returns a calendar timestamp for the previous day.
|
|
842
|
+
*
|
|
843
|
+
* @param timestamp Base timestamp.
|
|
844
|
+
* @param calendar Calendar implementation to use.
|
|
845
|
+
* @returns New timestamp for the previous adapter day.
|
|
846
|
+
* @category calendar
|
|
847
|
+
*/
|
|
848
|
+
export function prevCalendarDay(timestamp, calendar = gregorianCalendar) {
|
|
849
|
+
return createCalendarTimestamp(calendar.prevDay(toCalendarDateParts(timestamp)), calendar, getCalendarTimestampOptions(timestamp));
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Adds a number of days through a calendar adapter.
|
|
853
|
+
*
|
|
854
|
+
* @param timestamp Base timestamp.
|
|
855
|
+
* @param amount Number of days to move.
|
|
856
|
+
* @param calendar Calendar implementation to use.
|
|
857
|
+
* @returns New timestamp after moving.
|
|
858
|
+
* @category calendar
|
|
859
|
+
*/
|
|
860
|
+
export function addCalendarDays(timestamp, amount, calendar = gregorianCalendar) {
|
|
861
|
+
return createCalendarTimestamp(calendar.addDays(toCalendarDateParts(timestamp), amount), calendar, getCalendarTimestampOptions(timestamp));
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Adds a number of months through a calendar adapter and clamps invalid days.
|
|
865
|
+
*
|
|
866
|
+
* @param timestamp Base timestamp.
|
|
867
|
+
* @param amount Number of calendar months to move.
|
|
868
|
+
* @param calendar Calendar implementation to use.
|
|
869
|
+
* @returns New timestamp after moving.
|
|
870
|
+
* @category calendar
|
|
871
|
+
*/
|
|
872
|
+
export function addCalendarMonths(timestamp, amount, calendar = gregorianCalendar) {
|
|
873
|
+
let { year, month, day } = toCalendarDateParts(timestamp);
|
|
874
|
+
let remaining = Math.trunc(amount);
|
|
875
|
+
while (remaining > 0) {
|
|
876
|
+
month += 1;
|
|
877
|
+
if (month > calendar.monthsInYear(year)) {
|
|
878
|
+
year += 1;
|
|
879
|
+
month = 1;
|
|
880
|
+
}
|
|
881
|
+
remaining -= 1;
|
|
882
|
+
}
|
|
883
|
+
while (remaining < 0) {
|
|
884
|
+
month -= 1;
|
|
885
|
+
if (month < 1) {
|
|
886
|
+
year -= 1;
|
|
887
|
+
month = calendar.monthsInYear(year);
|
|
888
|
+
}
|
|
889
|
+
remaining += 1;
|
|
890
|
+
}
|
|
891
|
+
day = Math.min(day, calendar.daysInMonth(year, month));
|
|
892
|
+
return createCalendarTimestamp({ year, month, day }, calendar, getCalendarTimestampOptions(timestamp));
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Adds a number of years through a calendar adapter and clamps invalid days.
|
|
896
|
+
*
|
|
897
|
+
* @param timestamp Base timestamp.
|
|
898
|
+
* @param amount Number of calendar years to move.
|
|
899
|
+
* @param calendar Calendar implementation to use.
|
|
900
|
+
* @returns New timestamp after moving.
|
|
901
|
+
* @category calendar
|
|
902
|
+
*/
|
|
903
|
+
export function addCalendarYears(timestamp, amount, calendar = gregorianCalendar) {
|
|
904
|
+
let { year, month, day } = toCalendarDateParts(timestamp);
|
|
905
|
+
year += Math.trunc(amount);
|
|
906
|
+
month = Math.min(month, calendar.monthsInYear(year));
|
|
907
|
+
day = Math.min(day, calendar.daysInMonth(year, month));
|
|
908
|
+
return createCalendarTimestamp({ year, month, day }, calendar, getCalendarTimestampOptions(timestamp));
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Returns the start of the calendar month for a timestamp.
|
|
912
|
+
*
|
|
913
|
+
* @param timestamp Base timestamp.
|
|
914
|
+
* @param calendar Calendar implementation to use.
|
|
915
|
+
* @returns First day of the adapter month.
|
|
916
|
+
* @category calendar
|
|
917
|
+
*/
|
|
918
|
+
export function getCalendarStartOfMonth(timestamp, calendar = gregorianCalendar) {
|
|
919
|
+
return createCalendarTimestamp({ ...toCalendarDateParts(timestamp), day: 1 }, calendar, getCalendarTimestampOptions(timestamp));
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Returns the end of the calendar month for a timestamp.
|
|
923
|
+
*
|
|
924
|
+
* @param timestamp Base timestamp.
|
|
925
|
+
* @param calendar Calendar implementation to use.
|
|
926
|
+
* @returns Last day of the adapter month.
|
|
927
|
+
* @category calendar
|
|
928
|
+
*/
|
|
929
|
+
export function getCalendarEndOfMonth(timestamp, calendar = gregorianCalendar) {
|
|
930
|
+
const date = toCalendarDateParts(timestamp);
|
|
931
|
+
return createCalendarTimestamp({
|
|
932
|
+
...date,
|
|
933
|
+
day: calendar.daysInMonth(date.year, date.month),
|
|
934
|
+
}, calendar, getCalendarTimestampOptions(timestamp));
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Returns the start of the calendar year for a timestamp.
|
|
938
|
+
*
|
|
939
|
+
* @param timestamp Base timestamp.
|
|
940
|
+
* @param calendar Calendar implementation to use.
|
|
941
|
+
* @returns First day of the adapter year.
|
|
942
|
+
* @category calendar
|
|
943
|
+
*/
|
|
944
|
+
export function getCalendarStartOfYear(timestamp, calendar = gregorianCalendar) {
|
|
945
|
+
return createCalendarTimestamp({ year: timestamp.year, month: 1, day: 1 }, calendar, getCalendarTimestampOptions(timestamp));
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Returns the end of the calendar year for a timestamp.
|
|
949
|
+
*
|
|
950
|
+
* @param timestamp Base timestamp.
|
|
951
|
+
* @param calendar Calendar implementation to use.
|
|
952
|
+
* @returns Last day of the adapter year.
|
|
953
|
+
* @category calendar
|
|
954
|
+
*/
|
|
955
|
+
export function getCalendarEndOfYear(timestamp, calendar = gregorianCalendar) {
|
|
956
|
+
const month = calendar.monthsInYear(timestamp.year);
|
|
957
|
+
return createCalendarTimestamp({ year: timestamp.year, month, day: calendar.daysInMonth(timestamp.year, month) }, calendar, getCalendarTimestampOptions(timestamp));
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Finds the nearest matching weekday through a calendar adapter.
|
|
961
|
+
*
|
|
962
|
+
* @param timestamp Base timestamp.
|
|
963
|
+
* @param weekday Weekday number to find.
|
|
964
|
+
* @param calendar Calendar implementation to use.
|
|
965
|
+
* @param direction Direction to search.
|
|
966
|
+
* @param maxDays Maximum days to inspect.
|
|
967
|
+
* @returns Matching timestamp, or the last inspected timestamp.
|
|
968
|
+
* @category calendar
|
|
969
|
+
*/
|
|
970
|
+
export function findCalendarWeekday(timestamp, weekday, calendar = gregorianCalendar, direction = 'next', maxDays = 6) {
|
|
971
|
+
let ts = copyTimestamp(timestamp);
|
|
972
|
+
const mover = direction === 'next' ? nextCalendarDay : prevCalendarDay;
|
|
973
|
+
while (ts.weekday !== weekday && --maxDays >= 0) {
|
|
974
|
+
ts = mover(ts, calendar);
|
|
975
|
+
}
|
|
976
|
+
return ts;
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Returns the start of a calendar week for a timestamp and weekday set.
|
|
980
|
+
*
|
|
981
|
+
* @param timestamp Base timestamp.
|
|
982
|
+
* @param weekdays Weekday numbers to include, from `0` Sunday to `6` Saturday.
|
|
983
|
+
* @param calendar Calendar implementation to use.
|
|
984
|
+
* @param now Optional comparison timestamp from the same calendar.
|
|
985
|
+
* @returns Start of the adapter week.
|
|
986
|
+
* @category calendar
|
|
987
|
+
*/
|
|
988
|
+
export function getCalendarStartOfWeek(timestamp, weekdays, calendar = gregorianCalendar, now = null) {
|
|
989
|
+
let start = updateCalendarFormatted(timestamp, calendar);
|
|
990
|
+
if (!weekdays || !Array.isArray(weekdays)) {
|
|
991
|
+
return start;
|
|
992
|
+
}
|
|
993
|
+
if (start.day === 1 || start.weekday === 0) {
|
|
994
|
+
while (!weekdays.includes(Number(start.weekday))) {
|
|
995
|
+
start = nextCalendarDay(start, calendar);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
start = findCalendarWeekday(start, weekdays[0], calendar, 'prev');
|
|
999
|
+
return now ? updateCalendarRelative(start, now, calendar, start.hasTime) : start;
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Returns the end of a calendar week for a timestamp and weekday set.
|
|
1003
|
+
*
|
|
1004
|
+
* @param timestamp Base timestamp.
|
|
1005
|
+
* @param weekdays Weekday numbers to include, from `0` Sunday to `6` Saturday.
|
|
1006
|
+
* @param calendar Calendar implementation to use.
|
|
1007
|
+
* @param now Optional comparison timestamp from the same calendar.
|
|
1008
|
+
* @returns End of the adapter week.
|
|
1009
|
+
* @category calendar
|
|
1010
|
+
*/
|
|
1011
|
+
export function getCalendarEndOfWeek(timestamp, weekdays, calendar = gregorianCalendar, now = null) {
|
|
1012
|
+
let end = updateCalendarFormatted(timestamp, calendar);
|
|
1013
|
+
if (!weekdays || !Array.isArray(weekdays)) {
|
|
1014
|
+
return end;
|
|
1015
|
+
}
|
|
1016
|
+
const lastDay = calendar.daysInMonth(end.year, end.month);
|
|
1017
|
+
if (lastDay === end.day || end.weekday === weekdays[weekdays.length - 1]) {
|
|
1018
|
+
while (!weekdays.includes(Number(end.weekday))) {
|
|
1019
|
+
end = prevCalendarDay(end, calendar);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
end = findCalendarWeekday(end, weekdays[weekdays.length - 1], calendar, 'next');
|
|
1023
|
+
return now ? updateCalendarRelative(end, now, calendar, end.hasTime) : end;
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Creates an inclusive list of calendar adapter days between start and end.
|
|
1027
|
+
*
|
|
1028
|
+
* @param start First day in the list.
|
|
1029
|
+
* @param end Last day boundary for the list.
|
|
1030
|
+
* @param now Timestamp used to calculate relative flags.
|
|
1031
|
+
* @param calendar Calendar implementation to use.
|
|
1032
|
+
* @param options Optional weekday, disabled, and size filters.
|
|
1033
|
+
* @returns Timestamp days for the adapter calendar.
|
|
1034
|
+
* @category calendar
|
|
1035
|
+
*/
|
|
1036
|
+
export function createCalendarDayList(start, end, now, calendar = gregorianCalendar, options = {}) {
|
|
1037
|
+
const { weekdays = [0, 1, 2, 3, 4, 5, 6], disabledBefore, disabledAfter, disabledWeekdays = [], disabledDays = [], max = 42, min = 0, } = options;
|
|
1038
|
+
const begin = getCalendarDayIdentifier(start, calendar);
|
|
1039
|
+
const stop = getCalendarDayIdentifier(end, calendar);
|
|
1040
|
+
const days = [];
|
|
1041
|
+
let current = updateCalendarFormatted(start, calendar);
|
|
1042
|
+
let currentIdentifier = 0;
|
|
1043
|
+
let stopped = currentIdentifier === stop;
|
|
1044
|
+
if (stop < begin) {
|
|
1045
|
+
return days;
|
|
1046
|
+
}
|
|
1047
|
+
while ((!stopped || days.length < min) && days.length < max) {
|
|
1048
|
+
currentIdentifier = getCalendarDayIdentifier(current, calendar);
|
|
1049
|
+
stopped = stopped || (currentIdentifier > stop && days.length >= min);
|
|
1050
|
+
if (stopped) {
|
|
1051
|
+
break;
|
|
1052
|
+
}
|
|
1053
|
+
if (!weekdays.includes(Number(current.weekday))) {
|
|
1054
|
+
current = nextCalendarDay(current, calendar);
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
let day = updateCalendarFormatted(current, calendar);
|
|
1058
|
+
day = updateCalendarRelative(day, now, calendar);
|
|
1059
|
+
day = updateDisabled(day, disabledBefore, disabledAfter, disabledWeekdays, disabledDays);
|
|
1060
|
+
days.push(day);
|
|
1061
|
+
current = nextCalendarDay(current, calendar);
|
|
1062
|
+
}
|
|
1063
|
+
return days;
|
|
1064
|
+
}
|
|
613
1065
|
/**
|
|
614
1066
|
* Converts a Timestamp time into a sortable numeric identifier.
|
|
615
1067
|
*
|
|
616
1068
|
* @param {Timestamp} timestamp Timestamp object to read.
|
|
617
1069
|
* @returns {number} Numeric time identifier.
|
|
1070
|
+
* @category conversion
|
|
618
1071
|
*/
|
|
619
1072
|
export function getTimeIdentifier(timestamp) {
|
|
620
1073
|
return (timestamp.hour ?? 0) * 100 + (timestamp.minute ?? 0);
|
|
@@ -631,6 +1084,7 @@ function getTimeComparisonValue(timestamp) {
|
|
|
631
1084
|
*
|
|
632
1085
|
* @param {Timestamp} timestamp Timestamp object to read.
|
|
633
1086
|
* @returns {number} Numeric date-time identifier.
|
|
1087
|
+
* @category conversion
|
|
634
1088
|
*/
|
|
635
1089
|
export function getDayTimeIdentifier(timestamp) {
|
|
636
1090
|
return getDayIdentifier(timestamp) + getTimeIdentifier(timestamp);
|
|
@@ -641,6 +1095,7 @@ export function getDayTimeIdentifier(timestamp) {
|
|
|
641
1095
|
* @param {Timestamp} ts2 The second Timestamp
|
|
642
1096
|
* @param {boolean=} strict Optional flag to not to return negative numbers
|
|
643
1097
|
* @returns {number} The difference
|
|
1098
|
+
* @category arithmetic
|
|
644
1099
|
*/
|
|
645
1100
|
export function diffTimestamp(ts1, ts2, strict = false) {
|
|
646
1101
|
const utc1 = Date.UTC(ts1.year ?? 0, (ts1.month ?? 1) - 1, ts1.day ?? 1, ts1.hour ?? 0, ts1.minute ?? 0, ts1.second ?? 0, ts1.millisecond ?? 0);
|
|
@@ -663,6 +1118,7 @@ export function diffTimestamp(ts1, ts2, strict = false) {
|
|
|
663
1118
|
* @param {Timestamp} now Timestamp representing the comparison point.
|
|
664
1119
|
* @param {boolean=} time Include time-of-day in the comparison when true.
|
|
665
1120
|
* @returns {Timestamp} New Timestamp object with relative flags.
|
|
1121
|
+
* @category state
|
|
666
1122
|
*/
|
|
667
1123
|
export function updateRelative(timestamp, now, time = false) {
|
|
668
1124
|
const ts = cloneTimestamp(timestamp);
|
|
@@ -690,6 +1146,7 @@ export function updateRelative(timestamp, now, time = false) {
|
|
|
690
1146
|
* @param {number} minutes The number of minutes to set from midnight
|
|
691
1147
|
* @param {Timestamp=} now Optional Timestamp representing current date and time
|
|
692
1148
|
* @returns {Timestamp} A new Timestamp
|
|
1149
|
+
* @category arithmetic
|
|
693
1150
|
*/
|
|
694
1151
|
export function updateMinutes(timestamp, minutes, now = null) {
|
|
695
1152
|
let ts = cloneTimestamp(timestamp);
|
|
@@ -708,6 +1165,7 @@ export function updateMinutes(timestamp, minutes, now = null) {
|
|
|
708
1165
|
* Updates the Timestamp with the weekday
|
|
709
1166
|
* @param {Timestamp} timestamp The Timestamp to transform
|
|
710
1167
|
* @returns A new Timestamp
|
|
1168
|
+
* @category formatting
|
|
711
1169
|
*/
|
|
712
1170
|
export function updateWeekday(timestamp) {
|
|
713
1171
|
const ts = cloneTimestamp(timestamp);
|
|
@@ -718,6 +1176,7 @@ export function updateWeekday(timestamp) {
|
|
|
718
1176
|
* Updates the Timestamp with the day of the year (doy)
|
|
719
1177
|
* @param {Timestamp} timestamp The Timestamp to transform
|
|
720
1178
|
* @returns A new Timestamp
|
|
1179
|
+
* @category formatting
|
|
721
1180
|
*/
|
|
722
1181
|
export function updateDayOfYear(timestamp) {
|
|
723
1182
|
const ts = cloneTimestamp(timestamp);
|
|
@@ -728,6 +1187,7 @@ export function updateDayOfYear(timestamp) {
|
|
|
728
1187
|
* Updates the Timestamp with the workweek
|
|
729
1188
|
* @param {Timestamp} timestamp The Timestamp to transform
|
|
730
1189
|
* @returns A new Timestamp
|
|
1190
|
+
* @category formatting
|
|
731
1191
|
*/
|
|
732
1192
|
export function updateWorkWeek(timestamp) {
|
|
733
1193
|
const ts = cloneTimestamp(timestamp);
|
|
@@ -735,7 +1195,7 @@ export function updateWorkWeek(timestamp) {
|
|
|
735
1195
|
return freezeTimestamp(ts);
|
|
736
1196
|
}
|
|
737
1197
|
function isDisabledDayConfig(value) {
|
|
738
|
-
return typeof value ===
|
|
1198
|
+
return typeof value === 'object' && value !== null && Array.isArray(value) === false;
|
|
739
1199
|
}
|
|
740
1200
|
function applyDisabledDayConfig(timestamp, config) {
|
|
741
1201
|
timestamp.disabled = true;
|
|
@@ -787,6 +1247,7 @@ function isTimestampInDisabledDay(timestamp, day) {
|
|
|
787
1247
|
* @param {number[]} [disabledWeekdays] An array of numbers representing weekdays [0 = Sun, ..., 6 = Sat]
|
|
788
1248
|
* @param {DisabledDays} [disabledDays] An array of days in 'YYYY-MM-DD' format. If an array with a pair of dates is in first array, then this is treated as a range. Object entries can include date/from/to plus color metadata.
|
|
789
1249
|
* @returns A new Timestamp
|
|
1250
|
+
* @category state
|
|
790
1251
|
*/
|
|
791
1252
|
export function updateDisabled(timestamp, disabledBefore, disabledAfter, disabledWeekdays, disabledDays) {
|
|
792
1253
|
let ts = cloneTimestamp(timestamp);
|
|
@@ -831,6 +1292,7 @@ export function updateDisabled(timestamp, disabledBefore, disabledAfter, disable
|
|
|
831
1292
|
* Updates the passed Timestamp with formatted data (time string, date string, weekday, day of year and workweek)
|
|
832
1293
|
* @param {Timestamp} timestamp The Timestamp to transform
|
|
833
1294
|
* @returns A new Timestamp
|
|
1295
|
+
* @category formatting
|
|
834
1296
|
*/
|
|
835
1297
|
export function updateFormatted(timestamp) {
|
|
836
1298
|
const ts = cloneTimestamp(timestamp);
|
|
@@ -846,21 +1308,18 @@ export function updateFormatted(timestamp) {
|
|
|
846
1308
|
* Returns day of the year (doy) for the passed in Timestamp
|
|
847
1309
|
* @param {Timestamp} timestamp The Timestamp to use
|
|
848
1310
|
* @returns {number} The day of the year
|
|
1311
|
+
* @category formatting
|
|
849
1312
|
*/
|
|
850
1313
|
export function getDayOfYear(timestamp) {
|
|
851
1314
|
if (timestamp.year === 0)
|
|
852
1315
|
return;
|
|
853
|
-
return ((
|
|
854
|
-
Date.UTC(timestamp.year, 0, 0)) /
|
|
855
|
-
24 /
|
|
856
|
-
60 /
|
|
857
|
-
60 /
|
|
858
|
-
1000);
|
|
1316
|
+
return gregorianCalendar.getDayOfYear(toCalendarDateParts(timestamp));
|
|
859
1317
|
}
|
|
860
1318
|
/**
|
|
861
1319
|
* Returns workweek for the passed in Timestamp
|
|
862
1320
|
* @param {Timestamp} timestamp The Timestamp to use
|
|
863
1321
|
* @returns {number} The work week
|
|
1322
|
+
* @category formatting
|
|
864
1323
|
*/
|
|
865
1324
|
export function getWorkWeek(timestamp) {
|
|
866
1325
|
let ts = timestamp;
|
|
@@ -888,25 +1347,12 @@ export function getWorkWeek(timestamp) {
|
|
|
888
1347
|
* Returns weekday for the passed in Timestamp
|
|
889
1348
|
* @param {Timestamp} timestamp The Timestamp to use
|
|
890
1349
|
* @returns {number} The weekday
|
|
1350
|
+
* @category formatting
|
|
891
1351
|
*/
|
|
892
1352
|
export function getWeekday(timestamp) {
|
|
893
1353
|
let weekday = timestamp.weekday;
|
|
894
1354
|
if (timestamp.hasDay) {
|
|
895
|
-
|
|
896
|
-
const day = timestamp.day;
|
|
897
|
-
const month = ((timestamp.month + 9) % MONTH_MAX) + 1;
|
|
898
|
-
const century = floor(timestamp.year / 100);
|
|
899
|
-
const year = (timestamp.year % 100) - (timestamp.month <= 2 ? 1 : 0);
|
|
900
|
-
weekday =
|
|
901
|
-
(((day +
|
|
902
|
-
floor(2.6 * month - 0.2) -
|
|
903
|
-
2 * century +
|
|
904
|
-
year +
|
|
905
|
-
floor(year / 4) +
|
|
906
|
-
floor(century / 4)) %
|
|
907
|
-
7) +
|
|
908
|
-
7) %
|
|
909
|
-
7;
|
|
1355
|
+
weekday = gregorianCalendar.getWeekday(toCalendarDateParts(timestamp));
|
|
910
1356
|
}
|
|
911
1357
|
return weekday ?? 0;
|
|
912
1358
|
}
|
|
@@ -915,6 +1361,7 @@ export function getWeekday(timestamp) {
|
|
|
915
1361
|
*
|
|
916
1362
|
* @param {Timestamp} timestamp Timestamp object to copy.
|
|
917
1363
|
* @returns {Timestamp} Frozen Timestamp copy.
|
|
1364
|
+
* @category state
|
|
918
1365
|
*/
|
|
919
1366
|
export function copyTimestamp(timestamp) {
|
|
920
1367
|
return freezeTimestamp(timestamp);
|
|
@@ -943,6 +1390,7 @@ function setTimeParts(timestamp, hour, minute, second, millisecond) {
|
|
|
943
1390
|
*
|
|
944
1391
|
* @param {Timestamp} timestamp Timestamp object to transform.
|
|
945
1392
|
* @returns {Timestamp} New Timestamp at `00:00`.
|
|
1393
|
+
* @category ranges
|
|
946
1394
|
*/
|
|
947
1395
|
export function getStartOfDay(timestamp) {
|
|
948
1396
|
return setTimeParts(timestamp, 0, 0);
|
|
@@ -952,6 +1400,7 @@ export function getStartOfDay(timestamp) {
|
|
|
952
1400
|
*
|
|
953
1401
|
* @param {Timestamp} timestamp Timestamp object to transform.
|
|
954
1402
|
* @returns {Timestamp} New Timestamp at `23:59:59.999`.
|
|
1403
|
+
* @category ranges
|
|
955
1404
|
*/
|
|
956
1405
|
export function getEndOfDay(timestamp) {
|
|
957
1406
|
return setTimeParts(timestamp, 23, 59, 59, 999);
|
|
@@ -961,6 +1410,7 @@ export function getEndOfDay(timestamp) {
|
|
|
961
1410
|
*
|
|
962
1411
|
* @param {Timestamp} timestamp Timestamp object to transform.
|
|
963
1412
|
* @returns {Timestamp} New Timestamp for January 1 at `00:00`.
|
|
1413
|
+
* @category ranges
|
|
964
1414
|
*/
|
|
965
1415
|
export function getStartOfYear(timestamp) {
|
|
966
1416
|
const ts = cloneTimestamp(timestamp);
|
|
@@ -973,6 +1423,7 @@ export function getStartOfYear(timestamp) {
|
|
|
973
1423
|
*
|
|
974
1424
|
* @param {Timestamp} timestamp Timestamp object to transform.
|
|
975
1425
|
* @returns {Timestamp} New Timestamp for December 31 at `23:59:59.999`.
|
|
1426
|
+
* @category ranges
|
|
976
1427
|
*/
|
|
977
1428
|
export function getEndOfYear(timestamp) {
|
|
978
1429
|
const ts = cloneTimestamp(timestamp);
|
|
@@ -985,6 +1436,7 @@ export function getEndOfYear(timestamp) {
|
|
|
985
1436
|
*
|
|
986
1437
|
* @param {Timestamp} timestamp Timestamp object to format.
|
|
987
1438
|
* @returns {string} Date string such as `YYYY-MM-DD`.
|
|
1439
|
+
* @category conversion
|
|
988
1440
|
*/
|
|
989
1441
|
export function getDate(timestamp) {
|
|
990
1442
|
let str = `${padNumber(timestamp.year, 4)}-${padNumber(timestamp.month, 2)}`;
|
|
@@ -1000,10 +1452,11 @@ export function getDate(timestamp) {
|
|
|
1000
1452
|
*
|
|
1001
1453
|
* @param {Timestamp} timestamp Timestamp object to format.
|
|
1002
1454
|
* @returns {string} Time string, or an empty string when the timestamp has no time.
|
|
1455
|
+
* @category conversion
|
|
1003
1456
|
*/
|
|
1004
1457
|
export function getTime(timestamp) {
|
|
1005
1458
|
if (!timestamp.hasTime) {
|
|
1006
|
-
return
|
|
1459
|
+
return '';
|
|
1007
1460
|
}
|
|
1008
1461
|
let time = `${padNumber(timestamp.hour, 2)}:${padNumber(timestamp.minute, 2)}`;
|
|
1009
1462
|
if (timestamp.second !== undefined || timestamp.millisecond !== undefined) {
|
|
@@ -1019,9 +1472,10 @@ export function getTime(timestamp) {
|
|
|
1019
1472
|
*
|
|
1020
1473
|
* @param {Timestamp} timestamp Timestamp object to format.
|
|
1021
1474
|
* @returns {string} Date-time string such as `YYYY-MM-DD HH:mm`.
|
|
1475
|
+
* @category conversion
|
|
1022
1476
|
*/
|
|
1023
1477
|
export function getDateTime(timestamp) {
|
|
1024
|
-
return getDate(timestamp) +
|
|
1478
|
+
return getDate(timestamp) + ' ' + (timestamp.hasTime ? getTime(timestamp) : '00:00');
|
|
1025
1479
|
}
|
|
1026
1480
|
/**
|
|
1027
1481
|
* Alias for relativeDays.
|
|
@@ -1030,6 +1484,7 @@ export function getDateTime(timestamp) {
|
|
|
1030
1484
|
* @param {number} [days=1] The number of days to move.
|
|
1031
1485
|
* @param {number[]} [allowedWeekdays=[ 0, 1, 2, 3, 4, 5, 6 ]] An array of numbers representing the weekdays. ie: [0 = Sun, ..., 6 = Sat].
|
|
1032
1486
|
* @returns A new Timestamp
|
|
1487
|
+
* @category ranges
|
|
1033
1488
|
*/
|
|
1034
1489
|
export function moveRelativeDays(timestamp, mover = nextDay, days = 1, allowedWeekdays = [0, 1, 2, 3, 4, 5, 6]) {
|
|
1035
1490
|
return relativeDays(timestamp, mover, days, allowedWeekdays);
|
|
@@ -1041,6 +1496,7 @@ export function moveRelativeDays(timestamp, mover = nextDay, days = 1, allowedWe
|
|
|
1041
1496
|
* @param {number} [days=1] The number of days to move.
|
|
1042
1497
|
* @param {number[]} [allowedWeekdays=[ 0, 1, 2, 3, 4, 5, 6 ]] An array of numbers representing the weekdays. ie: [0 = Sun, ..., 6 = Sat].
|
|
1043
1498
|
* @returns A new Timestamp
|
|
1499
|
+
* @category ranges
|
|
1044
1500
|
*/
|
|
1045
1501
|
export function relativeDays(timestamp, mover = nextDay, days = 1, allowedWeekdays = [0, 1, 2, 3, 4, 5, 6]) {
|
|
1046
1502
|
let ts = copyTimestamp(timestamp);
|
|
@@ -1062,6 +1518,7 @@ export function relativeDays(timestamp, mover = nextDay, days = 1, allowedWeekda
|
|
|
1062
1518
|
* @param {function} [mover=nextDay] The function to use ({prevDay} or {nextDay}).
|
|
1063
1519
|
* @param {number} [maxDays=6] The number of days to look forward or back.
|
|
1064
1520
|
* @returns A new Timestamp
|
|
1521
|
+
* @category ranges
|
|
1065
1522
|
*/
|
|
1066
1523
|
export function findWeekday(timestamp, weekday, mover = nextDay, maxDays = 6) {
|
|
1067
1524
|
let ts = copyTimestamp(timestamp);
|
|
@@ -1086,6 +1543,7 @@ export function findWeekday(timestamp, weekday, mover = nextDay, maxDays = 6) {
|
|
|
1086
1543
|
* @param {number} [max=42] Maximum number of days to return.
|
|
1087
1544
|
* @param {number} [min=0] Minimum number of days to return.
|
|
1088
1545
|
* @returns {Timestamp[]} Timestamp days.
|
|
1546
|
+
* @category ranges
|
|
1089
1547
|
*/
|
|
1090
1548
|
export function createDayList(start, end, now, weekdays = [0, 1, 2, 3, 4, 5, 6], disabledBefore = undefined, disabledAfter = undefined, disabledWeekdays = [], disabledDays = [], max = 42, min = 0) {
|
|
1091
1549
|
const begin = getDayIdentifier(start);
|
|
@@ -1125,6 +1583,7 @@ export function createDayList(start, end, now, weekdays = [0, 1, 2, 3, 4, 5, 6],
|
|
|
1125
1583
|
* @param {number} count Number of intervals to create.
|
|
1126
1584
|
* @param {Timestamp} now Timestamp used to calculate relative flags.
|
|
1127
1585
|
* @returns {Timestamp[]} Interval Timestamp objects.
|
|
1586
|
+
* @category ranges
|
|
1128
1587
|
*/
|
|
1129
1588
|
export function createIntervalList(timestamp, first, minutes, count, now) {
|
|
1130
1589
|
const intervals = [];
|
|
@@ -1147,8 +1606,8 @@ export function createIntervalList(timestamp, first, minutes, count, now) {
|
|
|
1147
1606
|
* @returns {string} The localized string of the formatted Timestamp
|
|
1148
1607
|
*/
|
|
1149
1608
|
function createNativeLocaleFormatterByMode(locale, cb, toDate) {
|
|
1150
|
-
const emptyFormatter = () =>
|
|
1151
|
-
if (typeof Intl ===
|
|
1609
|
+
const emptyFormatter = () => '';
|
|
1610
|
+
if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat === 'undefined') {
|
|
1152
1611
|
return emptyFormatter;
|
|
1153
1612
|
}
|
|
1154
1613
|
return (timestamp, short) => {
|
|
@@ -1158,7 +1617,7 @@ function createNativeLocaleFormatterByMode(locale, cb, toDate) {
|
|
|
1158
1617
|
}
|
|
1159
1618
|
catch (e) {
|
|
1160
1619
|
console.error(`Intl.DateTimeFormat: ${e.message} -> ${getDateTime(timestamp)}`);
|
|
1161
|
-
return
|
|
1620
|
+
return '';
|
|
1162
1621
|
}
|
|
1163
1622
|
};
|
|
1164
1623
|
}
|
|
@@ -1175,6 +1634,7 @@ function createNativeLocaleFormatterByMode(locale, cb, toDate) {
|
|
|
1175
1634
|
* @param {string} locale The locale to use (ie: en-US)
|
|
1176
1635
|
* @param {getOptions} cb The function to call for options. This function should return an Intl formatted object. The function is passed (timestamp, short).
|
|
1177
1636
|
* @returns {formatter} The function has params (timestamp, short). The short is to use the short options.
|
|
1637
|
+
* @category locale
|
|
1178
1638
|
*/
|
|
1179
1639
|
export function createNativeLocaleFormatter(locale, cb) {
|
|
1180
1640
|
return createNativeLocaleFormatterByMode(locale, cb, makeDateTime);
|
|
@@ -1189,15 +1649,50 @@ export function createNativeLocaleFormatter(locale, cb) {
|
|
|
1189
1649
|
* @param {string} locale The locale to use (ie: en-US)
|
|
1190
1650
|
* @param {getOptions} cb The function to call for options. This function should return an Intl formatted object. The function is passed (timestamp, short).
|
|
1191
1651
|
* @returns {formatter} The function has params (timestamp, short). The short is to use the short options.
|
|
1652
|
+
* @category locale
|
|
1192
1653
|
*/
|
|
1193
1654
|
export function createNativeLocaleFormatterUTC(locale, cb) {
|
|
1194
1655
|
return createNativeLocaleFormatterByMode(locale, cb, makeDateTimeUTC);
|
|
1195
1656
|
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Returns a UTC locale formatter for a calendar adapter.
|
|
1659
|
+
*
|
|
1660
|
+
* The formatter converts adapter date fields through the adapter's serial day
|
|
1661
|
+
* before constructing the native Date. When the adapter has an Intl calendar id,
|
|
1662
|
+
* it is supplied to Intl.DateTimeFormat unless the caller already provided a
|
|
1663
|
+
* `calendar` option. The helper supplies `timeZone: "UTC"` unless the caller
|
|
1664
|
+
* provides a different timezone.
|
|
1665
|
+
*
|
|
1666
|
+
* @param calendar Calendar implementation to use.
|
|
1667
|
+
* @param locale The locale to use.
|
|
1668
|
+
* @param cb Callback that returns Intl formatting options.
|
|
1669
|
+
* @returns Function that formats a calendar timestamp.
|
|
1670
|
+
* @category calendar
|
|
1671
|
+
*/
|
|
1672
|
+
export function createCalendarLocaleFormatterUTC(calendar, locale, cb) {
|
|
1673
|
+
return createNativeLocaleFormatterByMode(locale, (timestamp, short) => {
|
|
1674
|
+
const options = cb(timestamp, short);
|
|
1675
|
+
const resolvedOptions = options.timeZone === undefined
|
|
1676
|
+
? {
|
|
1677
|
+
...options,
|
|
1678
|
+
timeZone: 'UTC',
|
|
1679
|
+
}
|
|
1680
|
+
: options;
|
|
1681
|
+
if (calendar.intlCalendar === undefined || resolvedOptions.calendar !== undefined) {
|
|
1682
|
+
return resolvedOptions;
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
...resolvedOptions,
|
|
1686
|
+
calendar: calendar.intlCalendar,
|
|
1687
|
+
};
|
|
1688
|
+
}, (timestamp) => makeCalendarDateTimeUTC(timestamp, calendar));
|
|
1689
|
+
}
|
|
1196
1690
|
/**
|
|
1197
1691
|
* Converts a Timestamp date into a host-local JavaScript Date.
|
|
1198
1692
|
*
|
|
1199
1693
|
* @param {Timestamp} timestamp Timestamp object to convert.
|
|
1200
1694
|
* @returns {Date} Host-local JavaScript Date object.
|
|
1695
|
+
* @category conversion
|
|
1201
1696
|
*/
|
|
1202
1697
|
export function makeDate(timestamp) {
|
|
1203
1698
|
return new Date(timestamp.year, timestamp.month - 1, timestamp.day, 0, 0);
|
|
@@ -1207,24 +1702,54 @@ export function makeDate(timestamp) {
|
|
|
1207
1702
|
*
|
|
1208
1703
|
* @param {Timestamp} timestamp Timestamp object to convert.
|
|
1209
1704
|
* @returns {Date} JavaScript Date object built with `Date.UTC()`.
|
|
1705
|
+
* @category conversion
|
|
1210
1706
|
*/
|
|
1211
1707
|
export function makeDateUTC(timestamp) {
|
|
1212
1708
|
return new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day, 0, 0));
|
|
1213
1709
|
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Converts an adapter calendar timestamp date into a UTC JavaScript Date.
|
|
1712
|
+
*
|
|
1713
|
+
* The native Date always represents the equivalent Gregorian civil day so Intl
|
|
1714
|
+
* can format the adapter date correctly when paired with a calendar option.
|
|
1715
|
+
*
|
|
1716
|
+
* @param timestamp Calendar timestamp object to convert.
|
|
1717
|
+
* @param calendar Calendar implementation to use.
|
|
1718
|
+
* @returns JavaScript Date object built with `Date.UTC()`.
|
|
1719
|
+
* @category calendar
|
|
1720
|
+
*/
|
|
1721
|
+
export function makeCalendarDateUTC(timestamp, calendar = gregorianCalendar) {
|
|
1722
|
+
const date = gregorianCalendar.fromEpochDay(getCalendarDayIdentifier(timestamp, calendar));
|
|
1723
|
+
return new Date(Date.UTC(date.year, date.month - 1, date.day, 0, 0));
|
|
1724
|
+
}
|
|
1214
1725
|
/**
|
|
1215
1726
|
* Converts a Timestamp date and time into a host-local JavaScript Date.
|
|
1216
1727
|
*
|
|
1217
1728
|
* @param {Timestamp} timestamp Timestamp object to convert.
|
|
1218
1729
|
* @returns {Date} Host-local JavaScript Date object.
|
|
1730
|
+
* @category conversion
|
|
1219
1731
|
*/
|
|
1220
1732
|
export function makeDateTime(timestamp) {
|
|
1221
1733
|
return new Date(timestamp.year, timestamp.month - 1, timestamp.day, timestamp.hour, timestamp.minute, timestamp.second ?? 0, timestamp.millisecond ?? 0);
|
|
1222
1734
|
}
|
|
1735
|
+
/**
|
|
1736
|
+
* Converts an adapter calendar timestamp date-time into a UTC JavaScript Date.
|
|
1737
|
+
*
|
|
1738
|
+
* @param timestamp Calendar timestamp object to convert.
|
|
1739
|
+
* @param calendar Calendar implementation to use.
|
|
1740
|
+
* @returns JavaScript Date object built with `Date.UTC()`.
|
|
1741
|
+
* @category calendar
|
|
1742
|
+
*/
|
|
1743
|
+
export function makeCalendarDateTimeUTC(timestamp, calendar = gregorianCalendar) {
|
|
1744
|
+
const date = gregorianCalendar.fromEpochDay(getCalendarDayIdentifier(timestamp, calendar));
|
|
1745
|
+
return new Date(Date.UTC(date.year, date.month - 1, date.day, timestamp.hour, timestamp.minute, timestamp.second ?? 0, timestamp.millisecond ?? 0));
|
|
1746
|
+
}
|
|
1223
1747
|
/**
|
|
1224
1748
|
* Converts a Timestamp date and time into a UTC JavaScript Date.
|
|
1225
1749
|
*
|
|
1226
1750
|
* @param {Timestamp} timestamp Timestamp object to convert.
|
|
1227
1751
|
* @returns {Date} JavaScript Date object built with `Date.UTC()`.
|
|
1752
|
+
* @category conversion
|
|
1228
1753
|
*/
|
|
1229
1754
|
export function makeDateTimeUTC(timestamp) {
|
|
1230
1755
|
return new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day, timestamp.hour, timestamp.minute, timestamp.second ?? 0, timestamp.millisecond ?? 0));
|
|
@@ -1237,6 +1762,7 @@ export function makeDateTimeUTC(timestamp) {
|
|
|
1237
1762
|
*
|
|
1238
1763
|
* @param {Timestamp} timestamp Timestamp object to convert.
|
|
1239
1764
|
* @returns {number} Unix milliseconds.
|
|
1765
|
+
* @category conversion
|
|
1240
1766
|
*/
|
|
1241
1767
|
export function toUnixMilliseconds(timestamp) {
|
|
1242
1768
|
return makeDateTimeUTC(timestamp).getTime();
|
|
@@ -1248,6 +1774,7 @@ export function toUnixMilliseconds(timestamp) {
|
|
|
1248
1774
|
*
|
|
1249
1775
|
* @param {Timestamp} timestamp Timestamp object to convert.
|
|
1250
1776
|
* @returns {number} Unix seconds.
|
|
1777
|
+
* @category conversion
|
|
1251
1778
|
*/
|
|
1252
1779
|
export function toUnixSeconds(timestamp) {
|
|
1253
1780
|
return Math.floor(toUnixMilliseconds(timestamp) / MILLISECONDS_IN_SECOND);
|
|
@@ -1257,6 +1784,7 @@ export function toUnixSeconds(timestamp) {
|
|
|
1257
1784
|
*
|
|
1258
1785
|
* @param {number} milliseconds Unix milliseconds.
|
|
1259
1786
|
* @returns {Timestamp | null} Timestamp built from UTC fields, or `null` for invalid input.
|
|
1787
|
+
* @category conversion
|
|
1260
1788
|
*/
|
|
1261
1789
|
export function fromUnixMilliseconds(milliseconds) {
|
|
1262
1790
|
return parseDateUTC(new Date(milliseconds));
|
|
@@ -1266,6 +1794,7 @@ export function fromUnixMilliseconds(milliseconds) {
|
|
|
1266
1794
|
*
|
|
1267
1795
|
* @param {number} seconds Unix seconds.
|
|
1268
1796
|
* @returns {Timestamp | null} Timestamp built from UTC fields, or `null` for invalid input.
|
|
1797
|
+
* @category conversion
|
|
1269
1798
|
*/
|
|
1270
1799
|
export function fromUnixSeconds(seconds) {
|
|
1271
1800
|
return fromUnixMilliseconds(seconds * MILLISECONDS_IN_SECOND);
|
|
@@ -1277,6 +1806,7 @@ export function fromUnixSeconds(seconds) {
|
|
|
1277
1806
|
*
|
|
1278
1807
|
* @param {Timestamp} timestamp Timestamp object to convert.
|
|
1279
1808
|
* @returns {Date} Local JavaScript Date object.
|
|
1809
|
+
* @category conversion
|
|
1280
1810
|
*/
|
|
1281
1811
|
export function getDateObject(timestamp) {
|
|
1282
1812
|
return makeDateTime(timestamp);
|
|
@@ -1287,6 +1817,7 @@ export function getDateObject(timestamp) {
|
|
|
1287
1817
|
* @param input - The value to be validated. Can be a string or a number.
|
|
1288
1818
|
* @returns A boolean indicating whether the input is a finite number.
|
|
1289
1819
|
* Returns true if the input is a finite number, false otherwise.
|
|
1820
|
+
* @category validation
|
|
1290
1821
|
*/
|
|
1291
1822
|
export function validateNumber(input) {
|
|
1292
1823
|
return isFinite(Number(input));
|
|
@@ -1297,6 +1828,7 @@ export function validateNumber(input) {
|
|
|
1297
1828
|
* @param {Timestamp[]} timestamps Timestamp objects to compare.
|
|
1298
1829
|
* @param {boolean=} useTime Include time-of-day in the comparison when true.
|
|
1299
1830
|
* @returns Latest Timestamp object.
|
|
1831
|
+
* @category comparison
|
|
1300
1832
|
*/
|
|
1301
1833
|
export function maxTimestamp(timestamps, useTime = false) {
|
|
1302
1834
|
const func = useTime === true ? getDayTimeIdentifier : getDayIdentifier;
|
|
@@ -1310,6 +1842,7 @@ export function maxTimestamp(timestamps, useTime = false) {
|
|
|
1310
1842
|
* @param {Timestamp[]} timestamps Timestamp objects to compare.
|
|
1311
1843
|
* @param {boolean=} useTime Include time-of-day in the comparison when true.
|
|
1312
1844
|
* @returns Earliest Timestamp object.
|
|
1845
|
+
* @category comparison
|
|
1313
1846
|
*/
|
|
1314
1847
|
export function minTimestamp(timestamps, useTime = false) {
|
|
1315
1848
|
const func = useTime === true ? getDayTimeIdentifier : getDayIdentifier;
|
|
@@ -1321,7 +1854,7 @@ function getTimestampSortValue(timestamp, useTime) {
|
|
|
1321
1854
|
if (useTime === true) {
|
|
1322
1855
|
return toUnixMilliseconds(timestamp);
|
|
1323
1856
|
}
|
|
1324
|
-
return
|
|
1857
|
+
return getEpochDay(timestamp) * MILLISECONDS_IN_DAY;
|
|
1325
1858
|
}
|
|
1326
1859
|
function compareTimestampOrder(first, second, useTime) {
|
|
1327
1860
|
return getTimestampSortValue(first, useTime) - getTimestampSortValue(second, useTime);
|
|
@@ -1346,6 +1879,7 @@ function moveBoundary(timestamp, amount, useTime) {
|
|
|
1346
1879
|
* @param {Timestamp} end Second boundary.
|
|
1347
1880
|
* @param {boolean=} useTime Include time-of-day when ordering boundaries.
|
|
1348
1881
|
* @returns {TimestampRange} Frozen inclusive Timestamp range.
|
|
1882
|
+
* @category ranges
|
|
1349
1883
|
*/
|
|
1350
1884
|
export function createTimestampRange(start, end, useTime = false) {
|
|
1351
1885
|
if (compareTimestampOrder(start, end, useTime) <= 0) {
|
|
@@ -1360,6 +1894,7 @@ export function createTimestampRange(start, end, useTime = false) {
|
|
|
1360
1894
|
* @param {TimestampRange} range Inclusive range to test against.
|
|
1361
1895
|
* @param {boolean=} useTime Include time-of-day in the comparison.
|
|
1362
1896
|
* @returns {boolean} True when the timestamp is inside the range.
|
|
1897
|
+
* @category comparison
|
|
1363
1898
|
*/
|
|
1364
1899
|
export function isTimestampInRange(timestamp, range, useTime = false) {
|
|
1365
1900
|
return isBetweenDates(timestamp, range.start, range.end, useTime);
|
|
@@ -1371,6 +1906,7 @@ export function isTimestampInRange(timestamp, range, useTime = false) {
|
|
|
1371
1906
|
* @param {TimestampRange} second Second range.
|
|
1372
1907
|
* @param {boolean=} useTime Include time-of-day in the comparison.
|
|
1373
1908
|
* @returns {boolean} True when the ranges overlap.
|
|
1909
|
+
* @category comparison
|
|
1374
1910
|
*/
|
|
1375
1911
|
export function isRangeOverlapping(first, second, useTime = false) {
|
|
1376
1912
|
const firstRange = createTimestampRange(first.start, first.end, useTime);
|
|
@@ -1387,6 +1923,7 @@ export function isRangeOverlapping(first, second, useTime = false) {
|
|
|
1387
1923
|
* @param {TimestampRange} second Second range.
|
|
1388
1924
|
* @param {boolean=} useTime Include time-of-day in the comparison.
|
|
1389
1925
|
* @returns {TimestampRange | null} Intersected range, or `null` when the ranges do not overlap.
|
|
1926
|
+
* @category ranges
|
|
1390
1927
|
*/
|
|
1391
1928
|
export function intersectRanges(first, second, useTime = false) {
|
|
1392
1929
|
if (isRangeOverlapping(first, second, useTime) === false) {
|
|
@@ -1406,6 +1943,7 @@ export function intersectRanges(first, second, useTime = false) {
|
|
|
1406
1943
|
* @param {TimestampRange[]} ranges Ranges to merge.
|
|
1407
1944
|
* @param {boolean=} useTime Include time-of-day in the comparison.
|
|
1408
1945
|
* @returns {TimestampRange[]} Merged ranges sorted by start boundary.
|
|
1946
|
+
* @category ranges
|
|
1409
1947
|
*/
|
|
1410
1948
|
export function mergeRanges(ranges, useTime = false) {
|
|
1411
1949
|
const sorted = ranges
|
|
@@ -1433,6 +1971,7 @@ export function mergeRanges(ranges, useTime = false) {
|
|
|
1433
1971
|
* @param {TimestampRange[]} blocked Ranges to remove from the source.
|
|
1434
1972
|
* @param {boolean=} useTime Include time-of-day in the comparison.
|
|
1435
1973
|
* @returns {TimestampRange[]} Remaining ranges.
|
|
1974
|
+
* @category ranges
|
|
1436
1975
|
*/
|
|
1437
1976
|
export function subtractRanges(source, blocked, useTime = false) {
|
|
1438
1977
|
const normalizedSource = createTimestampRange(source.start, source.end, useTime);
|
|
@@ -1467,6 +2006,7 @@ export function subtractRanges(source, blocked, useTime = false) {
|
|
|
1467
2006
|
* @param {TimestampRange[]} occupied Ranges that are not available.
|
|
1468
2007
|
* @param {boolean=} useTime Include time-of-day in the comparison.
|
|
1469
2008
|
* @returns {TimestampRange[]} Gap ranges.
|
|
2009
|
+
* @category ranges
|
|
1470
2010
|
*/
|
|
1471
2011
|
export function findRangeGaps(source, occupied, useTime = false) {
|
|
1472
2012
|
return subtractRanges(source, occupied, useTime);
|
|
@@ -1479,6 +2019,7 @@ export function findRangeGaps(source, occupied, useTime = false) {
|
|
|
1479
2019
|
* @param {Timestamp} endTimestamp Inclusive end boundary.
|
|
1480
2020
|
* @param {boolean=} useTime Include time-of-day in the comparison when true.
|
|
1481
2021
|
* @returns {boolean} True when the timestamp is inside the range.
|
|
2022
|
+
* @category comparison
|
|
1482
2023
|
*/
|
|
1483
2024
|
export function isBetweenDates(timestamp, startTimestamp, endTimestamp, useTime = false) {
|
|
1484
2025
|
const cd = getDayIdentifier(timestamp) + (useTime === true ? getTimeIdentifier(timestamp) : 0);
|
|
@@ -1494,6 +2035,7 @@ export function isBetweenDates(timestamp, startTimestamp, endTimestamp, useTime
|
|
|
1494
2035
|
* @param {Timestamp} firstTimestamp Start of the second range.
|
|
1495
2036
|
* @param {Timestamp} lastTimestamp End of the second range.
|
|
1496
2037
|
* @returns {boolean} True when the ranges overlap.
|
|
2038
|
+
* @category comparison
|
|
1497
2039
|
*/
|
|
1498
2040
|
export function isOverlappingDates(startTimestamp, endTimestamp, firstTimestamp, lastTimestamp) {
|
|
1499
2041
|
const start = getDayIdentifier(startTimestamp);
|
|
@@ -1522,6 +2064,7 @@ export function isOverlappingDates(startTimestamp, endTimestamp, firstTimestamp,
|
|
|
1522
2064
|
* @param {number=} options.second If positive, adds seconds. If negative, removes seconds.
|
|
1523
2065
|
* @param {number=} options.millisecond If positive, adds milliseconds. If negative, removes milliseconds.
|
|
1524
2066
|
* @returns {Timestamp} New normalized Timestamp object.
|
|
2067
|
+
* @category arithmetic
|
|
1525
2068
|
*/
|
|
1526
2069
|
export function addToDate(timestamp, options) {
|
|
1527
2070
|
const ts = cloneTimestamp(timestamp);
|
|
@@ -1563,6 +2106,7 @@ export function addToDate(timestamp, options) {
|
|
|
1563
2106
|
* @param {number=} options.second If positive, adds seconds. If negative, removes seconds.
|
|
1564
2107
|
* @param {number=} options.millisecond If positive, adds milliseconds. If negative, removes milliseconds.
|
|
1565
2108
|
* @returns {Timestamp} New normalized Timestamp object.
|
|
2109
|
+
* @category arithmetic
|
|
1566
2110
|
*/
|
|
1567
2111
|
export function addToDateClamped(timestamp, options) {
|
|
1568
2112
|
const ts = cloneTimestamp(timestamp);
|
|
@@ -1588,6 +2132,7 @@ export function addToDateClamped(timestamp, options) {
|
|
|
1588
2132
|
* Normalizes a year/month pair while keeping the day out of the calculation.
|
|
1589
2133
|
* This lets clamped date math choose the final day explicitly instead of
|
|
1590
2134
|
* letting JavaScript Date roll an overflowing day into the next month.
|
|
2135
|
+
* @category arithmetic
|
|
1591
2136
|
*/
|
|
1592
2137
|
function normalizeYearMonth(year, month) {
|
|
1593
2138
|
const date = new Date(year, month - 1, 1);
|
|
@@ -1633,6 +2178,7 @@ function normalizeTimestamp(ts) {
|
|
|
1633
2178
|
* @param {Timestamp} ts1 The first Timestamp
|
|
1634
2179
|
* @param {Timestamp} ts2 The second Timestamp
|
|
1635
2180
|
* @returns Number of days
|
|
2181
|
+
* @category arithmetic
|
|
1636
2182
|
*/
|
|
1637
2183
|
export function daysBetween(ts1, ts2) {
|
|
1638
2184
|
const diff = diffTimestamp(ts1, ts2, true);
|
|
@@ -1642,6 +2188,7 @@ export function daysBetween(ts1, ts2) {
|
|
|
1642
2188
|
* Returns number of weeks between two Timestamps
|
|
1643
2189
|
* @param {Timestamp} ts1 The first Timestamp
|
|
1644
2190
|
* @param {Timestamp} ts2 The second Timestamp
|
|
2191
|
+
* @category arithmetic
|
|
1645
2192
|
*/
|
|
1646
2193
|
export function weeksBetween(ts1, ts2) {
|
|
1647
2194
|
let t1 = copyTimestamp(ts1);
|
|
@@ -1655,6 +2202,7 @@ export function weeksBetween(ts1, ts2) {
|
|
|
1655
2202
|
*
|
|
1656
2203
|
* @param {number} milliseconds Signed elapsed milliseconds.
|
|
1657
2204
|
* @returns {TimestampDuration} Frozen duration object.
|
|
2205
|
+
* @category duration
|
|
1658
2206
|
*/
|
|
1659
2207
|
export function createDuration(milliseconds) {
|
|
1660
2208
|
const sign = milliseconds === 0 ? 0 : milliseconds < 0 ? -1 : 1;
|
|
@@ -1687,6 +2235,7 @@ export function createDuration(milliseconds) {
|
|
|
1687
2235
|
* @param {Timestamp} start Start timestamp.
|
|
1688
2236
|
* @param {Timestamp} end End timestamp.
|
|
1689
2237
|
* @returns {TimestampDuration} Frozen duration object.
|
|
2238
|
+
* @category duration
|
|
1690
2239
|
*/
|
|
1691
2240
|
export function durationBetween(start, end) {
|
|
1692
2241
|
return createDuration(toUnixMilliseconds(end) - toUnixMilliseconds(start));
|
|
@@ -1701,9 +2250,10 @@ export function durationBetween(start, end) {
|
|
|
1701
2250
|
* @param {Timestamp} timestamp Timestamp object to offset.
|
|
1702
2251
|
* @param {TimestampDuration | number} duration Duration object or signed milliseconds.
|
|
1703
2252
|
* @returns {Timestamp} Offset Timestamp.
|
|
2253
|
+
* @category duration
|
|
1704
2254
|
*/
|
|
1705
2255
|
export function addDuration(timestamp, duration) {
|
|
1706
|
-
const milliseconds = typeof duration ===
|
|
2256
|
+
const milliseconds = typeof duration === 'number' ? duration : duration.totalMilliseconds;
|
|
1707
2257
|
return fromUnixMilliseconds(toUnixMilliseconds(timestamp) + milliseconds);
|
|
1708
2258
|
}
|
|
1709
2259
|
/**
|
|
@@ -1712,9 +2262,10 @@ export function addDuration(timestamp, duration) {
|
|
|
1712
2262
|
* @param {Timestamp} timestamp Timestamp object to offset.
|
|
1713
2263
|
* @param {TimestampDuration | number} duration Duration object or signed milliseconds.
|
|
1714
2264
|
* @returns {Timestamp} Offset Timestamp.
|
|
2265
|
+
* @category duration
|
|
1715
2266
|
*/
|
|
1716
2267
|
export function subtractDuration(timestamp, duration) {
|
|
1717
|
-
const milliseconds = typeof duration ===
|
|
2268
|
+
const milliseconds = typeof duration === 'number' ? duration : duration.totalMilliseconds;
|
|
1718
2269
|
return addDuration(timestamp, -milliseconds);
|
|
1719
2270
|
}
|
|
1720
2271
|
/**
|
|
@@ -1725,11 +2276,12 @@ export function subtractDuration(timestamp, duration) {
|
|
|
1725
2276
|
* @param {TimestampDuration | number} duration Duration object or signed milliseconds.
|
|
1726
2277
|
* @param {FormatDurationOptions=} options Formatting options.
|
|
1727
2278
|
* @returns {string} Formatted duration.
|
|
2279
|
+
* @category duration
|
|
1728
2280
|
*/
|
|
1729
2281
|
export function formatDuration(duration, options = {}) {
|
|
1730
|
-
const value = typeof duration ===
|
|
2282
|
+
const value = typeof duration === 'number' ? createDuration(duration) : duration;
|
|
1731
2283
|
const hours = value.days * HOURS_IN_DAY + value.hours;
|
|
1732
|
-
const sign = options.signed === true && value.sign < 0 ?
|
|
2284
|
+
const sign = options.signed === true && value.sign < 0 ? '-' : '';
|
|
1733
2285
|
let formatted = `${sign}${padNumber(hours, 2)}:${padNumber(value.minutes, 2)}:${padNumber(value.seconds, 2)}`;
|
|
1734
2286
|
if (options.milliseconds === true) {
|
|
1735
2287
|
formatted += `.${padNumber(value.milliseconds, 3)}`;
|
|
@@ -1750,6 +2302,7 @@ function roundTimestampToInterval(timestamp, minutes, rounder) {
|
|
|
1750
2302
|
* @param {Timestamp} timestamp Timestamp object to round.
|
|
1751
2303
|
* @param {number} minutes Interval size in minutes.
|
|
1752
2304
|
* @returns {Timestamp} Rounded Timestamp.
|
|
2305
|
+
* @category arithmetic
|
|
1753
2306
|
*/
|
|
1754
2307
|
export function floorToInterval(timestamp, minutes) {
|
|
1755
2308
|
return roundTimestampToInterval(timestamp, minutes, Math.floor);
|
|
@@ -1760,6 +2313,7 @@ export function floorToInterval(timestamp, minutes) {
|
|
|
1760
2313
|
* @param {Timestamp} timestamp Timestamp object to round.
|
|
1761
2314
|
* @param {number} minutes Interval size in minutes.
|
|
1762
2315
|
* @returns {Timestamp} Rounded Timestamp.
|
|
2316
|
+
* @category arithmetic
|
|
1763
2317
|
*/
|
|
1764
2318
|
export function ceilToInterval(timestamp, minutes) {
|
|
1765
2319
|
return roundTimestampToInterval(timestamp, minutes, Math.ceil);
|
|
@@ -1770,22 +2324,23 @@ export function ceilToInterval(timestamp, minutes) {
|
|
|
1770
2324
|
* @param {Timestamp} timestamp Timestamp object to round.
|
|
1771
2325
|
* @param {number} minutes Interval size in minutes.
|
|
1772
2326
|
* @returns {Timestamp} Rounded Timestamp.
|
|
2327
|
+
* @category arithmetic
|
|
1773
2328
|
*/
|
|
1774
2329
|
export function roundToInterval(timestamp, minutes) {
|
|
1775
2330
|
return roundTimestampToInterval(timestamp, minutes, Math.round);
|
|
1776
2331
|
}
|
|
1777
2332
|
// Known dates
|
|
1778
2333
|
const weekdayDateMap = {
|
|
1779
|
-
Sun: new Date(
|
|
1780
|
-
Mon: new Date(
|
|
1781
|
-
Tue: new Date(
|
|
1782
|
-
Wed: new Date(
|
|
1783
|
-
Thu: new Date(
|
|
1784
|
-
Fri: new Date(
|
|
1785
|
-
Sat: new Date(
|
|
2334
|
+
Sun: new Date('2020-01-05T00:00:00.000Z'),
|
|
2335
|
+
Mon: new Date('2020-01-06T00:00:00.000Z'),
|
|
2336
|
+
Tue: new Date('2020-01-07T00:00:00.000Z'),
|
|
2337
|
+
Wed: new Date('2020-01-08T00:00:00.000Z'),
|
|
2338
|
+
Thu: new Date('2020-01-09T00:00:00.000Z'),
|
|
2339
|
+
Fri: new Date('2020-01-10T00:00:00.000Z'),
|
|
2340
|
+
Sat: new Date('2020-01-11T00:00:00.000Z'),
|
|
1786
2341
|
};
|
|
1787
2342
|
function resolveIntlNameFormat(options, type) {
|
|
1788
|
-
if (type ===
|
|
2343
|
+
if (type === 'long' || type === 'short' || type === 'narrow') {
|
|
1789
2344
|
return options[type];
|
|
1790
2345
|
}
|
|
1791
2346
|
return options.long;
|
|
@@ -1806,15 +2361,16 @@ function resolveIntlNameFormat(options, type) {
|
|
|
1806
2361
|
* @param {string} [locale=''] - The locale to use for formatting.
|
|
1807
2362
|
*
|
|
1808
2363
|
* @returns {string} The formatted weekday.
|
|
2364
|
+
* @category locale
|
|
1809
2365
|
*/
|
|
1810
2366
|
export function getWeekdayFormatter() {
|
|
1811
|
-
const emptyFormatter = () =>
|
|
2367
|
+
const emptyFormatter = () => '';
|
|
1812
2368
|
const options = {
|
|
1813
|
-
long: { timeZone:
|
|
1814
|
-
short: { timeZone:
|
|
1815
|
-
narrow: { timeZone:
|
|
2369
|
+
long: { timeZone: 'UTC', weekday: 'long' },
|
|
2370
|
+
short: { timeZone: 'UTC', weekday: 'short' },
|
|
2371
|
+
narrow: { timeZone: 'UTC', weekday: 'narrow' },
|
|
1816
2372
|
};
|
|
1817
|
-
if (typeof Intl ===
|
|
2373
|
+
if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat === 'undefined') {
|
|
1818
2374
|
return emptyFormatter;
|
|
1819
2375
|
}
|
|
1820
2376
|
/**
|
|
@@ -1834,7 +2390,7 @@ export function getWeekdayFormatter() {
|
|
|
1834
2390
|
if (e instanceof Error) {
|
|
1835
2391
|
console.error(`Intl.DateTimeFormat: ${e.message} -> day of week: ${weekday}`);
|
|
1836
2392
|
}
|
|
1837
|
-
return
|
|
2393
|
+
return '';
|
|
1838
2394
|
}
|
|
1839
2395
|
}
|
|
1840
2396
|
return weekdayFormatter;
|
|
@@ -1845,6 +2401,7 @@ export function getWeekdayFormatter() {
|
|
|
1845
2401
|
* @param {string} type Format type: `narrow`, `short`, or `long`.
|
|
1846
2402
|
* @param {string} locale Locale to use for formatting, such as `en-US`.
|
|
1847
2403
|
* @returns {string[]} Localized weekday names in Sunday-first order.
|
|
2404
|
+
* @category locale
|
|
1848
2405
|
*/
|
|
1849
2406
|
export function getWeekdayNames(type, locale) {
|
|
1850
2407
|
const shortWeekdays = Object.keys(weekdayDateMap);
|
|
@@ -1862,15 +2419,16 @@ export function getWeekdayNames(type, locale) {
|
|
|
1862
2419
|
* @returns {string} The formatted month name.
|
|
1863
2420
|
*
|
|
1864
2421
|
* @throws {Error} If Intl or Intl.DateTimeFormat is not supported in the environment.
|
|
2422
|
+
* @category locale
|
|
1865
2423
|
*/
|
|
1866
2424
|
export function getMonthFormatter() {
|
|
1867
|
-
const emptyFormatter = () =>
|
|
2425
|
+
const emptyFormatter = () => '';
|
|
1868
2426
|
const options = {
|
|
1869
|
-
long: { timeZone:
|
|
1870
|
-
short: { timeZone:
|
|
1871
|
-
narrow: { timeZone:
|
|
2427
|
+
long: { timeZone: 'UTC', month: 'long' },
|
|
2428
|
+
short: { timeZone: 'UTC', month: 'short' },
|
|
2429
|
+
narrow: { timeZone: 'UTC', month: 'narrow' },
|
|
1872
2430
|
};
|
|
1873
|
-
if (typeof Intl ===
|
|
2431
|
+
if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat === 'undefined') {
|
|
1874
2432
|
return emptyFormatter;
|
|
1875
2433
|
}
|
|
1876
2434
|
/**
|
|
@@ -1881,7 +2439,7 @@ export function getMonthFormatter() {
|
|
|
1881
2439
|
* @param {string} [locale] - The locale to use for formatting (defaults to the system locale if not provided).
|
|
1882
2440
|
* @returns {string} The formatted month string.
|
|
1883
2441
|
*/
|
|
1884
|
-
function monthFormatter(month, type =
|
|
2442
|
+
function monthFormatter(month, type = 'long', locale) {
|
|
1885
2443
|
try {
|
|
1886
2444
|
const intlFormatter = new Intl.DateTimeFormat(locale || undefined, resolveIntlNameFormat(options, type));
|
|
1887
2445
|
const date = new Date();
|
|
@@ -1893,7 +2451,7 @@ export function getMonthFormatter() {
|
|
|
1893
2451
|
if (e instanceof Error) {
|
|
1894
2452
|
console.error(`Intl.DateTimeFormat: ${e.message} -> month: ${month}`);
|
|
1895
2453
|
}
|
|
1896
|
-
return
|
|
2454
|
+
return '';
|
|
1897
2455
|
}
|
|
1898
2456
|
}
|
|
1899
2457
|
return monthFormatter;
|
|
@@ -1904,6 +2462,7 @@ export function getMonthFormatter() {
|
|
|
1904
2462
|
* @param {string} type Format type: `narrow`, `short`, or `long`.
|
|
1905
2463
|
* @param {string} locale Locale to use for formatting, such as `en-US`.
|
|
1906
2464
|
* @returns {string[]} Localized month names in January-first order.
|
|
2465
|
+
* @category locale
|
|
1907
2466
|
*/
|
|
1908
2467
|
export function getMonthNames(type, locale) {
|
|
1909
2468
|
const monthFormatter = getMonthFormatter();
|