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,4 +1,4 @@
1
- export declare type DurationUnit = 'year' | 'years' | 'quarter' | 'quarters' | 'month' | 'months' | 'week' | 'weeks' | 'day' | 'days' | 'hour' | 'hours' | 'minute' | 'minutes' | 'second' | 'seconds' | 'millisecond' | 'milliseconds';
1
+ export type DurationUnit = 'year' | 'years' | 'quarter' | 'quarters' | 'month' | 'months' | 'week' | 'weeks' | 'day' | 'days' | 'hour' | 'hours' | 'minute' | 'minutes' | 'second' | 'seconds' | 'millisecond' | 'milliseconds';
2
2
  export interface TDuration {
3
3
  years?: number;
4
4
  months?: number;
@@ -14,6 +14,7 @@ export declare class Duration {
14
14
  values: TDuration;
15
15
  private matrix;
16
16
  constructor(dur?: number | TDuration | Duration, exact?: boolean);
17
+ private withValues;
17
18
  /** Get the years. */
18
19
  get years(): number;
19
20
  /** Get the months. */
@@ -24,7 +25,7 @@ export declare class Duration {
24
25
  get days(): number;
25
26
  /** Get the hours. */
26
27
  get hours(): number;
27
- /** et the minutes. */
28
+ /** Get the minutes. */
28
29
  get minutes(): number;
29
30
  /** Get the seconds. */
30
31
  get seconds(): number;
@@ -33,9 +34,9 @@ export declare class Duration {
33
34
  /**
34
35
  * Get the value of unit.
35
36
  * @param {string} unit - a unit such as 'minute' or 'day'
36
- * @example Duration.fromObject({years: 2, days: 3}).get('years') // 2
37
- * @example Duration.fromObject({years: 2, days: 3}).get('months') // 0
38
- * @example Duration.fromObject({years: 2, days: 3}).get('days') // 3
37
+ * @example new Duration({years: 2, days: 3}).get('years') // 2
38
+ * @example new Duration({years: 2, days: 3}).get('months') // 0
39
+ * @example new Duration({years: 2, days: 3}).get('days') // 3
39
40
  * @return {number}
40
41
  */
41
42
  get(unit: string): number;
@@ -85,11 +86,11 @@ export declare class Duration {
85
86
  /**
86
87
  * Returns an ISO 8601-compliant string representation of this Duration.
87
88
  * @see https://en.wikipedia.org/wiki/ISO_8601#Durations
88
- * @example Duration.fromObject({ years: 3, seconds: 45 }).toString() // 'P3YT45S'
89
- * @example Duration.fromObject({ months: 4, seconds: 45 }).toString() // 'P4MT45S'
90
- * @example Duration.fromObject({ months: 5 }).toString() // 'P5M'
91
- * @example Duration.fromObject({ minutes: 5 }).toString() // 'PT5M'
92
- * @example Duration.fromObject({ milliseconds: 6 }).toString() // 'PT0.006S'
89
+ * @example new Duration({ years: 3, seconds: 45 }).toString() // 'P3YT45S'
90
+ * @example new Duration({ months: 4, seconds: 45 }).toString() // 'P4MT45S'
91
+ * @example new Duration({ months: 5 }).toString() // 'P5M'
92
+ * @example new Duration({ minutes: 5 }).toString() // 'PT5M'
93
+ * @example new Duration({ milliseconds: 6 }).toString() // 'PT0.006S'
93
94
  * @return {string}
94
95
  */
95
96
  toISOString(): string;
@@ -115,23 +115,26 @@ const orderedUnits = [
115
115
  ];
116
116
  const reverseUnits = orderedUnits.slice(0).reverse();
117
117
  export class Duration {
118
- values;
118
+ values = {};
119
119
  matrix = casualMatrix;
120
120
  constructor(dur = {}, exact = false) {
121
121
  if (dur instanceof Duration) {
122
122
  this.values = { ...dur.values };
123
123
  }
124
- else if (isNumber(dur)) {
124
+ else if (typeof dur === 'number') {
125
125
  this.values = { milliseconds: dur };
126
126
  }
127
127
  else if (isObject(dur)) {
128
128
  this.values = normalizeUnits(dur);
129
129
  }
130
- if (exact) {
130
+ if (exact || (dur instanceof Duration && dur.matrix === exactMatrix)) {
131
131
  // Set the conversion matrix to use
132
132
  this.matrix = exactMatrix;
133
133
  }
134
134
  }
135
+ withValues(values) {
136
+ return new Duration(values, this.matrix === exactMatrix);
137
+ }
135
138
  /** Get the years. */
136
139
  get years() {
137
140
  return this.values.years || 0;
@@ -152,7 +155,7 @@ export class Duration {
152
155
  get hours() {
153
156
  return this.values.hours || 0;
154
157
  }
155
- /** et the minutes. */
158
+ /** Get the minutes. */
156
159
  get minutes() {
157
160
  return this.values.minutes || 0;
158
161
  }
@@ -167,13 +170,13 @@ export class Duration {
167
170
  /**
168
171
  * Get the value of unit.
169
172
  * @param {string} unit - a unit such as 'minute' or 'day'
170
- * @example Duration.fromObject({years: 2, days: 3}).get('years') // 2
171
- * @example Duration.fromObject({years: 2, days: 3}).get('months') // 0
172
- * @example Duration.fromObject({years: 2, days: 3}).get('days') // 3
173
+ * @example new Duration({years: 2, days: 3}).get('years') // 2
174
+ * @example new Duration({years: 2, days: 3}).get('months') // 0
175
+ * @example new Duration({years: 2, days: 3}).get('days') // 3
173
176
  * @return {number}
174
177
  */
175
178
  get(unit) {
176
- return this[UNITS_PLURAL[unit.toLowerCase()]];
179
+ return this.values[UNITS_PLURAL[unit.toLowerCase()]] ?? 0;
177
180
  }
178
181
  add(duration) {
179
182
  const dur = new Duration(duration);
@@ -183,7 +186,7 @@ export class Duration {
183
186
  result[k] = dur.get(k) + this.get(k);
184
187
  }
185
188
  }
186
- return new Duration(result);
189
+ return this.withValues(result);
187
190
  }
188
191
  /**
189
192
  * Make this Duration shorter by the specified amount. Return a newly-constructed Duration.
@@ -208,7 +211,7 @@ export class Duration {
208
211
  for (const k of Object.keys(this.values)) {
209
212
  negated[k] = this.values[k] === 0 ? 0 : -this.values[k];
210
213
  }
211
- return new Duration(negated);
214
+ return this.withValues(negated);
212
215
  }
213
216
  /**
214
217
  * Return the absolute values of this Duration.
@@ -224,7 +227,7 @@ export class Duration {
224
227
  for (const k of Object.keys(this.values)) {
225
228
  parts[k] = Math.abs(this.values[k]);
226
229
  }
227
- return new Duration(parts);
230
+ return this.withValues(parts);
228
231
  }
229
232
  shiftToAll() {
230
233
  return this.shiftTo('years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds');
@@ -273,59 +276,61 @@ export class Duration {
273
276
  key === lastUnit ? accumulated[key] : accumulated[key] / this.matrix[lastUnit][key];
274
277
  }
275
278
  }
276
- return new Duration(built);
279
+ return this.withValues(built);
277
280
  }
278
281
  total(unit) {
279
282
  return this.shiftTo(unit).get(unit);
280
283
  }
281
284
  exact(unit) {
285
+ if (unit)
286
+ return new Duration(this, true).total(unit);
282
287
  this.matrix = exactMatrix;
283
- if (!unit)
284
- return this;
285
- return this.total(unit);
288
+ return this;
286
289
  }
287
290
  toObject() {
288
291
  return { ...this.values };
289
292
  }
290
293
  normalize() {
291
- normalizeValues(this.matrix, this.values);
292
- return new Duration(this.values);
294
+ const values = this.toObject();
295
+ normalizeValues(this.matrix, values);
296
+ return this.withValues(values);
293
297
  }
294
298
  rescale() {
295
299
  const dur = this.normalize().shiftToAll();
296
300
  const vals = removeZeroes(dur.values);
297
- return new Duration(vals);
301
+ return this.withValues(vals);
298
302
  }
299
303
  /**
300
304
  * Returns an ISO 8601-compliant string representation of this Duration.
301
305
  * @see https://en.wikipedia.org/wiki/ISO_8601#Durations
302
- * @example Duration.fromObject({ years: 3, seconds: 45 }).toString() // 'P3YT45S'
303
- * @example Duration.fromObject({ months: 4, seconds: 45 }).toString() // 'P4MT45S'
304
- * @example Duration.fromObject({ months: 5 }).toString() // 'P5M'
305
- * @example Duration.fromObject({ minutes: 5 }).toString() // 'PT5M'
306
- * @example Duration.fromObject({ milliseconds: 6 }).toString() // 'PT0.006S'
306
+ * @example new Duration({ years: 3, seconds: 45 }).toString() // 'P3YT45S'
307
+ * @example new Duration({ months: 4, seconds: 45 }).toString() // 'P4MT45S'
308
+ * @example new Duration({ months: 5 }).toString() // 'P5M'
309
+ * @example new Duration({ minutes: 5 }).toString() // 'PT5M'
310
+ * @example new Duration({ milliseconds: 6 }).toString() // 'PT0.006S'
307
311
  * @return {string}
308
312
  */
309
313
  toISOString() {
310
314
  let s = 'P';
315
+ const months = this.get('quarters') * 3 + this.months;
311
316
  if (this.years !== 0)
312
- s += this.years + 'Y';
313
- if (this.months !== 0)
314
- s += this.months + 'M';
317
+ s += `${this.years}Y`;
318
+ if (months !== 0)
319
+ s += `${months}M`;
315
320
  if (this.weeks !== 0)
316
- s += this.weeks + 'W';
321
+ s += `${this.weeks}W`;
317
322
  if (this.days !== 0)
318
- s += this.days + 'D';
323
+ s += `${this.days}D`;
319
324
  if (this.hours !== 0 || this.minutes !== 0 || this.seconds !== 0 || this.milliseconds !== 0)
320
325
  s += 'T';
321
326
  if (this.hours !== 0)
322
- s += this.hours + 'H';
327
+ s += `${this.hours}H`;
323
328
  if (this.minutes !== 0)
324
- s += this.minutes + 'M';
329
+ s += `${this.minutes}M`;
325
330
  if (this.seconds !== 0 || this.milliseconds !== 0)
326
331
  // this will handle "floating point madness" by removing extra decimal places
327
332
  // https://stackoverflow.com/questions/588004/is-floating-point-math-broken
328
- s += round(this.seconds + this.milliseconds / 1000, 3) + 'S';
333
+ s += `${round(this.seconds + this.milliseconds / 1000, 3)}S`;
329
334
  if (s === 'P')
330
335
  s += 'T0S';
331
336
  return s;
@@ -357,7 +362,8 @@ export class Duration {
357
362
  function normalizeUnits(obj) {
358
363
  const vals = {};
359
364
  for (const unit of Object.keys(obj)) {
360
- vals[UNITS_PLURAL[unit]] = obj[unit];
365
+ if (obj[unit] != null)
366
+ vals[UNITS_PLURAL[unit]] = obj[unit];
361
367
  }
362
368
  return vals;
363
369
  }
@@ -1,3 +1,4 @@
1
+ export declare function fromFields(utc: boolean, year: number, month: any, day: any, hour: any, minute: any, second: any, ms: any): Date;
1
2
  /**
2
3
  * Returns a new Date from the given arguments.
3
4
  * @param {string | number | Date} date The date value
@@ -7,11 +8,13 @@
7
8
  export declare function asDate(date: string | number | Date, utc?: boolean): Date;
8
9
  /**
9
10
  * Returns a localized string representation of this date, according to the given format string. Format codes use the same specification as {@link https://momentjs.com/docs/#/displaying/format/ moment}.
10
- * @param {string} str The format string to use
11
+ * @param {string} formatStr The format string to use
12
+ * @param {Date} date The date value
13
+ * @param {?string} locale The locale to use when formatting (default is system locale).
11
14
  * @see {@link https://momentjs.com/docs/#/displaying/format/ List of formats}
12
15
  * @example
13
16
  ```js
14
- formatDate(new Date(), 'MM/DD/YYYY') // '10/31/2022'
17
+ formatDate('MM/DD/YYYY', new Date()) // '10/31/2022'
15
18
  ```
16
19
  */
17
20
  export declare function formatDate(formatStr: string, date: Date, locale?: string): string;
@@ -1,16 +1,32 @@
1
1
  const NATIVE_DATE = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/;
2
- const REGEX_PARSE = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/;
3
- const REGEX_FORMAT = /\[([^\]]+)]|Y{1,4}|M{1,4}|Q|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|z{1,4}|N{1,4}|LTS?|L{1,4}|l{1,4}|x|X|SSS/g;
2
+ const REGEX_FORMAT = /\[([^\]]+)]|Y{1,4}|M{1,4}|Q|Do|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|z{1,4}|N{1,4}|LTS?|L{1,4}|l{1,4}|x|X|SSS/g;
4
3
  const FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ssZ';
4
+ const INVALID_DATE_STRING = 'Invalid Date';
5
+ const ORDINAL_SUFFIXES = { one: 'st', two: 'nd', few: 'rd', other: 'th' };
5
6
  function padStart(string, length, pad) {
6
7
  return String(string).padStart(length, pad);
7
8
  }
9
+ function padYear(year) {
10
+ return `${year < 0 ? '-' : ''}${padStart(Math.abs(year), 4, '0')}`;
11
+ }
8
12
  function padZoneStr(instance) {
9
13
  const negMinutes = -instance.getTimezoneOffset();
10
14
  const minutes = Math.abs(negMinutes);
11
15
  const hourOffset = Math.floor(minutes / 60);
12
16
  const minuteOffset = minutes % 60;
13
- return `${negMinutes <= 0 ? '+' : '-'}${padStart(hourOffset, 2, '0')}:${padStart(minuteOffset, 2, '0')}`;
17
+ return `${negMinutes >= 0 ? '+' : '-'}${padStart(hourOffset, 2, '0')}:${padStart(minuteOffset, 2, '0')}`;
18
+ }
19
+ export function fromFields(utc, year, month, day, hour, minute, second, ms) {
20
+ // Date reads the years 0 to 99 as 1900 to 1999, 400 years later the leap years still match
21
+ const shift = year >= 0 && year < 100 ? 400 : 0;
22
+ if (utc) {
23
+ const date = new Date(Date.UTC(year + shift, month, day, hour, minute, second, ms));
24
+ date.setUTCFullYear(date.getUTCFullYear() - shift);
25
+ return date;
26
+ }
27
+ const date = new Date(year + shift, month, day, hour, minute, second, ms);
28
+ date.setFullYear(date.getFullYear() - shift);
29
+ return date;
14
30
  }
15
31
  /**
16
32
  * Returns a new Date from the given arguments.
@@ -29,22 +45,13 @@ export function asDate(date, utc = false) {
29
45
  const d = date.match(NATIVE_DATE);
30
46
  if (d) {
31
47
  const m = d[2] - 1 || 0;
32
- const ms = (d[7] || '0').slice(0, 3);
33
- if (utc) {
34
- return new Date(Date.UTC(d[1], m, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms));
35
- }
36
- return new Date(d[1], m, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms);
48
+ const ms = (d[7] || '').padEnd(3, '0').slice(0, 3);
49
+ return fromFields(utc, Number(d[1]), m, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms);
37
50
  }
38
51
  }
39
52
  const fromDate = new Date(date);
40
53
  if (utc) {
41
- fromDate.setFullYear(fromDate.getUTCFullYear());
42
- fromDate.setMonth(fromDate.getUTCMonth());
43
- fromDate.setDate(fromDate.getUTCDate());
44
- fromDate.setHours(fromDate.getUTCHours());
45
- fromDate.setMinutes(fromDate.getUTCMinutes());
46
- fromDate.setSeconds(fromDate.getUTCSeconds());
47
- fromDate.setMilliseconds(fromDate.getUTCMilliseconds());
54
+ return fromFields(false, fromDate.getUTCFullYear(), fromDate.getUTCMonth(), fromDate.getUTCDate(), fromDate.getUTCHours(), fromDate.getUTCMinutes(), fromDate.getUTCSeconds(), fromDate.getUTCMilliseconds());
48
55
  }
49
56
  return fromDate;
50
57
  }
@@ -56,7 +63,6 @@ const STRFTIME = {
56
63
  // weekday
57
64
  '%A': { weekday: l },
58
65
  '%a': { weekday: s },
59
- '%aa': { weekday: n },
60
66
  // day
61
67
  '%d': { day: dd },
62
68
  '%-d': { day: n },
@@ -89,7 +95,6 @@ const STRFTIME = {
89
95
  // era
90
96
  '%N': { era: l, year: dd },
91
97
  '%n': { era: s, year: dd },
92
- '%nn': { era: n, year: 'narrow' },
93
98
  // timeZoneName
94
99
  // '%Z': { timeZoneName: 'longGeneric', year: dd },
95
100
  // '%-Z': { timeZoneName: 'shortGeneric', year: dd },
@@ -136,14 +141,12 @@ const TOKENS = {
136
141
  NNN: { era: s, year: dd },
137
142
  NNNN: { era: l, year: dd },
138
143
  // timeZoneName
139
- ZZ: { timeZoneName: 'longOffset', year: dd },
140
- Z: { timeZoneName: 'shortOffset', year: dd },
141
144
  zzz: { timeZoneName: l, year: dd },
142
145
  z: { timeZoneName: s, year: dd }
143
146
  };
144
147
  const HOUR_PARTS = { '%I': 1, '%-I': 1, '%K': 1, '%k': 1 };
145
- const ERA_PARTS = { '%n': 1, '%N': 1, '%nn': 1 };
146
- const TZ_PARTS = { '%ZZ': 1, '%-ZZ': 1, '%-z': 1, '%z': 1, '%-Z': 1, '%Z': 1 };
148
+ const ERA_PARTS = { '%n': 1, '%N': 1 };
149
+ const TZ_PARTS = { '%-z': 1, '%z': 1, '%-Z': 1, '%Z': 1 };
147
150
  function formatter(locale, opts) {
148
151
  return new Intl.DateTimeFormat(locale, opts);
149
152
  }
@@ -154,20 +157,30 @@ function formatMeridiem(hour, isLowercase) {
154
157
  const m = hour < 12 ? 'AM' : 'PM';
155
158
  return isLowercase ? m.toLowerCase() : m;
156
159
  }
157
- function formatPart(locale, t, date) {
158
- const value = formatter(locale, TOKENS[t]).format(date);
159
- return value.split(/\s+/g).slice(1).join(' ');
160
+ function formatOrdinal(day, locale) {
161
+ const rules = new Intl.PluralRules(locale, { type: 'ordinal' });
162
+ const isEnglish = rules.resolvedOptions().locale.split('-')[0] === 'en';
163
+ return isEnglish ? `${day}${ORDINAL_SUFFIXES[rules.select(day)]}` : String(day);
164
+ }
165
+ function formatPart(locale, opts, date, type) {
166
+ return (formatter(locale, opts)
167
+ .formatToParts(date)
168
+ .find((part) => part.type === type)?.value ?? '');
160
169
  }
161
170
  /**
162
171
  * Returns a localized string representation of this date, according to the given format string. Format codes use the same specification as {@link https://momentjs.com/docs/#/displaying/format/ moment}.
163
- * @param {string} str The format string to use
172
+ * @param {string} formatStr The format string to use
173
+ * @param {Date} date The date value
174
+ * @param {?string} locale The locale to use when formatting (default is system locale).
164
175
  * @see {@link https://momentjs.com/docs/#/displaying/format/ List of formats}
165
176
  * @example
166
177
  ```js
167
- formatDate(new Date(), 'MM/DD/YYYY') // '10/31/2022'
178
+ formatDate('MM/DD/YYYY', new Date()) // '10/31/2022'
168
179
  ```
169
180
  */
170
181
  export function formatDate(formatStr, date, locale) {
182
+ if (Number.isNaN(date.getTime()))
183
+ return INVALID_DATE_STRING;
171
184
  const str = formatStr || FORMAT_DEFAULT;
172
185
  const zoneStr = padZoneStr(date);
173
186
  const time = {
@@ -187,12 +200,12 @@ export function formatDate(formatStr, date, locale) {
187
200
  };
188
201
  const matches = {
189
202
  ...time,
190
- N: formatPart(locale, 'N', date),
191
- NN: formatPart(locale, 'NN', date),
192
- NNN: formatPart(locale, 'NNN', date),
193
- NNNN: formatPart(locale, 'NNNN', date),
194
- YY: String(date.getFullYear()).slice(-2),
195
- YYYY: date.getFullYear(),
203
+ N: formatPart(locale, TOKENS.N, date, 'era'),
204
+ NN: formatPart(locale, TOKENS.NN, date, 'era'),
205
+ NNN: formatPart(locale, TOKENS.NNN, date, 'era'),
206
+ NNNN: formatPart(locale, TOKENS.NNNN, date, 'era'),
207
+ YY: padStart(Math.abs(date.getFullYear()) % 100, 2, '0'),
208
+ YYYY: padYear(date.getFullYear()),
196
209
  M: date.getMonth() + 1,
197
210
  MM: padStart(date.getMonth() + 1, 2, '0'),
198
211
  MMM: formatter(locale, TOKENS.MMM).format(date),
@@ -200,13 +213,14 @@ export function formatDate(formatStr, date, locale) {
200
213
  Q: Math.ceil((date.getMonth() + 1) / 3),
201
214
  D: date.getDate(),
202
215
  DD: padStart(date.getDate(), 2, '0'),
216
+ Do: formatOrdinal(date.getDate(), locale),
203
217
  d: date.getDay(),
204
218
  ddd: formatter(locale, TOKENS.ddd).format(date),
205
219
  dddd: formatter(locale, TOKENS.dddd).format(date),
206
- Z: formatPart(locale, 'Z', date),
207
- ZZ: formatPart(locale, 'ZZ', date),
208
- z: formatPart(locale, 'z', date),
209
- zzz: formatPart(locale, 'zzz', date),
220
+ Z: zoneStr,
221
+ ZZ: zoneStr.replace(':', ''),
222
+ z: formatPart(locale, TOKENS.z, date, 'timeZoneName'),
223
+ zzz: formatPart(locale, TOKENS.zzz, date, 'timeZoneName'),
210
224
  LT: `${time.h}:${time.mm} ${time.A}`,
211
225
  LTS: `${time.h}:${time.mm}:${time.ss} ${time.A}`,
212
226
  L: formatter(locale, DATE_SHORT_DD).format(date),
@@ -218,7 +232,7 @@ export function formatDate(formatStr, date, locale) {
218
232
  lll: formatter(locale, DATE_MED_TIME).format(date),
219
233
  llll: formatter(locale, DATE_MED_WITH_WEEKDAY).format(date)
220
234
  };
221
- return str.replace(REGEX_FORMAT, (match, $1) => $1 || matches[match] || zoneStr.replace(':', '')); // 'ZZ'
235
+ return str.replace(REGEX_FORMAT, (match, $1) => $1 ?? matches[match] ?? match);
222
236
  }
223
237
  /**
224
238
  * Formats a date using a formatting string in the {@link https://strftime.org/ strftime} format, in any given locale.
@@ -234,9 +248,11 @@ strftime('%A, %d/%m/%y', new Date())
234
248
  ```
235
249
  */
236
250
  export function strftime(fmt, date, locale) {
251
+ if (date != null && Number.isNaN(+date))
252
+ return INVALID_DATE_STRING;
237
253
  const results = [];
238
254
  let dayPeriod = '';
239
- let dayPeriodIndex = -1;
255
+ const dayPeriodIndexes = [];
240
256
  for (let i = 0; i < fmt.length; i++) {
241
257
  let part = fmt[i];
242
258
  if (part === '%') {
@@ -248,37 +264,43 @@ export function strftime(fmt, date, locale) {
248
264
  if (fmt[i] === '-') {
249
265
  part += fmt[i++];
250
266
  }
251
- part += fmt[i];
252
- // add any repeated characters that come after
253
- while (i < fmt.length - 1 && fmt[i + 1] === fmt[i])
267
+ part += fmt[i] ?? '';
268
+ // a doubled letter is a directive of its own only when the table lists it
269
+ if (fmt[i + 1] === fmt[i] && STRFTIME[part + fmt[i]])
254
270
  part += fmt[++i];
271
+ if (!STRFTIME[part]) {
272
+ results.push(part);
273
+ continue;
274
+ }
255
275
  if (HOUR_PARTS[part]) {
256
- const res = formatter(locale, STRFTIME[part]).format(date).split(/\s+/g);
276
+ results.push(formatPart(locale, STRFTIME[part], date, 'hour'));
257
277
  // save the AM/PM for the correct spot
258
- results.push(res[0]);
259
- dayPeriod = res[1];
278
+ dayPeriod = formatPart(locale, STRFTIME[part], date, 'dayPeriod');
260
279
  continue;
261
280
  }
262
281
  if (part === '%P' || part === '%p') {
263
282
  // leave this flag in place for later
264
- dayPeriodIndex = results.length;
265
- if (part === '%p' && dayPeriod) {
266
- dayPeriod = dayPeriod.toLowerCase();
267
- }
283
+ dayPeriodIndexes.push(results.length);
268
284
  results.push(part);
269
285
  continue;
270
286
  }
271
287
  if (ERA_PARTS[part] || TZ_PARTS[part]) {
272
- // the formatting includes the date/year so we take what comes after
273
- const res = formatter(locale, STRFTIME[part]).format(date).split(/\s+/g);
274
- results.push(res.slice(1).join(' '));
288
+ const type = ERA_PARTS[part] ? 'era' : 'timeZoneName';
289
+ results.push(formatPart(locale, STRFTIME[part], date, type));
275
290
  continue;
276
291
  }
277
- // format any other parts
278
- let res = formatter(locale, STRFTIME[part]).format(date);
292
+ // format any other parts, numbers without the unit text of the locale
293
+ const [type, style] = Object.entries(STRFTIME[part]).at(-1);
294
+ let res = style === n || style === dd
295
+ ? formatPart(locale, STRFTIME[part], date, type)
296
+ : formatter(locale, STRFTIME[part]).format(date);
279
297
  // fix bugs in time formatter
280
298
  if ((part === '%S' || part === '%M') && res.length === 1)
281
- res = '0' + res;
299
+ res = `0${res}`;
300
+ if (part === '%-H') {
301
+ const hours = (date === undefined ? new Date() : new Date(date)).getHours();
302
+ res = new Intl.NumberFormat(locale).format(hours);
303
+ }
282
304
  results.push(res);
283
305
  }
284
306
  else {
@@ -286,12 +308,12 @@ export function strftime(fmt, date, locale) {
286
308
  results.push(part);
287
309
  }
288
310
  }
289
- // insert the AM/PM at the correct spot
290
- if (dayPeriodIndex >= 0)
291
- results[dayPeriodIndex] = dayPeriod || '';
311
+ // insert the AM/PM at the correct spots
312
+ for (const index of dayPeriodIndexes) {
313
+ results[index] = results[index] === '%p' ? dayPeriod.toLowerCase() : dayPeriod;
314
+ }
292
315
  return results.join('').trim();
293
316
  }
294
- const DATETIME_DEFAULT = {};
295
317
  /** L: 09/04/1983 */
296
318
  const DATE_SHORT_DD = {
297
319
  year: n,
@@ -344,7 +366,7 @@ const DATE_MED_WITH_WEEKDAY = {
344
366
  minute: n,
345
367
  hourCycle: 'h12'
346
368
  };
347
- /** LLLL: Tuesday, October 14, 1983, 8:30 PM */
369
+ /** LLLL: Friday, October 14, 1983, 8:30 PM */
348
370
  const DATE_HUGE = {
349
371
  year: n,
350
372
  month: l,
@@ -354,153 +376,3 @@ const DATE_HUGE = {
354
376
  minute: n,
355
377
  hourCycle: 'h12'
356
378
  };
357
- /** LT: 09:30 AM */
358
- const TIME_SIMPLE = {
359
- hour: n,
360
- minute: n,
361
- hourCycle: 'h12'
362
- };
363
- /** LTS: 09:30:23 AM */
364
- const TIME_WITH_SECONDS = {
365
- hour: n,
366
- minute: n,
367
- second: n,
368
- hourCycle: 'h12'
369
- };
370
- /** 09:30:23 AM EDT */
371
- const TIME_WITH_SHORT_OFFSET = {
372
- hour: n,
373
- minute: n,
374
- second: n,
375
- timeZoneName: s,
376
- hourCycle: 'h12'
377
- };
378
- /** 09:30:23 AM Eastern Daylight Time */
379
- const TIME_WITH_LONG_OFFSET = {
380
- hour: n,
381
- minute: n,
382
- second: n,
383
- timeZoneName: l,
384
- hourCycle: 'h12'
385
- };
386
- /** 09:30 */
387
- const TIME_24_SIMPLE = {
388
- hour: n,
389
- minute: n,
390
- hourCycle: 'h23'
391
- };
392
- /** 09:30:23 */
393
- const TIME_24_WITH_SECONDS = {
394
- hour: n,
395
- minute: n,
396
- second: n,
397
- hourCycle: 'h23'
398
- };
399
- /** 09:30:23 EDT */
400
- const TIME_24_WITH_SHORT_OFFSET = {
401
- hour: n,
402
- minute: n,
403
- second: n,
404
- hourCycle: 'h23',
405
- timeZoneName: s
406
- };
407
- /** 09:30:23 Eastern Daylight Time */
408
- const TIME_24_WITH_LONG_OFFSET = {
409
- hour: n,
410
- minute: n,
411
- second: n,
412
- hourCycle: 'h23',
413
- timeZoneName: l
414
- };
415
- /** 10/14/1983, 9:30 AM */
416
- const DATETIME_SHORT = {
417
- year: n,
418
- month: n,
419
- day: n,
420
- hour: n,
421
- minute: n,
422
- hourCycle: 'h12'
423
- };
424
- /** 10/14/1983, 9:30:33 AM */
425
- const DATETIME_SHORT_WITH_SECONDS = {
426
- year: n,
427
- month: n,
428
- day: n,
429
- hour: n,
430
- minute: n,
431
- second: n,
432
- hourCycle: 'h12'
433
- };
434
- /** Oct 14, 1983, 9:30 AM */
435
- const DATETIME_MED = {
436
- year: n,
437
- month: s,
438
- day: n,
439
- hour: n,
440
- minute: n,
441
- hourCycle: 'h12'
442
- };
443
- /** Oct 14, 1983, 9:30:33 AM */
444
- const DATETIME_MED_WITH_SECONDS = {
445
- year: n,
446
- month: s,
447
- day: n,
448
- hour: n,
449
- minute: n,
450
- second: n,
451
- hourCycle: 'h12'
452
- };
453
- /** Fri, 14 Oct 1983, 9:30 AM */
454
- const DATETIME_MED_WITH_WEEKDAY = {
455
- year: n,
456
- month: s,
457
- day: n,
458
- weekday: s,
459
- hour: n,
460
- minute: n,
461
- hourCycle: 'h12'
462
- };
463
- /** October 14, 1983, 9:30 AM EDT */
464
- const DATETIME_FULL = {
465
- year: n,
466
- month: l,
467
- day: n,
468
- hour: n,
469
- minute: n,
470
- timeZoneName: s,
471
- hourCycle: 'h12'
472
- };
473
- /** October 14, 1983, 9:30:33 AM EDT */
474
- const DATETIME_FULL_WITH_SECONDS = {
475
- year: n,
476
- month: l,
477
- day: n,
478
- hour: n,
479
- minute: n,
480
- second: n,
481
- timeZoneName: s,
482
- hourCycle: 'h12'
483
- };
484
- /** Friday, October 14, 1983, 9:30 AM Eastern Daylight Time */
485
- const DATETIME_HUGE = {
486
- year: n,
487
- month: l,
488
- day: n,
489
- weekday: l,
490
- hour: n,
491
- minute: n,
492
- timeZoneName: l,
493
- hourCycle: 'h12'
494
- };
495
- /** Friday, October 14, 1983, 9:30:33 AM Eastern Daylight Time */
496
- const DATETIME_HUGE_WITH_SECONDS = {
497
- year: n,
498
- month: l,
499
- day: n,
500
- weekday: l,
501
- hour: n,
502
- minute: n,
503
- second: n,
504
- timeZoneName: l,
505
- hourCycle: 'h12'
506
- };