atomic-fns 1.1.7 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +51 -12
  2. package/dist/collections/BTree.d.ts +11 -10
  3. package/dist/collections/BTree.js +58 -80
  4. package/dist/collections/Heap.js +1 -1
  5. package/dist/collections/LRUCache.d.ts +6 -3
  6. package/dist/collections/LRUCache.js +24 -11
  7. package/dist/collections/SortedMap.js +2 -2
  8. package/dist/collections/SortedTree.d.ts +2 -2
  9. package/dist/collections/SortedTree.js +3 -2
  10. package/dist/collections/SplayTree.d.ts +7 -5
  11. package/dist/collections/SplayTree.js +39 -26
  12. package/dist/collections/Trie.d.ts +1 -1
  13. package/dist/collections/Trie.js +26 -26
  14. package/dist/collections/deque.d.ts +1 -1
  15. package/dist/collections/deque.js +17 -10
  16. package/dist/collections/frozenset.js +4 -1
  17. package/dist/collections/index.d.ts +13 -32
  18. package/dist/collections/index.js +210 -82
  19. package/dist/decimal/index.d.ts +5 -2
  20. package/dist/decimal/index.js +63 -27
  21. package/dist/functools/index.d.ts +6 -30
  22. package/dist/functools/index.js +24 -56
  23. package/dist/globals/index.d.ts +3 -20
  24. package/dist/globals/index.js +23 -39
  25. package/dist/i18n/currency.d.ts +3 -0
  26. package/dist/i18n/currency.js +22 -7
  27. package/dist/i18n/date/index.d.ts +17 -10
  28. package/dist/i18n/date/index.js +79 -58
  29. package/dist/i18n/date/utils.d.ts +4 -5
  30. package/dist/i18n/date/utils.js +15 -113
  31. package/dist/i18n/duration.d.ts +11 -10
  32. package/dist/i18n/duration.js +38 -32
  33. package/dist/i18n/format.d.ts +5 -2
  34. package/dist/i18n/format.js +81 -209
  35. package/dist/i18n/index.d.ts +2 -1
  36. package/dist/itertools/index.d.ts +9 -13
  37. package/dist/itertools/index.js +38 -41
  38. package/dist/math/index.d.ts +10 -27
  39. package/dist/math/index.js +16 -33
  40. package/dist/operators/index.d.ts +1 -5
  41. package/dist/operators/index.js +60 -40
  42. package/dist/random/index.js +9 -6
  43. package/dist/string/index.d.ts +1 -1
  44. package/dist/string/index.js +2 -4
  45. package/dist/types.d.ts +3 -3
  46. package/package.json +16 -21
@@ -1,6 +1,8 @@
1
1
  import { defaults } from '../collections/index.js';
2
2
  import { isObject } from '../globals/index.js';
3
3
  import { Decimal } from '../decimal/index.js';
4
+ /** `Intl` options that set the digits shown. Some reject each other, so they replace as a group. */
5
+ const DIGIT_OPTION = /^(minimum|maximum)(Fraction|Significant)Digits$|^rounding(Increment|Priority)$/;
4
6
  /**
5
7
  * Currency represents a local currency amount that can be formatted using the `Intl` apis.
6
8
  */
