@aemforms/af-formatters 0.22.9

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.
@@ -0,0 +1,522 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ShorthandStyles = void 0;
7
+ exports.adjustTimeZone = adjustTimeZone;
8
+ exports.datetimeToNumber = datetimeToNumber;
9
+ exports.formatDate = formatDate;
10
+ exports.numberToDatetime = numberToDatetime;
11
+ exports.offsetMS = offsetMS;
12
+ exports.offsetMSFallback = offsetMSFallback;
13
+ exports.parseDate = parseDate;
14
+ exports.parseDefaultDate = parseDefaultDate;
15
+
16
+ var _SkeletonParser = require("./SkeletonParser.js");
17
+
18
+ /*************************************************************************
19
+ * ADOBE CONFIDENTIAL
20
+ * ___________________
21
+ *
22
+ * Copyright 2022 Adobe
23
+ * All Rights Reserved.
24
+ *
25
+ * NOTICE: All information contained herein is, and remains
26
+ * the property of Adobe and its suppliers, if any. The intellectual
27
+ * and technical concepts contained herein are proprietary to Adobe
28
+ * and its suppliers and are protected by all applicable intellectual
29
+ * property laws, including trade secret and copyright laws.
30
+ * Dissemination of this information or reproduction of this material
31
+ * is strictly forbidden unless prior written permission is obtained
32
+ * from Adobe.
33
+ **************************************************************************/
34
+
35
+ /**
36
+ * Credit: https://git.corp.adobe.com/dc/dfl/blob/master/src/patterns/dates.js
37
+ */
38
+ // Test Japanese full/half width character support
39
+ // get the localized month names resulting from a given pattern
40
+ const twelveMonths = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map(m => new Date(2000, m, 1));
41
+ /**
42
+ * returns the name of all the months for a given locale and given Date Format Settings
43
+ * @param locale {string}
44
+ * @param options {string} instance of Intl.DateTimeFormatOptions
45
+ */
46
+
47
+ function monthNames(locale, options) {
48
+ return twelveMonths.map(month => {
49
+ const parts = new Intl.DateTimeFormat(locale, options).formatToParts(month);
50
+ const m = parts.find(p => p.type === 'month');
51
+ return m && m.value;
52
+ });
53
+ }
54
+ /**
55
+ * return an array of digits used by a given locale
56
+ * @param locale {string}
57
+ */
58
+
59
+
60
+ function digitChars(locale) {
61
+ return new Intl.NumberFormat(locale, {
62
+ style: 'decimal',
63
+ useGrouping: false
64
+ }).format(9876543210).split('').reverse();
65
+ }
66
+ /**
67
+ * returns the calendar name used in a given locale
68
+ * @param locale {string}
69
+ */
70
+
71
+
72
+ function calendarName(locale) {
73
+ var _parts$find;
74
+
75
+ const parts = new Intl.DateTimeFormat(locale, {
76
+ era: 'short'
77
+ }).formatToParts(new Date());
78
+ const era = (_parts$find = parts.find(p => p.type === 'era')) === null || _parts$find === void 0 ? void 0 : _parts$find.value;
79
+ return era === 'هـ' ? 'islamic' : 'gregory';
80
+ }
81
+ /**
82
+ * returns the representation of the time of day for a given language
83
+ * @param language {string}
84
+ */
85
+
86
+
87
+ function getDayPeriod(language) {
88
+ const morning = new Date(2000, 1, 1, 1, 1, 1);
89
+ const afternoon = new Date(2000, 1, 1, 16, 1, 1);
90
+ const df = new Intl.DateTimeFormat(language, {
91
+ dateStyle: 'full',
92
+ timeStyle: 'full'
93
+ });
94
+ const am = df.formatToParts(morning).find(p => p.type === 'dayPeriod');
95
+ const pm = df.formatToParts(afternoon).find(p => p.type === 'dayPeriod');
96
+ if (!am || !pm) return null;
97
+ return {
98
+ regex: `(${am.value}|${pm.value})`,
99
+ fn: (period, obj) => obj.hour += period === pm.value ? 12 : 0
100
+ };
101
+ }
102
+ /**
103
+ * get the offset in MS, given a date and timezone
104
+ * @param dateObj {Date}
105
+ * @param timeZone {string}
106
+ */
107
+
108
+
109
+ function offsetMS(dateObj, timeZone) {
110
+ let tzOffset;
111
+
112
+ try {
113
+ tzOffset = new Intl.DateTimeFormat('en-US', {
114
+ timeZone,
115
+ timeZoneName: 'longOffset'
116
+ }).format(dateObj);
117
+ } catch (e) {
118
+ return offsetMSFallback(dateObj, timeZone);
119
+ }
120
+
121
+ const offset = /GMT([+\-−])?(\d{1,2}):?(\d{0,2})?/.exec(tzOffset);
122
+ if (!offset) return 0;
123
+ const [sign, hours, minutes] = offset.slice(1);
124
+ const nHours = isNaN(parseInt(hours)) ? 0 : parseInt(hours);
125
+ const nMinutes = isNaN(parseInt(minutes)) ? 0 : parseInt(minutes);
126
+ const result = (nHours * 60 + nMinutes) * 60 * 1000;
127
+ return sign === '-' ? -result : result;
128
+ }
129
+
130
+ function getTimezoneOffsetFrom(otherTimezone) {
131
+ var date = new Date();
132
+
133
+ function objFromStr(str) {
134
+ var array = str.replace(":", " ").split(" ");
135
+ return {
136
+ day: parseInt(array[0]),
137
+ hour: parseInt(array[1]),
138
+ minute: parseInt(array[2])
139
+ };
140
+ }
141
+
142
+ var str = date.toLocaleString('en-US', {
143
+ timeZone: otherTimezone,
144
+ day: 'numeric',
145
+ hour: 'numeric',
146
+ minute: 'numeric',
147
+ hour12: false
148
+ });
149
+ var other = objFromStr(str);
150
+ str = date.toLocaleString('en-US', {
151
+ day: 'numeric',
152
+ hour: 'numeric',
153
+ minute: 'numeric',
154
+ hour12: false
155
+ });
156
+ var myLocale = objFromStr(str);
157
+ var otherOffset = other.day * 24 * 60 + other.hour * 60 + other.minute; // utc date + otherTimezoneDifference
158
+
159
+ var myLocaleOffset = myLocale.day * 24 * 60 + myLocale.hour * 60 + myLocale.minute; // utc date + myTimeZoneDifference
160
+ // (utc date + otherZoneDifference) - (utc date + myZoneDifference) - (-1 * myTimeZoneDifference)
161
+
162
+ return otherOffset - myLocaleOffset - date.getTimezoneOffset();
163
+ }
164
+
165
+ function offsetMSFallback(dateObj, timezone) {
166
+ //const defaultOffset = dateObj.getTimezoneOffset();
167
+ const timezoneOffset = getTimezoneOffsetFrom(timezone);
168
+ return timezoneOffset * 60 * 1000;
169
+ }
170
+ /**
171
+ * adjust from the default JavaScript timezone to the default timezone
172
+ * @param dateObj {Date}
173
+ * @param timeZone {string}
174
+ */
175
+
176
+
177
+ function adjustTimeZone(dateObj, timeZone) {
178
+ if (dateObj === null) return null; // const defaultOffset = new Intl.DateTimeFormat('en-US', { timeZoneName: 'longOffset'}).format(dateObj);
179
+
180
+ let baseDate = dateObj.getTime() - dateObj.getTimezoneOffset() * 60 * 1000;
181
+ const offset = offsetMS(dateObj, timeZone);
182
+ const fallBackOffset = offsetMSFallback(dateObj, timeZone);
183
+ baseDate += -offset; // get the offset for the default JS environment
184
+ // return days since the epoch
185
+
186
+ return new Date(baseDate);
187
+ }
188
+ /**
189
+ * Our script object model treats dates as numbers where the integer portion is days since the epoch,
190
+ * the fractional portion is the number hours in the day
191
+ * @param dateObj {Date}
192
+ * @returns {number}
193
+ */
194
+
195
+
196
+ function datetimeToNumber(dateObj) {
197
+ if (dateObj === null) return 0; // return days since the epoch
198
+
199
+ return dateObj.getTime() / (1000 * 60 * 60 * 24);
200
+ }
201
+ /**
202
+ * Our script object model treats dates as numbers where the integer portion is days since the epoch,
203
+ * the fractional portion is the number hours in the day
204
+ * @param num
205
+ * @returns {Date}
206
+ */
207
+
208
+
209
+ function numberToDatetime(num) {
210
+ return new Date(Math.round(num * 1000 * 60 * 60 * 24));
211
+ }
212
+ /**
213
+ * in some cases, DateTimeFormat doesn't respect the 'numeric' vs. '2-digit' setting
214
+ * for time values. The function corrects that
215
+ * @param formattedParts instance of Intl.DateTimeFormatPart[]
216
+ * @param parsed
217
+ */
218
+
219
+
220
+ function fixDigits(formattedParts, parsed) {
221
+ ['hour', 'minute', 'second'].forEach(type => {
222
+ const defn = formattedParts.find(f => f.type === type);
223
+ if (!defn) return;
224
+ const fmt = parsed.find(pair => pair[0] === type)[1];
225
+ if (fmt === '2-digit' && defn.value.length === 1) defn.value = `0${defn.value}`;
226
+ if (fmt === 'numeric' && defn.value.length === 2 && defn.value.charAt(0) === '0') defn.value = defn.value.slice(1);
227
+ });
228
+ }
229
+
230
+ function fixYear(formattedParts, parsed) {
231
+ // two digit years are handled differently in DateTimeFormat. 00 becomes 1900
232
+ // providing a two digit year 0010 gets formatted to 10 and when parsed becomes 1910
233
+ // Hence we need to pad the year with 0 as required by the skeleton and mentioned in
234
+ // unicode. https://www.unicode.org/reports/tr35/tr35-dates.html#dfst-year
235
+ const defn = formattedParts.find(f => f.type === 'year');
236
+ if (!defn) return; // eslint-disable-next-line no-unused-vars
237
+
238
+ const chars = parsed.find(pair => pair[0] === 'year')[2];
239
+
240
+ while (defn.value.length < chars) {
241
+ defn.value = `0${defn.value}`;
242
+ }
243
+ }
244
+ /**
245
+ *
246
+ * @param dateValue {Date}
247
+ * @param language {string}
248
+ * @param skeleton {string}
249
+ * @param timeZone {string}
250
+ * @returns {T}
251
+ */
252
+
253
+
254
+ function formatDateToParts(dateValue, language, skeleton, timeZone) {
255
+ // DateTimeFormat renames some of the options in its formatted output
256
+ //@ts-ignore
257
+ const mappings = key => ({
258
+ hour12: 'dayPeriod',
259
+ fractionalSecondDigits: 'fractionalSecond'
260
+ })[key] || key; // produces an array of name/value pairs of skeleton parts
261
+
262
+
263
+ const allParameters = (0, _SkeletonParser.parseDateTimeSkeleton)(skeleton, language);
264
+ allParameters.push(['timeZone', timeZone]);
265
+ const parsed = allParameters.filter(p => !p[0].startsWith('x-'));
266
+ const nonStandard = allParameters.filter(p => p[0].startsWith('x-')); // reduce to a set of options that can be used to format
267
+
268
+ const options = Object.fromEntries(parsed);
269
+ delete options.literal;
270
+ const df = new Intl.DateTimeFormat(language, options); // formattedParts will have all the pieces we need for our date -- but not in the correct order
271
+
272
+ const formattedParts = df.formatToParts(dateValue);
273
+ fixDigits(formattedParts, allParameters);
274
+ fixYear(formattedParts, parsed); // iterate through the original parsed components and use its ordering and literals,
275
+ // and add the formatted pieces
276
+
277
+ return parsed.reduce((result, cur) => {
278
+ if (cur[0] === 'literal') result.push(cur);else {
279
+ const v = formattedParts.find(p => p.type === mappings(cur[0]));
280
+
281
+ if (v && v.type === 'timeZoneName') {
282
+ const tz = nonStandard.find(p => p[0] === 'x-timeZoneName')[1];
283
+ const category = tz[0];
284
+
285
+ if (category === 'Z') {
286
+ if (tz.length < 4) {
287
+ // handle 'Z', 'ZZ', 'ZZZ' Time Zone: ISO8601 basic hms? / RFC 822
288
+ v.value = v.value.replace(/(GMT|:)/g, '');
289
+ if (v.value === '') v.value = '+0000';
290
+ } else if (tz.length === 5) {
291
+ // 'ZZZZZ' Time Zone: ISO8601 extended hms?
292
+ if (v.value === 'GMT') v.value = 'Z';else v.value = v.value.replace(/GMT/, '');
293
+ }
294
+ }
295
+
296
+ if (category === 'X' || category === 'x') {
297
+ if (tz.length === 1) {
298
+ // 'X' ISO8601 basic hm?, with Z for 0
299
+ // -08, +0530, Z
300
+ // 'x' ISO8601 basic hm?, without Z for 0
301
+ v.value = v.value.replace(/(GMT|:(00)?)/g, '');
302
+ }
303
+
304
+ if (tz.length === 2) {
305
+ // 'XX' ISO8601 basic hm, with Z
306
+ // -0800, Z
307
+ // 'xx' ISO8601 basic hm, without Z
308
+ v.value = v.value.replace(/(GMT|:)/g, '');
309
+ }
310
+
311
+ if (tz.length === 3) {
312
+ // 'XXX' ISO8601 extended hm, with Z
313
+ // -08:00, Z
314
+ // 'xxx' ISO8601 extended hm, without Z
315
+ v.value = v.value.replace(/GMT/g, '');
316
+ }
317
+
318
+ if (category === 'X' && v.value === '') v.value = 'Z';
319
+ } else if (tz === 'O') {
320
+ // eliminate 'GMT', leading and trailing zeros
321
+ v.value = v.value.replace(/GMT/g, '').replace(/0(\d+):/, '$1:').replace(/:00/, '');
322
+ if (v.value === '') v.value = '+0';
323
+ }
324
+ }
325
+
326
+ if (v) result.push([v.type, v.value]);
327
+ }
328
+ return result;
329
+ }, []);
330
+ }
331
+
332
+ const ShorthandStyles = ["full", "long", "medium", "short"];
333
+ /**
334
+ *
335
+ * @param dateValue {Date}
336
+ * @param language {string}
337
+ * @param skeleton {string}
338
+ * @param timeZone {string}
339
+ */
340
+
341
+ exports.ShorthandStyles = ShorthandStyles;
342
+
343
+ function formatDate(dateValue, language, skeleton, timeZone) {
344
+ if (ShorthandStyles.find(type => skeleton.includes(type))) {
345
+ const options = {
346
+ timeZone
347
+ }; // the skeleton could have two keywords -- one for date, one for time
348
+
349
+ const parts = skeleton.split(/\s/).filter(s => s.length);
350
+
351
+ if (ShorthandStyles.indexOf(parts[0]) > -1) {
352
+ options.dateStyle = parts[0];
353
+ }
354
+
355
+ if (parts.length > 1 && ShorthandStyles.indexOf(parts[1]) > -1) {
356
+ options.timeStyle = parts[1];
357
+ }
358
+
359
+ return new Intl.DateTimeFormat(language, options).format(dateValue);
360
+ }
361
+
362
+ const parts = formatDateToParts(dateValue, language, skeleton, timeZone);
363
+ return parts.map(p => p[1]).join('');
364
+ }
365
+ /**
366
+ *
367
+ * @param dateString {string}
368
+ * @param language {string}
369
+ * @param skeleton {string}
370
+ * @param timeZone {string}
371
+ */
372
+
373
+
374
+ function parseDate(dateString, language, skeleton, timeZone) {
375
+ let bUseUTC = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
376
+ // start by getting all the localized parts of a date/time picture:
377
+ // digits, calendar name
378
+ const lookups = [];
379
+ const regexParts = [];
380
+ const calendar = calendarName(language);
381
+ const digits = digitChars(language);
382
+ const twoDigit = `([${digits[0]}-${digits[9]}]{1,2})`;
383
+ const threeDigit = `([${digits[0]}-${digits[9]}]{1,3})`;
384
+ const fourDigit = `([${digits[0]}-${digits[9]}]{1,4})`;
385
+ let hourCycle = 'h12';
386
+ let _bUseUTC = bUseUTC;
387
+ let _setFullYear = false; // functions to process the results of the regex match
388
+
389
+ const isSeparator = str => str.length === 1 && ':-/.'.includes(str);
390
+
391
+ const monthNumber = str => getNumber(str) - 1;
392
+
393
+ const getNumber = str => str.split('').reduce((total, digit) => total * 10 + digits.indexOf(digit), 0);
394
+
395
+ const yearNumber = templateDigits => str => {
396
+ let year = getNumber(str); //todo: align with AF
397
+
398
+ year = year < 100 && templateDigits === 2 ? year + 2000 : year;
399
+ if (calendar === 'islamic') year = Math.ceil(year * 0.97 + 622);
400
+
401
+ if (templateDigits > 2 && year < 100) {
402
+ _setFullYear = true;
403
+ }
404
+
405
+ return year;
406
+ };
407
+
408
+ const monthLookup = list => month => list.indexOf(month);
409
+
410
+ const parsed = (0, _SkeletonParser.parseDateTimeSkeleton)(skeleton, language);
411
+ const months = monthNames(language, Object.fromEntries(parsed)); // build up a regex expression that identifies each option in the skeleton
412
+ // We build two parallel structures:
413
+ // 1. the regex expression that will extract parts of the date/time
414
+ // 2. a lookup array that will convert the matched results into date/time values
415
+
416
+ parsed.forEach(_ref => {
417
+ let [option, value, len] = _ref;
418
+
419
+ // use a generic regex pattern for all single-character separator literals.
420
+ // Then we'll be forgiving when it comes to separators: / vs - vs : etc
421
+ if (option === 'literal') {
422
+ if (isSeparator(value)) regexParts.push(`[^${digits[0]}-${digits[9]}]`);else regexParts.push(value);
423
+ } else if (option === 'month' && ['numeric', '2-digit'].includes(value)) {
424
+ regexParts.push(twoDigit);
425
+ lookups.push(['month', monthNumber]);
426
+ } else if (option === 'month' && ['formatted', 'long', 'short', 'narrow'].includes(value)) {
427
+ regexParts.push(`(${months.join('|')})`);
428
+ lookups.push(['month', monthLookup(months)]);
429
+ } else if (['day', 'minute', 'second'].includes(option)) {
430
+ if (option === 'minute' || option === 'second') {
431
+ _bUseUTC = false;
432
+ }
433
+
434
+ regexParts.push(twoDigit);
435
+ lookups.push([option, getNumber]);
436
+ } else if (option === 'fractionalSecondDigits') {
437
+ _bUseUTC = false;
438
+ regexParts.push(threeDigit);
439
+ lookups.push([option, (v, obj) => obj.fractionalSecondDigits + getNumber(v)]);
440
+ } else if (option === 'hour') {
441
+ _bUseUTC = false;
442
+ regexParts.push(twoDigit);
443
+ lookups.push([option, (v, obj) => obj.hour + getNumber(v)]);
444
+ } else if (option === 'year') {
445
+ regexParts.push('numeric' === value ? fourDigit : twoDigit);
446
+ lookups.push(['year', yearNumber(len)]);
447
+ } else if (option === 'dayPeriod') {
448
+ _bUseUTC = false;
449
+ const dayPeriod = getDayPeriod(language);
450
+
451
+ if (dayPeriod) {
452
+ regexParts.push(dayPeriod.regex);
453
+ lookups.push(['hour', dayPeriod.fn]);
454
+ } // Any other part that we don't need, we'll just add a non-greedy consumption
455
+
456
+ } else if (option === 'hourCycle') {
457
+ _bUseUTC = false;
458
+ hourCycle = value;
459
+ } else if (option === 'x-timeZoneName') {
460
+ _bUseUTC = false; // we handle only the GMT offset picture
461
+
462
+ regexParts.push('(?:GMT|UTC|Z)?([+\\-−0-9]{0,3}:?[0-9]{0,2})');
463
+ lookups.push([option, (v, obj) => {
464
+ _bUseUTC = true; // v could be undefined if we're on GMT time
465
+
466
+ if (!v) return; // replace the unicode minus, then extract hours [and minutes]
467
+
468
+ const timeParts = v.replace(/−/, '-').match(/([+\-\d]{2,3}):?(\d{0,2})/);
469
+ const hours = timeParts[1] * 1;
470
+ obj.hour -= hours;
471
+ const mins = timeParts.length > 2 ? timeParts[2] * 1 : 0;
472
+ obj.minute -= hours < 0 ? -mins : mins;
473
+ }]);
474
+ } else if (option !== 'timeZoneName') {
475
+ _bUseUTC = false;
476
+ regexParts.push('.+?');
477
+ }
478
+
479
+ return regexParts;
480
+ }, []);
481
+ const regex = new RegExp(regexParts.join(''));
482
+ const match = dateString.match(regex);
483
+ if (match === null) return dateString; // now loop through all the matched pieces and build up an object we'll use to create a Date object
484
+
485
+ const dateObj = {
486
+ year: 1972,
487
+ month: 0,
488
+ day: 1,
489
+ hour: 0,
490
+ minute: 0,
491
+ second: 0,
492
+ fractionalSecondDigits: 0
493
+ };
494
+ match.slice(1).forEach((m, index) => {
495
+ const [element, func] = lookups[index];
496
+ dateObj[element] = func(m, dateObj);
497
+ });
498
+ if (hourCycle === 'h24' && dateObj.hour === 24) dateObj.hour = 0;
499
+ if (hourCycle === 'h12' && dateObj.hour === 12) dateObj.hour = 0;
500
+
501
+ if (_bUseUTC) {
502
+ const utcDate = new Date(Date.UTC(dateObj.year, dateObj.month, dateObj.day, dateObj.hour, dateObj.minute, dateObj.second, dateObj.fractionalSecondDigits));
503
+
504
+ if (_setFullYear) {
505
+ utcDate.setUTCFullYear(dateObj.year);
506
+ }
507
+
508
+ return utcDate;
509
+ }
510
+
511
+ const jsDate = new Date(dateObj.year, dateObj.month, dateObj.day, dateObj.hour, dateObj.minute, dateObj.second, dateObj.fractionalSecondDigits);
512
+
513
+ if (_setFullYear) {
514
+ jsDate.setFullYear(dateObj.year);
515
+ }
516
+
517
+ return timeZone == null ? jsDate : adjustTimeZone(jsDate, timeZone);
518
+ }
519
+
520
+ function parseDefaultDate(dateString, language, bUseUTC) {
521
+ return parseDate(dateString, language, 'short', null, false);
522
+ }
@@ -0,0 +1,315 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getSkeleton = getSkeleton;
7
+ exports.parseDateTimeSkeleton = parseDateTimeSkeleton;
8
+
9
+ var _DateParser = require("./DateParser");
10
+
11
+ /*************************************************************************
12
+ * ADOBE CONFIDENTIAL
13
+ * ___________________
14
+ *
15
+ * Copyright 2022 Adobe
16
+ * All Rights Reserved.
17
+ *
18
+ * NOTICE: All information contained herein is, and remains
19
+ * the property of Adobe and its suppliers, if any. The intellectual
20
+ * and technical concepts contained herein are proprietary to Adobe
21
+ * and its suppliers and are protected by all applicable intellectual
22
+ * property laws, including trade secret and copyright laws.
23
+ * Dissemination of this information or reproduction of this material
24
+ * is strictly forbidden unless prior written permission is obtained
25
+ * from Adobe.
26
+ **************************************************************************/
27
+
28
+ /**
29
+ * https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
30
+ * Credit: https://git.corp.adobe.com/dc/dfl/blob/master/src/patterns/parseDateTimeSkeleton.js
31
+ * Created a separate library to be used elsewhere as well.
32
+ */
33
+ const DATE_TIME_REGEX = // eslint-disable-next-line max-len
34
+ /(?:[Eec]{1,6}|G{1,5}|[Qq]{1,5}|(?:[yYur]+|U{1,5})|[ML]{1,5}|d{1,2}|D{1,3}|F{1}|[abB]{1,5}|[hkHK]{1,2}|w{1,2}|W{1}|m{1,2}|s{1,2}|[zZOvV]{1,5}|[zZOvVxX]{1,3}|S{1,3}|'(?:[^']|'')*')|[^a-zA-Z']+/g;
35
+ const testDate = new Date(2000, 2, 1, 2, 3, 4);
36
+ /**
37
+ * Since the formatted month names are different than standalone month names, we need to identify the correct option
38
+ * to pass for formatting
39
+ * @param value {string} formatted value of the month
40
+ * @param language {string} language in which the month is formatted
41
+ * @param isMediumFormatStyle {boolean} if shorthand style used for formatting was medium
42
+ */
43
+
44
+ function deduceMonthOption(value, language, isMediumFormatStyle) {
45
+ const formattedMarch = value;
46
+ const longMarch = new Intl.DateTimeFormat(language, {
47
+ month: 'long'
48
+ }).formatToParts(testDate)[0].value;
49
+ const shortMarch = new Intl.DateTimeFormat(language, {
50
+ month: 'short'
51
+ }).formatToParts(testDate)[0].value;
52
+ const monthOptions = {
53
+ [longMarch]: 'long',
54
+ [shortMarch]: 'short',
55
+ '03': '2-digit',
56
+ '3': 'numeric'
57
+ };
58
+
59
+ if (formattedMarch !== undefined) {
60
+ monthOptions[formattedMarch] = isMediumFormatStyle ? 'short' : 'long';
61
+ }
62
+
63
+ return monthOptions[value];
64
+ }
65
+
66
+ function getSkeleton(skeleton, language) {
67
+ if (_DateParser.ShorthandStyles.find(type => skeleton.includes(type))) {
68
+ const parsed = parseDateStyle(skeleton, language);
69
+ const result = [];
70
+ const symbols = {
71
+ month: 'M',
72
+ year: 'Y',
73
+ day: 'd'
74
+ };
75
+ parsed.forEach(_ref => {
76
+ let [type, option, length] = _ref;
77
+
78
+ if (type in symbols) {
79
+ result.push(Array(length).fill(symbols[type]).join(''));
80
+ } else if (type === 'literal') {
81
+ result.push(option);
82
+ }
83
+ });
84
+ return result.join('');
85
+ }
86
+
87
+ return skeleton;
88
+ }
89
+ /**
90
+ *
91
+ * @param skeleton shorthand style for the date concatenated with shorthand style of time. The
92
+ * Shorthand style for both date and time is one of ['full', 'long', 'medium', 'short'].
93
+ * @param language {string} language to parse the date shorthand style
94
+ * @returns {[*,string][]}
95
+ */
96
+
97
+
98
+ function parseDateStyle(skeleton, language) {
99
+ const options = {}; // the skeleton could have two keywords -- one for date, one for time
100
+
101
+ const styles = skeleton.split(/\s/).filter(s => s.length);
102
+ options.dateStyle = styles[0];
103
+ if (styles.length > 1) options.timeStyle = styles[1];
104
+ const testDate = new Date(2000, 2, 1, 2, 3, 4);
105
+ const parts = new Intl.DateTimeFormat(language, options).formatToParts(testDate); // oddly, the formatted month name can be different from the standalone month name
106
+
107
+ const formattedMarch = parts.find(p => p.type === 'month').value;
108
+ const longMarch = new Intl.DateTimeFormat(language, {
109
+ month: 'long'
110
+ }).formatToParts(testDate)[0].value;
111
+ const shortMarch = new Intl.DateTimeFormat(language, {
112
+ month: 'short'
113
+ }).formatToParts(testDate)[0].value;
114
+ const result = [];
115
+ parts.forEach(_ref2 => {
116
+ let {
117
+ type,
118
+ value
119
+ } = _ref2;
120
+ let option;
121
+
122
+ if (type === 'month') {
123
+ option = {
124
+ [formattedMarch]: skeleton === 'medium' ? 'short' : 'long',
125
+ [longMarch]: 'long',
126
+ [shortMarch]: 'short',
127
+ '03': '2-digit',
128
+ '3': 'numeric'
129
+ }[value];
130
+ }
131
+
132
+ if (type === 'year') option = {
133
+ '2000': 'numeric',
134
+ '00': '2-digit'
135
+ }[value];
136
+ if (['day', 'hour', 'minute', 'second'].includes(type)) option = value.length === 2 ? '2-digit' : 'numeric';
137
+ if (type === 'literal') option = value;
138
+ if (type === 'dayPeriod') option = 'short';
139
+ result.push([type, option, value.length]);
140
+ });
141
+ return result;
142
+ }
143
+ /**
144
+ * Parse Date time skeleton into Intl.DateTimeFormatOptions parts
145
+ * Ref: https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
146
+ */
147
+
148
+
149
+ function parseDateTimeSkeleton(skeleton, language) {
150
+ if (_DateParser.ShorthandStyles.find(type => skeleton.includes(type))) {
151
+ return parseDateStyle(skeleton, language);
152
+ }
153
+
154
+ const result = [];
155
+ skeleton.replace(DATE_TIME_REGEX, match => {
156
+ const len = match.length;
157
+
158
+ switch (match[0]) {
159
+ // Era
160
+ case 'G':
161
+ result.push(['era', len === 4 ? 'long' : len === 5 ? 'narrow' : 'short', len]);
162
+ break;
163
+ // Year
164
+
165
+ case 'y':
166
+ result.push(['year', len === 2 ? '2-digit' : 'numeric', len]);
167
+ break;
168
+
169
+ case 'Y':
170
+ case 'u':
171
+ case 'U':
172
+ case 'r':
173
+ throw new RangeError('`Y/u/U/r` (year) patterns are not supported, use `y` instead');
174
+ // Quarter
175
+
176
+ case 'q':
177
+ case 'Q':
178
+ throw new RangeError('`q/Q` (quarter) patterns are not supported');
179
+ // Month
180
+
181
+ case 'M':
182
+ case 'L':
183
+ result.push(['month', ['numeric', '2-digit', 'short', 'long', 'narrow'][len - 1], len]);
184
+ break;
185
+ // Week
186
+
187
+ case 'w':
188
+ case 'W':
189
+ throw new RangeError('`w/W` (week) patterns are not supported');
190
+
191
+ case 'd':
192
+ result.push(['day', ['numeric', '2-digit'][len - 1], len]);
193
+ break;
194
+
195
+ case 'D':
196
+ case 'F':
197
+ case 'g':
198
+ throw new RangeError('`D/F/g` (day) patterns are not supported, use `d` instead');
199
+ // Weekday
200
+
201
+ case 'E':
202
+ result.push(['weekday', ['short', 'short', 'short', 'long', 'narrow', 'narrow'][len - 1], len]);
203
+ break;
204
+
205
+ case 'e':
206
+ if (len < 4) {
207
+ throw new RangeError('`e..eee` (weekday) patterns are not supported');
208
+ }
209
+
210
+ result.push(['weekday', ['short', 'long', 'narrow', 'short'][len - 4], len]);
211
+ break;
212
+
213
+ case 'c':
214
+ if (len < 3 || len > 5) {
215
+ throw new RangeError('`c, cc, cccccc` (weekday) patterns are not supported');
216
+ }
217
+
218
+ result.push(['weekday', ['short', 'long', 'narrow', 'short'][len - 3], len]);
219
+ break;
220
+ // Period
221
+
222
+ case 'a':
223
+ // AM, PM
224
+ result.push(['hour12', true, 1]);
225
+ break;
226
+
227
+ case 'b': // am, pm, noon, midnight
228
+
229
+ case 'B':
230
+ // flexible day periods
231
+ throw new RangeError('`b/B` (period) patterns are not supported, use `a` instead');
232
+ // Hour
233
+
234
+ case 'h':
235
+ result.push(['hourCycle', 'h12']);
236
+ result.push(['hour', ['numeric', '2-digit'][len - 1], len]);
237
+ break;
238
+
239
+ case 'H':
240
+ result.push(['hourCycle', 'h23', 1]);
241
+ result.push(['hour', ['numeric', '2-digit'][len - 1], len]);
242
+ break;
243
+
244
+ case 'K':
245
+ result.push(['hourCycle', 'h11', 1]);
246
+ result.push(['hour', ['numeric', '2-digit'][len - 1], len]);
247
+ break;
248
+
249
+ case 'k':
250
+ result.push(['hourCycle', 'h24', 1]);
251
+ result.push(['hour', ['numeric', '2-digit'][len - 1], len]);
252
+ break;
253
+
254
+ case 'j':
255
+ case 'J':
256
+ case 'C':
257
+ throw new RangeError('`j/J/C` (hour) patterns are not supported, use `h/H/K/k` instead');
258
+ // Minute
259
+
260
+ case 'm':
261
+ result.push(['minute', ['numeric', '2-digit'][len - 1], len]);
262
+ break;
263
+ // Second
264
+
265
+ case 's':
266
+ result.push(['second', ['numeric', '2-digit'][len - 1], len]);
267
+ break;
268
+
269
+ case 'S':
270
+ result.push(['fractionalSecondDigits', len, len]);
271
+ break;
272
+
273
+ case 'A':
274
+ throw new RangeError('`S/A` (millisecond) patterns are not supported, use `s` instead');
275
+ // Zone
276
+
277
+ case 'O':
278
+ // timeZone GMT-8 or GMT-08:00
279
+ result.push(['timeZoneName', len < 4 ? 'shortOffset' : 'longOffset', len]);
280
+ result.push(['x-timeZoneName', len < 4 ? 'O' : 'OOOO', len]);
281
+ break;
282
+
283
+ case 'X': // 1, 2, 3, 4: The ISO8601 varios formats
284
+
285
+ case 'x': // 1, 2, 3, 4: The ISO8601 varios formats
286
+
287
+ case 'Z':
288
+ // 1..3, 4, 5: The ISO8601 varios formats
289
+ // Z, ZZ, ZZZ should produce -0800
290
+ // ZZZZ should produce GMT-08:00
291
+ // ZZZZZ should produce -8:00 or -07:52:58
292
+ result.push(['timeZoneName', 'longOffset', 1]);
293
+ result.push(['x-timeZoneName', match, 1]);
294
+ break;
295
+
296
+ case 'z': // 1..3, 4: specific non-location format
297
+
298
+ case 'v': // 1, 4: generic non-location format
299
+
300
+ case 'V':
301
+ // 1, 2, 3, 4: time zone ID or city
302
+ throw new RangeError('z/v/V` (timeZone) patterns are not supported, use `X/x/Z/O` instead');
303
+
304
+ case '\'':
305
+ result.push(['literal', match.slice(1, -1).replace(/''/g, '\''), -1]);
306
+ break;
307
+
308
+ default:
309
+ result.push(['literal', match, -1]);
310
+ }
311
+
312
+ return '';
313
+ });
314
+ return result;
315
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "formatDate", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _DateParser.formatDate;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "getSkeleton", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _SkeletonParser.getSkeleton;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "parseDate", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _DateParser.parseDate;
22
+ }
23
+ });
24
+
25
+ var _DateParser = require("./DateParser");
26
+
27
+ var _SkeletonParser = require("./SkeletonParser");
package/lib/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "formatDate", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _date.formatDate;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "getSkeleton", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _date.getSkeleton;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "parseDate", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _date.parseDate;
22
+ }
23
+ });
24
+
25
+ var _date = require("./date");
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@aemforms/af-formatters",
3
+ "version": "0.22.9",
4
+ "description": "Core Module for Forms Runtime",
5
+ "author": "Adobe Systems",
6
+ "license": "Adobe Proprietary",
7
+ "main": "lib/index.js",
8
+ "directories": {
9
+ "lib": "lib",
10
+ "source": "src",
11
+ "test": "src/__tests__"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "jest-junit": {
17
+ "suiteName": "core tests",
18
+ "suiteNameTemplate": "{filename}",
19
+ "classNameTemplate": "model-test.{filename}",
20
+ "outputDirectory": "./target/test-reports"
21
+ },
22
+ "files": [
23
+ "lib",
24
+ "LICENSE"
25
+ ],
26
+ "scripts": {
27
+ "test": "jest --silent",
28
+ "eslint": "npx eslint src/**",
29
+ "eslint:fix": "npx eslint --fix src/**",
30
+ "test:ci": "jest --silent --coverage",
31
+ "build": "babel src -d lib",
32
+ "clean": "rm -rf lib target",
33
+ "prepublishOnly": "npm run build && npm run test",
34
+ "docs": "npx typedoc --options .typedoc.cjs"
35
+ },
36
+ "devDependencies": {
37
+ "@babel/cli": "^7.18.10",
38
+ "@babel/core": "^7.19.0",
39
+ "@babel/preset-env": "^7.19.0",
40
+ "babel-jest": "^29.0.2",
41
+ "eslint": "^7.30.0",
42
+ "eslint-config-standard": "^16.0.3",
43
+ "eslint-plugin-import": "^2.23.4",
44
+ "eslint-plugin-jest": "^24.3.6",
45
+ "eslint-plugin-node": "^11.1.0",
46
+ "eslint-plugin-promise": "^5.1.0",
47
+ "form-data": "^4.0.0",
48
+ "jest": "26.6.0",
49
+ "jest-junit": "^12.2.0",
50
+ "nock": "^13.1.3",
51
+ "node-fetch": "^2.6.1",
52
+ "ts-jest": "26.5.6",
53
+ "typedoc": "0.22.11",
54
+ "typedoc-plugin-markdown": "3.11.13"
55
+ }
56
+ }