@fr0st/datetime 6.1.1 → 8.0.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.
@@ -1,5 +1,6 @@
1
+ import { valuesRegExp } from './../helpers.js';
1
2
  import { formatDay, formatMonth, formatNumber, formatOffset } from './format.js';
2
- import { parseDay, parseDayPeriod, parseEra, parseMonth, parseNumber } from './parse.js';
3
+ import { parseDay, parseDayPeriod, parseEra, parseMonth, parseNumber, parseNumberString } from './parse.js';
3
4
  import { getType } from './utility.js';
4
5
  import { getDayPeriods, getDays, getEras, getMonths, numberRegExp } from './values.js';
5
6
 
@@ -13,10 +14,9 @@ export default {
13
14
 
14
15
  G: {
15
16
  key: 'era',
16
- maxLength: 5,
17
17
  regex: (locale, length) => {
18
18
  const type = getType(length);
19
- return getEras(locale, type).join('|');
19
+ return valuesRegExp(getEras(locale, type));
20
20
  },
21
21
  input: (locale, value, length) => {
22
22
  const type = getType(length);
@@ -91,26 +91,28 @@ export default {
91
91
  // quarter
92
92
  Q: {
93
93
  key: 'quarter',
94
+ supportsLength: (length) => length !== 3 && length !== 4,
94
95
  regex: (locale) => numberRegExp(locale),
95
96
  input: (locale, value) => parseNumber(locale, value),
96
97
  output: (datetime, length) =>
97
98
  formatNumber(
98
99
  datetime.getLocale(),
99
100
  datetime.getQuarter(),
100
- length,
101
+ length < 3 ? length : 0,
101
102
  ),
102
103
  },
103
104
 
104
105
  // quarter (standalone)
105
106
  q: {
106
107
  key: 'quarter',
108
+ supportsLength: (length) => length !== 3 && length !== 4,
107
109
  regex: (locale) => numberRegExp(locale),
108
110
  input: (locale, value) => parseNumber(locale, value),
109
111
  output: (datetime, length) =>
110
112
  formatNumber(
111
113
  datetime.getLocale(),
112
114
  datetime.getQuarter(),
113
- length,
115
+ length < 3 ? length : 0,
114
116
  ),
115
117
  },
116
118
 
@@ -119,25 +121,26 @@ export default {
119
121
  // month
120
122
  M: {
121
123
  key: 'month',
124
+ supportsLength: (length, parsing) => !parsing || length !== 5,
122
125
  regex: (locale, length) => {
123
126
  switch (length) {
124
127
  case 5:
125
128
  case 4:
126
- case 3:
129
+ case 3: {
127
130
  const type = getType(length);
128
- return getMonths(locale, type, false).join('|');
131
+ return valuesRegExp(getMonths(locale, type, false));
132
+ }
129
133
  default:
130
134
  return numberRegExp(locale);
131
135
  }
132
136
  },
133
137
  input: (locale, value, length) => {
134
138
  switch (length) {
135
- case 5:
136
- return null;
137
139
  case 4:
138
- case 3:
140
+ case 3: {
139
141
  const type = getType(length);
140
142
  return parseMonth(locale, value, type, false);
143
+ }
141
144
  default:
142
145
  return parseNumber(locale, value);
143
146
  }
@@ -148,9 +151,10 @@ export default {
148
151
  switch (length) {
149
152
  case 5:
150
153
  case 4:
151
- case 3:
154
+ case 3: {
152
155
  const type = getType(length);
153
156
  return formatMonth(locale, month, type, false);
157
+ }
154
158
  default:
155
159
  return formatNumber(locale, month, length);
156
160
  }
@@ -160,25 +164,26 @@ export default {
160
164
  // month (standalone)
161
165
  L: {
162
166
  key: 'month',
167
+ supportsLength: (length, parsing) => !parsing || length !== 5,
163
168
  regex: (locale, length) => {
164
169
  switch (length) {
165
170
  case 5:
166
171
  case 4:
167
- case 3:
172
+ case 3: {
168
173
  const type = getType(length);
169
- return getMonths(locale, type).join('|');
174
+ return valuesRegExp(getMonths(locale, type));
175
+ }
170
176
  default:
171
177
  return numberRegExp(locale);
172
178
  }
173
179
  },
174
180
  input: (locale, value, length) => {
175
181
  switch (length) {
176
- case 5:
177
- return null;
178
182
  case 4:
179
- case 3:
183
+ case 3: {
180
184
  const type = getType(length);
181
185
  return parseMonth(locale, value, type);
186
+ }
182
187
  default:
183
188
  return parseNumber(locale, value);
184
189
  }
@@ -189,9 +194,10 @@ export default {
189
194
  switch (length) {
190
195
  case 5:
191
196
  case 4:
192
- case 3:
197
+ case 3: {
193
198
  const type = getType(length);
194
199
  return formatMonth(locale, month, type);
200
+ }
195
201
  default:
196
202
  return formatNumber(locale, month, length);
197
203
  }
@@ -218,10 +224,11 @@ export default {
218
224
  key: 'weekOfMonth',
219
225
  regex: (locale) => numberRegExp(locale),
220
226
  input: (locale, value) => parseNumber(locale, value),
221
- output: (datetime) =>
227
+ output: (datetime, length) =>
222
228
  formatNumber(
223
229
  datetime.getLocale(),
224
230
  datetime.getWeekOfMonth(),
231
+ length,
225
232
  ),
226
233
  },
227
234
 
@@ -258,25 +265,24 @@ export default {
258
265
  key: 'weekDayInMonth',
259
266
  regex: (locale) => numberRegExp(locale),
260
267
  input: (locale, value) => parseNumber(locale, value),
261
- output: (datetime) =>
268
+ output: (datetime, length) =>
262
269
  formatNumber(
263
270
  datetime.getLocale(),
264
271
  datetime.getWeekDayInMonth(),
272
+ length,
265
273
  ),
266
274
  },
267
275
 
268
276
  // week day name
269
277
  E: {
270
278
  key: 'weekDay',
279
+ supportsLength: (length, parsing) =>
280
+ length !== 6 && (!parsing || length !== 5),
271
281
  regex: (locale, length) => {
272
282
  const type = getType(length);
273
- return getDays(locale, type, false).join('|');
283
+ return valuesRegExp(getDays(locale, type, false));
274
284
  },
275
285
  input: (locale, value, length) => {
276
- if (length === 5) {
277
- return null;
278
- }
279
-
280
286
  const type = getType(length);
281
287
  return parseDay(locale, value, type, false);
282
288
  },
@@ -291,86 +297,64 @@ export default {
291
297
  // week day
292
298
  e: {
293
299
  key: 'weekDay',
294
- maxLength: 5,
300
+ supportsLength: (length, parsing) =>
301
+ length !== 6 && (!parsing || length !== 5),
295
302
  regex: (locale, length) => {
296
- switch (length) {
297
- case 5:
298
- case 4:
299
- case 3:
300
- const type = getType(length);
301
- return getDays(locale, type, false).join('|');
302
- default:
303
- return numberRegExp(locale);
303
+ if (length < 3) {
304
+ return numberRegExp(locale);
304
305
  }
306
+
307
+ const type = getType(length);
308
+ return valuesRegExp(getDays(locale, type, false));
305
309
  },
306
310
  input: (locale, value, length) => {
307
- switch (length) {
308
- case 5:
309
- return null;
310
- case 4:
311
- case 3:
312
- const type = getType(length);
313
- return parseDay(locale, value, type, false);
314
- default:
315
- return parseNumber(locale, value);
311
+ if (length < 3) {
312
+ return parseNumber(locale, value);
316
313
  }
314
+
315
+ const type = getType(length);
316
+ return parseDay(locale, value, type, false);
317
317
  },
318
318
  output: (datetime, length) => {
319
319
  const locale = datetime.getLocale();
320
- switch (length) {
321
- case 5:
322
- case 4:
323
- case 3:
324
- const type = getType(length);
325
- const day = datetime.getDay();
326
- return formatDay(locale, day, type, false);
327
- default:
328
- const weekDay = datetime.getWeekDay();
329
- return formatNumber(locale, weekDay, length);
320
+ if (length < 3) {
321
+ return formatNumber(locale, datetime.getWeekDay(), length);
330
322
  }
323
+
324
+ const type = getType(length);
325
+ return formatDay(locale, datetime.getDay(), type, false);
331
326
  },
332
327
  },
333
328
 
334
329
  // week day (standalone)
335
330
  c: {
336
331
  key: 'weekDay',
337
- maxLength: 5,
332
+ supportsLength: (length, parsing) =>
333
+ length !== 6 && (!parsing || length !== 5),
338
334
  regex: (locale, length) => {
339
- switch (length) {
340
- case 5:
341
- case 4:
342
- case 3:
343
- const type = getType(length);
344
- return getDays(locale, type).join('|');
345
- default:
346
- return numberRegExp(locale);
335
+ if (length < 3) {
336
+ return numberRegExp(locale);
347
337
  }
338
+
339
+ const type = getType(length);
340
+ return valuesRegExp(getDays(locale, type));
348
341
  },
349
342
  input: (locale, value, length) => {
350
- switch (length) {
351
- case 5:
352
- return null;
353
- case 4:
354
- case 3:
355
- const type = getType(length);
356
- return parseDay(locale, value, type);
357
- default:
358
- return parseNumber(locale, value);
343
+ if (length < 3) {
344
+ return parseNumber(locale, value);
359
345
  }
346
+
347
+ const type = getType(length);
348
+ return parseDay(locale, value, type);
360
349
  },
361
350
  output: (datetime, length) => {
362
351
  const locale = datetime.getLocale();
363
- switch (length) {
364
- case 5:
365
- case 4:
366
- case 3:
367
- const type = getType(length);
368
- const day = datetime.getDay();
369
- return formatDay(locale, day, type);
370
- default:
371
- const weekDay = datetime.getWeekDay();
372
- return formatNumber(locale, weekDay);
352
+ if (length < 3) {
353
+ return formatNumber(locale, datetime.getWeekDay());
373
354
  }
355
+
356
+ const type = getType(length);
357
+ return formatDay(locale, datetime.getDay(), type);
374
358
  },
375
359
  },
376
360
 
@@ -378,9 +362,10 @@ export default {
378
362
 
379
363
  a: {
380
364
  key: 'dayPeriod',
365
+ supportsLength: (length) => length <= 4,
381
366
  regex: (locale, length) => {
382
367
  const type = getType(length);
383
- return getDayPeriods(locale, type).join('|');
368
+ return valuesRegExp(getDayPeriods(locale, type));
384
369
  },
385
370
  input: (locale, value, length) => {
386
371
  const type = getType(length);
@@ -487,25 +472,28 @@ export default {
487
472
  S: {
488
473
  key: 'milliseconds',
489
474
  regex: (locale) => numberRegExp(locale),
490
- input: (_) => 0,
491
- output: (datetime, length) =>
492
- formatNumber(
493
- datetime.getLocale(),
494
- `${Math.floor(
495
- datetime.getMilliseconds() *
496
- 1000,
497
- )}`.padEnd(length, '0').slice(0, length),
475
+ input: (locale, value) =>
476
+ parseInt(
477
+ parseNumberString(locale, value)
478
+ .padEnd(3, '0')
479
+ .slice(0, 3),
480
+ 10,
498
481
  ),
482
+ output: (datetime, length) => {
483
+ const milliseconds = `${datetime.getMilliseconds()}`.padStart(3, '0');
484
+ return formatNumber(
485
+ datetime.getLocale(),
486
+ milliseconds.padEnd(length, '0').slice(0, length),
487
+ );
488
+ },
499
489
  },
500
490
 
501
491
  /* TIMEZONE/OFFSET */
502
492
 
503
493
  z: {
494
+ supportsLength: (_, parsing) => !parsing,
504
495
  output: (datetime, length) => {
505
- if (length === 5) {
506
- length = 1;
507
- }
508
- const type = getType(length);
496
+ const type = getType(Math.min(length, 4));
509
497
  return datetime.timeZoneName(type);
510
498
  },
511
499
  },
@@ -513,14 +501,13 @@ export default {
513
501
  Z: {
514
502
  key: 'timeZone',
515
503
  regex: (_, length) => {
516
- switch (length) {
517
- case 5:
518
- return `[\\+\\-]\\d{2}\\:\\d{2}|Z`;
519
- case 4:
520
- return `GMT[\\+\\-]\\d{2}\\:\\d{2}|GMT`;
521
- default:
522
- return `[\\+\\-]\\d{4}`;
504
+ if (length === 5) {
505
+ return `[\\+\\-]\\d{2}\\:\\d{2}(?:\\:\\d{2})?|Z`;
523
506
  }
507
+
508
+ return length >= 4 ?
509
+ `GMT[\\+\\-]\\d{2}\\:\\d{2}|GMT` :
510
+ `[\\+\\-]\\d{4}`;
524
511
  },
525
512
  input: (_, value) => value,
526
513
  output: (datetime, length) => {
@@ -528,31 +515,27 @@ export default {
528
515
 
529
516
  let useColon = true;
530
517
  let prefix = '';
531
- switch (length) {
532
- case 5:
533
- if (!offset) {
534
- return 'Z';
535
- }
536
- break;
537
- case 4:
538
- prefix = 'GMT';
539
-
540
- if (!offset) {
541
- return prefix;
542
- }
543
-
544
- break;
545
- default:
546
- useColon = false;
547
- break;
518
+ if (length === 5) {
519
+ if (!offset) {
520
+ return 'Z';
521
+ }
522
+ } else if (length >= 4) {
523
+ prefix = 'GMT';
524
+
525
+ if (!offset) {
526
+ return prefix;
527
+ }
528
+ } else {
529
+ useColon = false;
548
530
  }
549
531
 
550
- return prefix + formatOffset(offset, useColon);
532
+ return prefix + formatOffset(offset, useColon, false, length === 5);
551
533
  },
552
534
  },
553
535
 
554
536
  O: {
555
537
  key: 'timeZone',
538
+ supportsLength: (length) => length === 1 || length === 4,
556
539
  regex: (_, length) => {
557
540
  switch (length) {
558
541
  case 4:
@@ -584,19 +567,23 @@ export default {
584
567
 
585
568
  V: {
586
569
  key: 'timeZone',
587
- regex: (_) => '([a-zA-Z_\/]+)',
570
+ supportsLength: (length) => length === 2,
571
+ regex: (_) => '([A-Za-z0-9_.+\\-/]+)',
588
572
  input: (_, value) => value,
589
573
  output: (datetime) => datetime.getTimeZone(),
590
574
  },
591
575
 
592
576
  X: {
593
577
  key: 'timeZone',
578
+ supportsLength: (length) => length <= 5,
594
579
  regex: (_, length) => {
595
580
  switch (length) {
596
581
  case 5:
582
+ return `[\\+\\-]\\d{2}\\:\\d{2}(?:\\:\\d{2})?|Z`;
583
+ case 4:
584
+ return `[\\+\\-]\\d{4}(?:\\d{2})?|Z`;
597
585
  case 3:
598
586
  return `[\\+\\-]\\d{2}\\:\\d{2}|Z`;
599
- case 4:
600
587
  case 2:
601
588
  return `[\\+\\-]\\d{4}|Z`;
602
589
  default:
@@ -622,18 +609,21 @@ export default {
622
609
  break;
623
610
  }
624
611
 
625
- return formatOffset(offset, useColon, length === 1);
612
+ return formatOffset(offset, useColon, length === 1, length >= 4);
626
613
  },
627
614
  },
628
615
 
629
616
  x: {
630
617
  key: 'timeZone',
618
+ supportsLength: (length) => length <= 5,
631
619
  regex: (_, length) => {
632
620
  switch (length) {
633
621
  case 5:
622
+ return `[\\+\\-]\\d{2}\\:\\d{2}(?:\\:\\d{2})?`;
623
+ case 4:
624
+ return `[\\+\\-]\\d{4}(?:\\d{2})?`;
634
625
  case 3:
635
626
  return `[\\+\\-]\\d{2}\\:\\d{2}`;
636
- case 4:
637
627
  case 2:
638
628
  return `[\\+\\-]\\d{4}`;
639
629
  default:
@@ -653,7 +643,7 @@ export default {
653
643
  break;
654
644
  }
655
645
 
656
- return formatOffset(datetime.getTimeZoneOffset(), useColon, length === 1);
646
+ return formatOffset(datetime.getTimeZoneOffset(), useColon, length === 1, length >= 4);
657
647
  },
658
648
  },
659
649
 
@@ -1,8 +1,19 @@
1
- import { minDaysInFirstWeek, weekStart } from './locales.js';
2
- import { getData } from './../factory.js';
1
+ import { getWeekInfo } from './locale.js';
2
+ import { numberRegExp } from './values.js';
3
3
 
4
4
  /**
5
- * Get the formatting type from the component token length.
5
+ * Decodes a quoted ICU format literal.
6
+ * @param {string} literal The literal to decode.
7
+ * @return {string} The decoded literal.
8
+ */
9
+ export function decodeLiteral(literal) {
10
+ return literal === `''` ?
11
+ `'` :
12
+ literal.slice(1, -1).replace(/''/g, `'`);
13
+ };
14
+
15
+ /**
16
+ * Gets the formatting type from the component token length.
6
17
  * @param {number} length The component token length.
7
18
  * @return {string} The formatting type.
8
19
  */
@@ -18,79 +29,42 @@ export function getType(length) {
18
29
  };
19
30
 
20
31
  /**
21
- * Get the minimum days.
22
- * @param {string} locale The locale.
23
- * @return {number} The minimum days.
32
+ * Gets the parsing RegExp data for a format token.
33
+ * @param {string} source The token RegExp.
34
+ * @param {string|null} nextSource The next token RegExp.
35
+ * @param {number} length The token length.
36
+ * @param {string} locale The parsing locale.
37
+ * @param {boolean} previousNumeric Whether the previous token was an adjacent numeric token.
38
+ * @return {{numeric: boolean, source: string}} The token RegExp data.
24
39
  */
25
- export function minimumDays(locale) {
26
- return getData(
27
- `minimumDays.${locale}`,
28
- (_) => {
29
- let minDays = 1;
30
- const localeTest = locale.toLowerCase().split('-');
31
- while (minDays === 1 && localeTest.length) {
32
- for (const days in minDaysInFirstWeek) {
33
- if (!{}.hasOwnProperty.call(minDaysInFirstWeek, days)) {
34
- continue;
35
- }
36
-
37
- const locales = minDaysInFirstWeek[days];
38
-
39
- if (locales.includes(localeTest.join('-'))) {
40
- minDays = parseInt(days);
41
- break;
42
- }
43
- }
40
+ export function getTokenRegExp(source, nextSource, length, locale, previousNumeric) {
41
+ const numberSource = numberRegExp(locale);
42
+ let numeric = true;
44
43
 
45
- localeTest.pop();
46
- }
44
+ if (source !== numberSource) {
45
+ numeric = false;
46
+ } else if (previousNumeric || nextSource === numberSource) {
47
+ source = numberRegExp(locale, length);
48
+ }
47
49
 
48
- return minDays;
49
- },
50
- );
50
+ return { numeric, source };
51
51
  };
52
52
 
53
53
  /**
54
- * Get the week start offset for a locale.
55
- * @param {string} [locale] The locale to load.
56
- * @return {number} The week start offset.
54
+ * Gets the locale's minimum days in the first week of the year.
55
+ * @param {string} locale The locale.
56
+ * @return {number} The minimum day count.
57
57
  */
58
- function weekStartOffset(locale) {
59
- return getData(
60
- `weekStartOffset.${locale}`,
61
- (_) => {
62
- let weekStarted;
63
- const localeTest = locale.toLowerCase().split('-');
64
- while (!weekStarted && localeTest.length) {
65
- for (const start in weekStart) {
66
- if (!{}.hasOwnProperty.call(weekStart, start)) {
67
- continue;
68
- }
69
-
70
- const locales = weekStart[start];
71
-
72
- if (locales.includes(localeTest.join('-'))) {
73
- weekStarted = parseInt(start);
74
- break;
75
- }
76
- }
77
-
78
- localeTest.pop();
79
- }
80
-
81
- return weekStarted ?
82
- weekStarted - 2 :
83
- 0;
84
- },
85
- );
58
+ export function minimumDays(locale) {
59
+ return getWeekInfo(locale).minimalDays;
86
60
  };
87
61
 
88
62
  /**
89
- * Convert a day of the week to a local format.
63
+ * Converts a Sunday-based day-of-week value to the locale's week numbering.
90
64
  * @param {string} locale The locale.
91
- * @param {number} day The day of the week.
65
+ * @param {number} day The day of the week. (0 = Sunday, 6 = Saturday)
92
66
  * @return {number} The local day of the week.
93
67
  */
94
68
  export function weekDay(locale, day) {
95
- return (7 + parseInt(day) - weekStartOffset(locale)) % 7 || 7;
69
+ return (7 + parseInt(day, 10) - (getWeekInfo(locale).firstDay % 7)) % 7 + 1;
96
70
  };