@@ -8,6 +10,7 @@ export class Currency extends Decimal {
8
10
  locale;
9
11
  currency;
10
12
  numberFormat;
13
+ overrides;
11
14
  options = {
12
15
  style: 'currency',
13
16
  currencyDisplay: 'symbol',
@@ -19,16 +22,17 @@ export class Currency extends Decimal {
19
22
  opts = locale;
20
23
  locale = undefined;
21
24
  }
22
- const systemLocale = new Intl.NumberFormat(locale, defaults(opts, { currency }, this.options));
25
+ const systemLocale = new Intl.NumberFormat(locale, defaults({}, opts, { currency }, this.options));
23
26
  const resolved = systemLocale.resolvedOptions();
24
27
  this.numberFormat = systemLocale;
25
28
  this.options.currency = currency;
29
+ this.overrides = defaults({}, opts);
26
30
  this.currency = resolved.currency;
27
31
  this.locale = resolved.locale;
28
32
  }
29
33
  // @ts-expect-error overrides
30
34
  clone(n) {
31
- return new Currency(n, this.currency, this.locale, this.options);
35
+ return new Currency(n === undefined ? this : n, this.currency, this.locale, this.overrides);
32
36
  }
33
37
  // @ts-expect-error overrides
34
38
  negated() {
@@ -58,14 +62,25 @@ export class Currency extends Decimal {
58
62
  toDecimal() {
59
63
  return super.clone.call(this);
60
64
  }
61
- toString() {
65
+ /** `Intl` reads a decimal string exactly, while a `Number` past 2^53 has lost digits. */
66
+ formattable() {
62
67
  const number = this.toNumber();
63
- return this.numberFormat.format(number);
68
+ if (Math.abs(number) <= Number.MAX_SAFE_INTEGER)
69
+ return number;
70
+ return Decimal.prototype.toString.call(this);
71
+ }
72
+ toString() {
73
+ return this.numberFormat.format(this.formattable());
64
74
  }
65
75
  format(opts = {}, locale) {
66
- const number = this.toNumber();
67
- opts = defaults(opts, this.options);
68
- return new Intl.NumberFormat(locale ?? this.locale, opts).format(number);
76
+ const overrides = { ...this.overrides };
77
+ if (Object.keys(opts).some((key) => DIGIT_OPTION.test(key) && opts[key] !== undefined)) {
78
+ for (const key in overrides)
79
+ if (DIGIT_OPTION.test(key))
80
+ delete overrides[key];
81
+ }
82
+ opts = defaults({}, opts, overrides, this.options);
83
+ return new Intl.NumberFormat(locale ?? this.locale, opts).format(this.formattable());
69
84
  }
70
85
  displayName() {
71
86
  return this.format({ currencyDisplay: 'name' });
@@ -1,9 +1,9 @@
1
- import { Duration, DurationUnit, TDuration } from '../duration.js';
1
+ import { Duration, type DurationUnit, type TDuration } from '../duration.js';
2
2
  import { asDate, formatDate, strftime } from '../format.js';
3
- import { DateObject, daysInMonth, daysInYear, isLeapYear, maxDate, minDate, weeksInYear } from './utils.js';
3
+ import { type DateObject, daysInMonth, daysInYear, isLeapYear, maxDate, minDate, weeksInYear } from './utils.js';
4
4
  export { asDate, daysInMonth, daysInYear, formatDate, isLeapYear, maxDate, minDate, strftime, weeksInYear };
5
- export { DateObject };
6
- export declare type DateLike = number | string | Date | IntlDate | DateObject;
5
+ export type { DateObject };
6
+ export type DateLike = number | string | Date | IntlDate | DateObject;
7
7
  /**
8
8
  * Creates a date tied to a given locale (default system locale) which can be formatted in that locale's language using the native {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl Intl Apis} directly or using a formatting string.
9
9
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl Intl Apis}
@@ -52,7 +52,7 @@ export declare class IntlDate {
52
52
  dayOfYear(): number;
53
53
  /** Returns `true` if this date's year is a leap year. */
54
54
  isLeapYear(): boolean;
55
- /** Gets the number of weeks according to locale in the current year. */
55
+ /** Gets the number of Monday-first weeks in the current year, where week 1 is the first full week. */
56
56
  weeksInYear(): number;
57
57
  /** Gets the number of weeks in the current year, according to ISO weeks. */
58
58
  isoWeeksInYear(): number;
@@ -128,13 +128,13 @@ export declare class IntlDate {
128
128
  /** Returns the time part formatted as ISO8601.
129
129
  * @example
130
130
  ```js
131
- new IntlDate().toISOTime() // 'T22:44:30.652Z'
131
+ new IntlDate().toISOTime() // '22:44:30.652Z'
132
132
  ```
133
133
  */
134
134
  toISOTime(): string;
135
135
  /**
136
136
  * Returns an object containing year, month, day-of-month, hours, minutes, seconds, milliseconds.
137
- * @returns {Object} An object like `{year, month, date, hours, minutes, seconds, ms}`
137
+ * @returns {Object} An object like `{year, month, day, hour, minute, second, millisecond}`
138
138
  */
139
139
  toObject(): DateObject;
140
140
  /** Returns the number of milliseconds since the Unix Epoch (January 1, 1970 UTC) */
@@ -185,19 +185,26 @@ export declare class IntlDate {
185
185
  diff(other: DateLike, unit?: DurationUnit, exact?: boolean): number;
186
186
  add(duration: TDuration, exact?: boolean): IntlDate;
187
187
  subtract(duration: TDuration, exact?: boolean): IntlDate;
188
- startOf(unit: DurationUnit): IntlDate;
189
- endOf(unit: DurationUnit): IntlDate;
188
+ startOf(unit: DurationUnit): any;
189
+ endOf(unit: DurationUnit): any;
190
190
  clone(date?: DateLike): IntlDate;
191
191
  static max(...args: any[]): any;
192
192
  static min(...args: any[]): any;
193
193
  inspect(): string;
194
194
  }
195
+ /**
196
+ * Add the specified years, months, weeks, days, hours, minutes and seconds to the given date
197
+ * @param date
198
+ * @param duration
199
+ * @param exact Kept for callers that pass it, it no longer changes the result.
200
+ * @returns
201
+ */
195
202
  export declare function addDuration(date: number | string | Date, duration: number | Duration | TDuration, exact?: boolean): Date;
196
203
  /**
197
204
  * Subtract the specified years, months, weeks, days, hours, minutes and seconds from the given date.
198
205
  * @param date
199
206
  * @param duration
200
- * @param exact
207
+ * @param exact Kept for callers that pass it, it no longer changes the result.
201
208
  * @returns
202
209
  */
203
210
  export declare function subDuration(date: number | string | Date, duration: number | Duration | TDuration, exact?: boolean): Date;
@@ -1,6 +1,6 @@
1
1
  import { isObject, ValueError } from '../../globals/index.js';
2
2
  import { Duration, UNITS_PLURAL } from '../duration.js';
3
- import { asDate, formatDate, strftime } from '../format.js';
3
+ import { asDate, formatDate, fromFields, strftime } from '../format.js';
4
4
  import { asNumber, dayOfYear, daysInMonth, daysInYear, isLeapYear, maxDate, minDate, weekOfYear, weeksInYear } from './utils.js';
5
5
  // Export date functions
6
6
  export { asDate, daysInMonth, daysInYear, formatDate, isLeapYear, maxDate, minDate, strftime, weeksInYear };
@@ -45,16 +45,8 @@ export class IntlDate {
45
45
  else if (isObject(obj)) {
46
46
  let { year, month, day, hour, minute, second, millisecond } = obj;
47
47
  // NOTE month is from 1-12, not from 0-11
48
- if (typeof month === 'number') {
49
- month -= 1;
50
- }
51
- if (utc) {
52
- // from UTC to current
53
- this._date = new Date(Date.UTC(year, month, day || 1, hour || 0, minute || 0, second || 0, millisecond || 0));
54
- }
55
- else {
56
- this._date = new Date(year, month, day || 1, hour || 0, minute || 0, second || 0, millisecond || 0);
57
- }
48
+ month = (month ?? 1) - 1;
49
+ this._date = fromFields(utc, Number(year), month, day || 1, hour || 0, minute || 0, second || 0, millisecond || 0);
58
50
  }
59
51
  else {
60
52
  this._date = asDate(obj, utc);
@@ -113,7 +105,7 @@ export class IntlDate {
113
105
  isLeapYear() {
114
106
  return isLeapYear(this.year);
115
107
  }
116
- /** Gets the number of weeks according to locale in the current year. */
108
+ /** Gets the number of Monday-first weeks in the current year, where week 1 is the first full week. */
117
109
  weeksInYear() {
118
110
  return weeksInYear(this.year);
119
111
  }
@@ -208,20 +200,20 @@ export class IntlDate {
208
200
  ```
209
201
  */
210
202
  toISODate() {
211
- return this._date.toISOString().slice(0, 10);
203
+ return this._date.toISOString().split('T')[0];
212
204
  }
213
205
  /** Returns the time part formatted as ISO8601.
214
206
  * @example
215
207
  ```js
216
- new IntlDate().toISOTime() // 'T22:44:30.652Z'
208
+ new IntlDate().toISOTime() // '22:44:30.652Z'
217
209
  ```
218
210
  */
219
211
  toISOTime() {
220
- return this._date.toISOString().slice(11);
212
+ return this._date.toISOString().split('T')[1];
221
213
  }
222
214
  /**
223
215
  * Returns an object containing year, month, day-of-month, hours, minutes, seconds, milliseconds.
224
- * @returns {Object} An object like `{year, month, date, hours, minutes, seconds, ms}`
216
+ * @returns {Object} An object like `{year, month, day, hour, minute, second, millisecond}`
225
217
  */
226
218
  toObject() {
227
219
  return {
@@ -248,7 +240,10 @@ export class IntlDate {
248
240
  }
249
241
  /** Returns the timezone GMT offset as a string */
250
242
  zone() {
251
- return this.format('ZZ');
243
+ if (!this.isValid())
244
+ return INVALID_DATE_STRING;
245
+ const zoneFormat = new Intl.DateTimeFormat(this.locale, { timeZoneName: 'longOffset' });
246
+ return zoneFormat.formatToParts(this._date).find((part) => part.type === 'timeZoneName').value;
252
247
  }
253
248
  /** Returns the difference in `minutes` between this date and UTC */
254
249
  utcOffset() {
@@ -321,9 +316,14 @@ export class IntlDate {
321
316
  // if we didn't set the day but we ended up on an overflow date,
322
317
  // use the last day of the right month
323
318
  if (normalized.day === undefined) {
324
- mixed.day = Math.min(daysInMonth(this._date), mixed.day);
319
+ const target = new Date(this._date);
320
+ target.setFullYear(mixed.year, mixed.month - 1, 1);
321
+ mixed.day = Math.min(daysInMonth(target), mixed.day);
325
322
  }
326
- return this.clone(mixed);
323
+ const result = this.clone(mixed);
324
+ return normalized.weekday === undefined
325
+ ? result
326
+ : result.add({ days: normalized.weekday - result.dayOfWeek });
327
327
  }
328
328
  get relativeFormat() {
329
329
  if (this.intlRelativeFormat)
@@ -332,7 +332,7 @@ export class IntlDate {
332
332
  return new Intl.RelativeTimeFormat(this.locale);
333
333
  }
334
334
  relativeTime(n, unit = 'seconds') {
335
- return this.relativeFormat.format(n, unit);
335
+ return Number.isNaN(n) ? INVALID_DATE_STRING : this.relativeFormat.format(n, unit);
336
336
  }
337
337
  fromNow(unit = 'seconds') {
338
338
  const diff = this.diff(new Date(), unit);
@@ -352,7 +352,22 @@ export class IntlDate {
352
352
  }
353
353
  diff(other, unit = 'milliseconds', exact = false) {
354
354
  const dt = new IntlDate(other);
355
- const dur = new Duration(this.getTime() - dt.getTime(), exact);
355
+ const normalizedUnit = UNITS_PLURAL[unit.toLowerCase()];
356
+ const monthsInUnit = MONTHS_IN_UNIT[normalizedUnit];
357
+ if (monthsInUnit && !exact) {
358
+ // whole months are the ones `add` can take from `other` without passing this date
359
+ let months = (this.year - dt.year) * 12 + this.month - dt.month;
360
+ const sign = Math.sign(this.getTime() - dt.getTime());
361
+ if (sign * (dt.add({ months }).getTime() - this.getTime()) > 0)
362
+ months -= sign;
363
+ return Math.trunc(months / monthsInUnit);
364
+ }
365
+ let elapsed = this.getTime() - dt.getTime();
366
+ if (normalizedUnit === 'days' || normalizedUnit === 'weeks') {
367
+ // a calendar day can last 23 or 25 hours, so measure it on the wall clock
368
+ elapsed += (dt._date.getTimezoneOffset() - this._date.getTimezoneOffset()) * 6e4;
369
+ }
370
+ const dur = new Duration(elapsed, exact);
356
371
  let total = dur.total(unit);
357
372
  if (!exact)
358
373
  total = Math.trunc(total);
@@ -392,16 +407,22 @@ export class IntlDate {
392
407
  break;
393
408
  case 'milliseconds':
394
409
  break;
395
- // no default, invalid units throw in normalizeUnit()
410
+ // no default, an unknown unit changes nothing
396
411
  }
397
412
  if (normalizedUnit === 'weeks') {
398
- obj.weekday = 1;
413
+ return this.subtract({ days: this.dayOfWeek - 1 }).startOf('days');
399
414
  }
400
415
  if (normalizedUnit === 'quarters') {
401
416
  const q = Math.ceil(this.month / 3);
402
417
  obj.month = (q - 1) * 3 + 1;
403
418
  }
404
- return this.set(obj);
419
+ const start = this.set(obj);
420
+ if (obj.hour !== undefined)
421
+ return start;
422
+ // Date reads a repeated wall clock as its first pass, this date may be on the second
423
+ const offset = this._date.getTimezoneOffset();
424
+ const kept = new Date(start.getTime() + (offset - start._date.getTimezoneOffset()) * 6e4);
425
+ return kept.getTimezoneOffset() === offset ? this.clone(kept) : start;
405
426
  }
406
427
  endOf(unit) {
407
428
  return this.isValid()
@@ -420,57 +441,56 @@ export class IntlDate {
420
441
  return minDate.apply(undefined, args);
421
442
  }
422
443
  inspect() {
423
- return `new IntlDate("${this.toISOString()}")`;
444
+ return this.isValid() ? `new IntlDate("${this.toISOString()}")` : 'new IntlDate(NaN)';
424
445
  }
425
446
  }
426
- // Check if the new offset is different because we crossed to DST
427
- function fixOffset(startDate, endDate) {
428
- const startOffset = startDate.getTimezoneOffset();
429
- const endOffset = endDate.getTimezoneOffset();
430
- if (startOffset === endOffset)
431
- return new Date(endDate);
432
- const postoffset = (startOffset - endOffset) * 60 * 1000;
433
- return new Date(+endDate + postoffset);
434
- }
447
+ /**
448
+ * Add the specified years, months, weeks, days, hours, minutes and seconds to the given date
449
+ * @param date
450
+ * @param duration
451
+ * @param exact Kept for callers that pass it, it no longer changes the result.
452
+ * @returns
453
+ */
435
454
  export function addDuration(date, duration, exact = false) {
436
- // Get the current offset of this date timezone
437
- const newDate = new Date(date);
438
- // Get the number of ticks forward
439
- const { values } = new Duration(duration, exact).normalize();
440
- // Apply any time changes that may have happened
441
- for (const unit in values) {
442
- setDateUnit(newDate, unit, Math.floor(values[unit]));
443
- }
444
- return fixOffset(new Date(date), newDate);
455
+ const newDate = asDate(date);
456
+ const { values } = new Duration(duration, exact);
457
+ const { years = 0, quarters = 0, months = 0, weeks = 0, days = 0 } = values;
458
+ // largest unit first and all the months in one step, so a clamped day cannot carry over
459
+ setDateUnit(newDate, 'months', Math.trunc(years * 12 + quarters * 3 + Number(months)));
460
+ setDateUnit(newDate, 'days', Math.trunc(weeks * 7 + Number(days)));
461
+ for (const unit of ['hours', 'minutes', 'seconds', 'milliseconds']) {
462
+ setDateUnit(newDate, unit, Number(values[unit] ?? 0));
463
+ }
464
+ return newDate;
465
+ }
466
+ function addMonths(date, months) {
467
+ const target = new Date(date);
468
+ target.setFullYear(date.getFullYear(), date.getMonth() + months, 1);
469
+ const day = Math.min(date.getDate(), daysInMonth(target));
470
+ date.setFullYear(target.getFullYear(), target.getMonth(), day);
445
471
  }
446
472
  function setDateUnit(date, unit, value) {
473
+ // writing a date field again would move a repeated hour to its first pass
474
+ if (value === 0)
475
+ return date;
447
476
  switch (unit) {
448
- case 'years':
449
- date.setFullYear(date.getFullYear() + value);
450
- break;
451
- case 'quarters':
452
- date.setMonth(date.getMonth() + value * 3);
453
- break;
454
477
  case 'months':
455
- date.setMonth(date.getMonth() + value);
456
- break;
457
- case 'weeks':
458
- date.setDate(date.getDate() + value * 7);
478
+ addMonths(date, value);
459
479
  break;
460
480
  case 'days':
461
481
  date.setDate(date.getDate() + value);
462
482
  break;
463
483
  case 'hours':
464
- date.setHours(date.getHours() + value);
484
+ date.setTime(date.getTime() + value * 36e5);
465
485
  break;
466
486
  case 'minutes':
467
- date.setMinutes(date.getMinutes() + value);
487
+ date.setTime(date.getTime() + value * 6e4);
468
488
  break;
469
489
  case 'seconds':
470
- date.setSeconds(date.getSeconds() + value);
490
+ date.setTime(date.getTime() + value * 1e3);
471
491
  break;
472
492
  case 'milliseconds':
473
- date.setMilliseconds(date.getMilliseconds() + value);
493
+ date.setTime(date.getTime() + value);
474
494
  break;
475
495
  default:
476
496
  break;
@@ -481,12 +501,13 @@ function setDateUnit(date, unit, value) {
481
501
  * Subtract the specified years, months, weeks, days, hours, minutes and seconds from the given date.
482
502
  * @param date
483
503
  * @param duration
484
- * @param exact
504
+ * @param exact Kept for callers that pass it, it no longer changes the result.
485
505
  * @returns
486
506
  */
487
507
  export function subDuration(date, duration, exact = false) {
488
508
  return addDuration(date, new Duration(duration, exact).negated(), exact);
489
509
  }
510
+ const MONTHS_IN_UNIT = { years: 12, quarters: 3, months: 1 };
490
511
  const UNITS_SINGULAR = {
491
512
  year: 'year',
492
513
  years: 'year',
@@ -51,11 +51,10 @@ export declare function parseISODuration(s: any): any;
51
51
  export declare function weeksInYear(year: number, firstDayOfWeek?: number, firstDayOfYear?: number): number;
52
52
  /**
53
53
  * Returns the week of the year for the specified date
54
- * @param year
55
- * @param dayOfYear
56
- * @param firstDayOfWeek
57
- * @param firstDayOfYear
58
- * @returns
54
+ * @param date The date value
55
+ * @param firstDayOfWeek The weekday a week starts on, where Sunday is 0
56
+ * @param firstDayOfYear Picks week 1: the week holding January `7 + firstDayOfWeek - firstDayOfYear`, so `1, 4` gives ISO weeks
57
+ * @returns The week and the year that week belongs to
59
58
  */
60
59
  export declare function weekOfYear(date: string | number | Date, firstDayOfWeek?: number, firstDayOfYear?: number): {
61
60
  week: any;
@@ -1,3 +1,4 @@
1
+ import { asDate } from '../format.js';
1
2
  /**
2
3
  * Returns x % n but takes the sign of n instead of x
3
4
  * @param x
@@ -7,28 +8,13 @@
7
8
  function floorMod(x, n) {
8
9
  return x - n * Math.floor(x / n);
9
10
  }
10
- function padStart(input, n = 2) {
11
- const isNeg = input < 0;
12
- let padded;
13
- if (isNeg) {
14
- padded = '-' + ('' + -input).padStart(n, '0');
15
- }
16
- else {
17
- padded = ('' + input).padStart(n, '0');
18
- }
19
- return padded;
20
- }
21
- function parseInteger(str) {
22
- if (str != null && str !== '')
23
- return parseInt(str, 10);
24
- }
25
11
  function parseFloating(str) {
26
12
  if (str != null && str !== '')
27
13
  return parseFloat(str);
28
14
  }
29
15
  function parseMilliseconds(str) {
30
16
  if (str != null && str !== '') {
31
- const f = parseFloat('0.' + str) * 1000;
17
+ const f = parseFloat(`0.${str}`) * 1000;
32
18
  return Math.floor(f);
33
19
  }
34
20
  }
@@ -39,7 +25,7 @@ export function minDate(...dates) {
39
25
  ans = ans[0];
40
26
  }
41
27
  for (let i = 1; i < dates.length; i++) {
42
- if (dates[i].valueOf() < ans.valueOf()) {
28
+ if (dates[i].valueOf() < ans.valueOf() || Number.isNaN(ans.valueOf())) {
43
29
  ans = dates[i];
44
30
  }
45
31
  }
@@ -52,7 +38,7 @@ export function maxDate(...dates) {
52
38
  ans = ans[0];
53
39
  }
54
40
  for (let i = 1; i < dates.length; i++) {
55
- if (dates[i].valueOf() > ans.valueOf()) {
41
+ if (dates[i].valueOf() > ans.valueOf() || Number.isNaN(ans.valueOf())) {
56
42
  ans = dates[i];
57
43
  }
58
44
  }
@@ -80,7 +66,7 @@ export function daysInYear(year) {
80
66
  * @returns {number} Number of days in month
81
67
  */
82
68
  export function daysInMonth(date) {
83
- const d = new Date(date), year = d.getFullYear(), month = d.getMonth() + 1;
69
+ const d = asDate(date), year = d.getFullYear(), month = d.getMonth() + 1;
84
70
  const modMonth = floorMod(month - 1, 12) + 1, modYear = year + (month - modMonth) / 12;
85
71
  if (modMonth === 2) {
86
72
  return isLeapYear(modYear) ? 29 : 28;
@@ -89,51 +75,6 @@ export function daysInMonth(date) {
89
75
  return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][modMonth - 1];
90
76
  }
91
77
  }
92
- // covert a calendar object to a local timestamp (epoch, but with the offset baked in)
93
- function objToLocalTS(obj) {
94
- let d = Date.UTC(obj.year, obj.month - 1, obj.day, obj.hour, obj.minute, obj.second, obj.millisecond);
95
- // for legacy reasons, years between 0 and 99 are interpreted as 19XX; revert that
96
- if (obj.year < 100 && obj.year >= 0) {
97
- let dt = new Date(d);
98
- dt.setUTCFullYear(dt.getUTCFullYear() - 1900);
99
- d = dt.getTime();
100
- }
101
- return +d;
102
- }
103
- function untruncateYear(year) {
104
- if (year > 99) {
105
- return year;
106
- }
107
- else
108
- return year > 60 ? 1900 + year : 2000 + year;
109
- }
110
- // PARSING
111
- function parseZoneInfo(ts, offsetFormat, locale, timeZone) {
112
- const date = new Date(ts), intlOpts = {
113
- hourCycle: 'h23',
114
- year: 'numeric',
115
- month: '2-digit',
116
- day: '2-digit',
117
- hour: '2-digit',
118
- minute: '2-digit',
119
- timeZone,
120
- timeZoneName: offsetFormat
121
- };
122
- const parsed = new Intl.DateTimeFormat(locale, intlOpts)
123
- .formatToParts(date)
124
- .find((m) => m.type === 'timeZoneName');
125
- return parsed?.value;
126
- }
127
- // signedOffset('-5', '30') -> -330
128
- function signedOffset(offHourStr, offMinuteStr) {
129
- let offHour = parseInt(offHourStr, 10);
130
- // don't || this because we want to preserve -0
131
- if (Number.isNaN(offHour)) {
132
- offHour = 0;
133
- }
134
- const offMin = parseInt(offMinuteStr, 10) || 0, offMinSigned = offHour < 0 || Object.is(offHour, -0) ? -offMin : offMin;
135
- return offHour * 60 + offMinSigned;
136
- }
137
78
  // COERCION
138
79
  export function asNumber(value) {
139
80
  const numericValue = Number(value);
@@ -141,24 +82,6 @@ export function asNumber(value) {
141
82
  throw new Error(`Invalid unit value ${value}`);
142
83
  return numericValue;
143
84
  }
144
- function formatOffset(offset, format) {
145
- const hours = Math.trunc(Math.abs(offset / 60)), minutes = Math.trunc(Math.abs(offset % 60)), sign = offset >= 0 ? '+' : '-';
146
- switch (format) {
147
- case 'short':
148
- return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`;
149
- case 'narrow':
150
- return `${sign}${hours}${minutes > 0 ? `:${minutes}` : ''}`;
151
- case 'techie':
152
- return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`;
153
- default:
154
- throw new RangeError(`Value format ${format} is out of range for property format`);
155
- }
156
- }
157
- function timeObject(obj) {
158
- const { hour, minute, second, millisecond } = obj;
159
- return { hour, minute, second, millisecond };
160
- }
161
- const ianaRegex = /[A-Za-z_+-]{1,256}(?::?\/[A-Za-z0-9_+-]{1,256}(?:\/[A-Za-z0-9_+-]{1,256})?)?/;
162
85
  const isoDuration = /^-?P(?:(?:(-?\d{1,20}(?:\.\d{1,20})?)Y)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20}(?:\.\d{1,20})?)W)?(?:(-?\d{1,20}(?:\.\d{1,20})?)D)?(?:T(?:(-?\d{1,20}(?:\.\d{1,20})?)H)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20})(?:[.,](-?\d{1,20}))?S)?)?)$/;
163
86
  function extractISODuration(match) {
164
87
  const [s, yearStr, monthStr, weekStr, dayStr, hourStr, minuteStr, secondStr, millisecondsStr] = match;
@@ -197,16 +120,15 @@ function createUTCDate(...args) {
197
120
  let date, y = args[0];
198
121
  // the Date.UTC function remaps years 0-99 to 1900-1999
199
122
  if (y < 100 && y >= 0) {
200
- args = Array.from(arguments);
201
123
  // preserve leap years using a full 400 year cycle, then reset
202
124
  args[0] = y + 400;
203
125
  date = new Date(Date.UTC.apply(null, args));
204
- if (isFinite(date.getUTCFullYear())) {
126
+ if (Number.isFinite(date.getUTCFullYear())) {
205
127
  date.setUTCFullYear(y);
206
128
  }
207
129
  }
208
130
  else {
209
- date = new Date(Date.UTC.apply(null, arguments));
131
+ date = new Date(Date.UTC.apply(null, args));
210
132
  }
211
133
  return date;
212
134
  }
@@ -217,26 +139,6 @@ function firstWeekOffset(year, dow, doy) {
217
139
  fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
218
140
  return -fwdlw + fwd - 1;
219
141
  }
220
- // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
221
- function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
222
- var localWeekday = (7 + weekday - dow) % 7, weekOffset = firstWeekOffset(year, dow, doy), dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, resYear, resDayOfYear;
223
- if (dayOfYear <= 0) {
224
- resYear = year - 1;
225
- resDayOfYear = daysInYear(resYear) + dayOfYear;
226
- }
227
- else if (dayOfYear > daysInYear(year)) {
228
- resYear = year + 1;
229
- resDayOfYear = dayOfYear - daysInYear(year);
230
- }
231
- else {
232
- resYear = year;
233
- resDayOfYear = dayOfYear;
234
- }
235
- return {
236
- year: resYear,
237
- dayOfYear: resDayOfYear
238
- };
239
- }
240
142
  /**
241
143
  * Returns the number of weeks in the specified year.
242
144
  * @param year
@@ -249,14 +151,13 @@ export function weeksInYear(year, firstDayOfWeek = 1, firstDayOfYear = 1) {
249
151
  }
250
152
  /**
251
153
  * Returns the week of the year for the specified date
252
- * @param year
253
- * @param dayOfYear
254
- * @param firstDayOfWeek
255
- * @param firstDayOfYear
256
- * @returns
154
+ * @param date The date value
155
+ * @param firstDayOfWeek The weekday a week starts on, where Sunday is 0
156
+ * @param firstDayOfYear Picks week 1: the week holding January `7 + firstDayOfWeek - firstDayOfYear`, so `1, 4` gives ISO weeks
157
+ * @returns The week and the year that week belongs to
257
158
  */
258
159
  export function weekOfYear(date, firstDayOfWeek = 1, firstDayOfYear = 1) {
259
- const year = new Date(date).getFullYear();
160
+ const year = asDate(date).getFullYear();
260
161
  const doy = dayOfYear(date);
261
162
  let weekOffset = firstWeekOffset(year, firstDayOfWeek, firstDayOfYear), week = Math.floor((doy - weekOffset - 1) / 7) + 1, resWeek, resYear;
262
163
  if (week < 1) {
@@ -280,11 +181,12 @@ export function isoWeeksInYear(year) {
280
181
  return weeksInYear(year, 1, 4);
281
182
  }
282
183
  export function dayOfYear(input) {
283
- input = new Date(input);
184
+ input = asDate(input);
284
185
  input.setHours(0);
285
186
  input.setMinutes(0);
286
187
  input.setSeconds(0);
287
188
  input.setMilliseconds(0);
288
- let start = new Date(input.getFullYear(), 0, 1, 0, 0, 0, 0);
189
+ const start = new Date(input);
190
+ start.setMonth(0, 1);
289
191
  return Math.round((+input - start.getTime()) / 864e5) + 1;
290
192
  }