@aemforms/af-formatters 0.22.23 → 0.22.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/afb-formatters.js +950 -0
- package/lib/cjs/index.cjs +239 -62
- package/lib/esm/date/DateParser.js +189 -52
- package/lib/esm/date/SkeletonParser.js +100 -39
- package/lib/esm/date/index.js +18 -20
- package/lib/esm/index.js +13 -34
- package/lib/esm/number/NumberParser.js +15 -34
- package/lib/esm/number/SkeletonParser.js +74 -75
- package/lib/esm/number/currencies.js +6 -24
- package/package.json +1 -11
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
/*************************************************************************
|
|
2
|
-
* ADOBE CONFIDENTIAL
|
|
3
|
-
* ___________________
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* NOTICE:
|
|
9
|
-
* the property of Adobe and its suppliers, if any. The intellectual
|
|
10
|
-
* and technical concepts contained herein are proprietary to Adobe
|
|
11
|
-
* and its suppliers and are protected by all applicable intellectual
|
|
12
|
-
* property laws, including trade secret and copyright laws.
|
|
13
|
-
* Dissemination of this information or reproduction of this material
|
|
14
|
-
* is strictly forbidden unless prior written permission is obtained
|
|
15
|
-
* from Adobe.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import { ShorthandStyles, parseDateTimeSkeleton } from './SkeletonParser.js';
|
|
2
|
+
* ADOBE CONFIDENTIAL
|
|
3
|
+
* ___________________
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2022 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: All information contained herein is, and remains
|
|
9
|
+
* the property of Adobe and its suppliers, if any. The intellectual
|
|
10
|
+
* and technical concepts contained herein are proprietary to Adobe
|
|
11
|
+
* and its suppliers and are protected by all applicable intellectual
|
|
12
|
+
* property laws, including trade secret and copyright laws.
|
|
13
|
+
* Dissemination of this information or reproduction of this material
|
|
14
|
+
* is strictly forbidden unless prior written permission is obtained
|
|
15
|
+
* from Adobe.
|
|
16
|
+
**************************************************************************/
|
|
17
|
+
/**
|
|
18
|
+
* Credit: https://git.corp.adobe.com/dc/dfl/blob/master/src/patterns/dates.js
|
|
19
|
+
*/
|
|
20
|
+
// Test Japanese full/half width character support
|
|
22
21
|
|
|
22
|
+
import {parseDateTimeSkeleton, ShorthandStyles} from './SkeletonParser.js';
|
|
23
|
+
|
|
24
|
+
// get the localized month names resulting from a given pattern
|
|
23
25
|
const twelveMonths = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map(m => new Date(2000, m, 1));
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* returns the name of all the months for a given locale and given Date Format Settings
|
|
29
|
+
* @param locale {string}
|
|
30
|
+
* @param options {string} instance of Intl.DateTimeFormatOptions
|
|
31
|
+
*/
|
|
24
32
|
function monthNames(locale, options) {
|
|
25
33
|
return twelveMonths.map(month => {
|
|
26
34
|
const parts = new Intl.DateTimeFormat(locale, options).formatToParts(month);
|
|
@@ -28,17 +36,32 @@ function monthNames(locale, options) {
|
|
|
28
36
|
return m && m.value;
|
|
29
37
|
});
|
|
30
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* return an array of digits used by a given locale
|
|
42
|
+
* @param locale {string}
|
|
43
|
+
*/
|
|
31
44
|
function digitChars(locale) {
|
|
32
45
|
return new Intl.NumberFormat(locale, {style:'decimal', useGrouping:false})
|
|
33
46
|
.format(9876543210)
|
|
34
47
|
.split('')
|
|
35
48
|
.reverse();
|
|
36
49
|
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* returns the calendar name used in a given locale
|
|
53
|
+
* @param locale {string}
|
|
54
|
+
*/
|
|
37
55
|
function calendarName(locale) {
|
|
38
56
|
const parts = new Intl.DateTimeFormat(locale, {era:'short'}).formatToParts(new Date());
|
|
39
57
|
const era = parts.find(p => p.type === 'era')?.value;
|
|
40
58
|
return era === 'هـ' ? 'islamic' : 'gregory';
|
|
41
59
|
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* returns the representation of the time of day for a given language
|
|
63
|
+
* @param language {string}
|
|
64
|
+
*/
|
|
42
65
|
function getDayPeriod(language) {
|
|
43
66
|
const morning = new Date(2000, 1, 1, 1, 1, 1);
|
|
44
67
|
const afternoon = new Date(2000, 1, 1, 16, 1, 1);
|
|
@@ -51,7 +74,13 @@ function getDayPeriod(language) {
|
|
|
51
74
|
fn: (period, obj) => obj.hour += (period === pm.value) ? 12 : 0
|
|
52
75
|
};
|
|
53
76
|
}
|
|
54
|
-
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* get the offset in MS, given a date and timezone
|
|
80
|
+
* @param dateObj {Date}
|
|
81
|
+
* @param timeZone {string}
|
|
82
|
+
*/
|
|
83
|
+
export function offsetMS(dateObj, timeZone) {
|
|
55
84
|
let tzOffset;
|
|
56
85
|
try {
|
|
57
86
|
tzOffset = new Intl.DateTimeFormat('en-US', {timeZone, timeZoneName: 'longOffset'}).format(dateObj);
|
|
@@ -60,12 +89,13 @@ function offsetMS(dateObj, timeZone) {
|
|
|
60
89
|
}
|
|
61
90
|
const offset = /GMT([+\-−])?(\d{1,2}):?(\d{0,2})?/.exec(tzOffset);
|
|
62
91
|
if (!offset) return 0;
|
|
63
|
-
const [sign, hours, minutes] = offset.slice(1)
|
|
64
|
-
const nHours = isNaN(parseInt(hours)) ? 0 : parseInt(hours)
|
|
65
|
-
const nMinutes = isNaN(parseInt(minutes)) ? 0 : parseInt(minutes)
|
|
92
|
+
const [sign, hours, minutes] = offset.slice(1)
|
|
93
|
+
const nHours = isNaN(parseInt(hours)) ? 0 : parseInt(hours)
|
|
94
|
+
const nMinutes = isNaN(parseInt(minutes)) ? 0 : parseInt(minutes)
|
|
66
95
|
const result = ((nHours * 60) + nMinutes) * 60 * 1000;
|
|
67
96
|
return sign === '-' ? - result : result;
|
|
68
97
|
}
|
|
98
|
+
|
|
69
99
|
function getTimezoneOffsetFrom(otherTimezone) {
|
|
70
100
|
var date = new Date();
|
|
71
101
|
function objFromStr(str) {
|
|
@@ -80,29 +110,65 @@ function getTimezoneOffsetFrom(otherTimezone) {
|
|
|
80
110
|
var other = objFromStr(str);
|
|
81
111
|
str = date.toLocaleString('en-US', { day: 'numeric', hour: 'numeric', minute: 'numeric', hourCycle: 'h23' });
|
|
82
112
|
var myLocale = objFromStr(str);
|
|
83
|
-
var otherOffset = (other.day * 24 * 60) + (other.hour * 60) + (other.minute);
|
|
84
|
-
var myLocaleOffset = (myLocale.day * 24 * 60) + (myLocale.hour * 60) + (myLocale.minute);
|
|
113
|
+
var otherOffset = (other.day * 24 * 60) + (other.hour * 60) + (other.minute); // utc date + otherTimezoneDifference
|
|
114
|
+
var myLocaleOffset = (myLocale.day * 24 * 60) + (myLocale.hour * 60) + (myLocale.minute); // utc date + myTimeZoneDifference
|
|
115
|
+
// (utc date + otherZoneDifference) - (utc date + myZoneDifference) - (-1 * myTimeZoneDifference)
|
|
85
116
|
return otherOffset - myLocaleOffset - date.getTimezoneOffset();
|
|
86
117
|
}
|
|
87
|
-
|
|
88
|
-
|
|
118
|
+
|
|
119
|
+
export function offsetMSFallback(dateObj, timezone) {
|
|
120
|
+
//const defaultOffset = dateObj.getTimezoneOffset();
|
|
121
|
+
const timezoneOffset = getTimezoneOffsetFrom(timezone)
|
|
89
122
|
return timezoneOffset * 60 * 1000;
|
|
90
123
|
}
|
|
91
|
-
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* adjust from the default JavaScript timezone to the default timezone
|
|
127
|
+
* @param dateObj {Date}
|
|
128
|
+
* @param timeZone {string}
|
|
129
|
+
*/
|
|
130
|
+
export function adjustTimeZone(dateObj, timeZone) {
|
|
92
131
|
if (dateObj === null) return null;
|
|
132
|
+
// const defaultOffset = new Intl.DateTimeFormat('en-US', { timeZoneName: 'longOffset'}).format(dateObj);
|
|
93
133
|
let baseDate = dateObj.getTime() - dateObj.getTimezoneOffset() * 60 * 1000;
|
|
94
134
|
const offset = offsetMS(dateObj, timeZone);
|
|
95
|
-
offsetMSFallback(dateObj, timeZone);
|
|
135
|
+
const fallBackOffset = offsetMSFallback(dateObj, timeZone);
|
|
96
136
|
baseDate += - offset;
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
// get the offset for the default JS environment
|
|
140
|
+
// return days since the epoch
|
|
97
141
|
return new Date(baseDate);
|
|
98
142
|
}
|
|
99
|
-
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Our script object model treats dates as numbers where the integer portion is days since the epoch,
|
|
146
|
+
* the fractional portion is the number hours in the day
|
|
147
|
+
* @param dateObj {Date}
|
|
148
|
+
* @returns {number}
|
|
149
|
+
*/
|
|
150
|
+
export function datetimeToNumber(dateObj) {
|
|
100
151
|
if (dateObj === null) return 0;
|
|
152
|
+
// return days since the epoch
|
|
101
153
|
return dateObj.getTime() / ( 1000 * 60 * 60 * 24 );
|
|
102
154
|
}
|
|
103
|
-
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Our script object model treats dates as numbers where the integer portion is days since the epoch,
|
|
158
|
+
* the fractional portion is the number hours in the day
|
|
159
|
+
* @param num
|
|
160
|
+
* @returns {Date}
|
|
161
|
+
*/
|
|
162
|
+
export function numberToDatetime(num) {
|
|
104
163
|
return new Date(Math.round(num * 1000 * 60 * 60 * 24));
|
|
105
164
|
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* in some cases, DateTimeFormat doesn't respect the 'numeric' vs. '2-digit' setting
|
|
168
|
+
* for time values. The function corrects that
|
|
169
|
+
* @param formattedParts instance of Intl.DateTimeFormatPart[]
|
|
170
|
+
* @param parsed
|
|
171
|
+
*/
|
|
106
172
|
function fixDigits(formattedParts, parsed) {
|
|
107
173
|
['hour', 'minute', 'second'].forEach(type => {
|
|
108
174
|
const defn = formattedParts.find(f => f.type === type);
|
|
@@ -112,29 +178,55 @@ function fixDigits(formattedParts, parsed) {
|
|
|
112
178
|
if (fmt === 'numeric' && defn.value.length === 2 && defn.value.charAt(0) === '0') defn.value = defn.value.slice(1);
|
|
113
179
|
});
|
|
114
180
|
}
|
|
181
|
+
|
|
115
182
|
function fixYear(formattedParts, parsed) {
|
|
183
|
+
// two digit years are handled differently in DateTimeFormat. 00 becomes 1900
|
|
184
|
+
// providing a two digit year 0010 gets formatted to 10 and when parsed becomes 1910
|
|
185
|
+
// Hence we need to pad the year with 0 as required by the skeleton and mentioned in
|
|
186
|
+
// unicode. https://www.unicode.org/reports/tr35/tr35-dates.html#dfst-year
|
|
116
187
|
const defn = formattedParts.find(f => f.type === 'year');
|
|
117
188
|
if (!defn) return;
|
|
189
|
+
// eslint-disable-next-line no-unused-vars
|
|
118
190
|
const chars = parsed.find(pair => pair[0] === 'year')[2];
|
|
119
191
|
while(defn.value.length < chars) {
|
|
120
192
|
defn.value = `0${defn.value}`;
|
|
121
193
|
}
|
|
122
194
|
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
*
|
|
198
|
+
* @param dateValue {Date}
|
|
199
|
+
* @param language {string}
|
|
200
|
+
* @param skeleton {string}
|
|
201
|
+
* @param timeZone {string}
|
|
202
|
+
* @returns {T}
|
|
203
|
+
*/
|
|
123
204
|
function formatDateToParts(dateValue, language, skeleton, timeZone) {
|
|
205
|
+
// DateTimeFormat renames some of the options in its formatted output
|
|
206
|
+
//@ts-ignore
|
|
124
207
|
const mappings = key => ({
|
|
125
208
|
hour12: 'dayPeriod',
|
|
126
209
|
fractionalSecondDigits: 'fractionalSecond',
|
|
127
210
|
})[key] || key;
|
|
211
|
+
|
|
212
|
+
// produces an array of name/value pairs of skeleton parts
|
|
128
213
|
const allParameters = parseDateTimeSkeleton(skeleton, language);
|
|
129
214
|
allParameters.push(['timeZone', timeZone]);
|
|
215
|
+
|
|
130
216
|
const parsed = allParameters.filter(p => !p[0].startsWith('x-'));
|
|
131
217
|
const nonStandard = allParameters.filter(p => p[0].startsWith('x-'));
|
|
218
|
+
// reduce to a set of options that can be used to format
|
|
132
219
|
const options = Object.fromEntries(parsed);
|
|
133
220
|
delete options.literal;
|
|
221
|
+
|
|
134
222
|
const df = new Intl.DateTimeFormat(language, options);
|
|
223
|
+
// formattedParts will have all the pieces we need for our date -- but not in the correct order
|
|
135
224
|
const formattedParts = df.formatToParts(dateValue);
|
|
225
|
+
|
|
136
226
|
fixDigits(formattedParts, allParameters);
|
|
137
227
|
fixYear(formattedParts, parsed);
|
|
228
|
+
// iterate through the original parsed components and use its ordering and literals,
|
|
229
|
+
// and add the formatted pieces
|
|
138
230
|
return parsed.reduce((result, cur) => {
|
|
139
231
|
if (cur[0] === 'literal') result.push(cur);
|
|
140
232
|
else {
|
|
@@ -144,25 +236,37 @@ function formatDateToParts(dateValue, language, skeleton, timeZone) {
|
|
|
144
236
|
const category = tz[0];
|
|
145
237
|
if (category === 'Z') {
|
|
146
238
|
if (tz.length < 4) {
|
|
239
|
+
// handle 'Z', 'ZZ', 'ZZZ' Time Zone: ISO8601 basic hms? / RFC 822
|
|
147
240
|
v.value = v.value.replace(/(GMT|:)/g, '');
|
|
148
241
|
if (v.value === '') v.value = '+0000';
|
|
149
242
|
} else if (tz.length === 5) {
|
|
243
|
+
// 'ZZZZZ' Time Zone: ISO8601 extended hms?
|
|
150
244
|
if (v.value === 'GMT') v.value = 'Z';
|
|
151
245
|
else v.value = v.value.replace(/GMT/, '');
|
|
152
246
|
}
|
|
153
247
|
}
|
|
154
248
|
if (category === 'X' || category === 'x') {
|
|
155
249
|
if (tz.length === 1) {
|
|
250
|
+
// 'X' ISO8601 basic hm?, with Z for 0
|
|
251
|
+
// -08, +0530, Z
|
|
252
|
+
// 'x' ISO8601 basic hm?, without Z for 0
|
|
156
253
|
v.value = v.value.replace(/(GMT|:(00)?)/g, '');
|
|
157
254
|
}
|
|
158
255
|
if (tz.length === 2) {
|
|
256
|
+
// 'XX' ISO8601 basic hm, with Z
|
|
257
|
+
// -0800, Z
|
|
258
|
+
// 'xx' ISO8601 basic hm, without Z
|
|
159
259
|
v.value = v.value.replace(/(GMT|:)/g, '');
|
|
160
260
|
}
|
|
161
261
|
if (tz.length === 3) {
|
|
262
|
+
// 'XXX' ISO8601 extended hm, with Z
|
|
263
|
+
// -08:00, Z
|
|
264
|
+
// 'xxx' ISO8601 extended hm, without Z
|
|
162
265
|
v.value = v.value.replace(/GMT/g, '');
|
|
163
266
|
}
|
|
164
267
|
if (category === 'X' && v.value === '') v.value = 'Z';
|
|
165
268
|
} else if (tz === 'O') {
|
|
269
|
+
// eliminate 'GMT', leading and trailing zeros
|
|
166
270
|
v.value = v.value.replace(/GMT/g, '').replace(/0(\d+):/, '$1:').replace(/:00/, '');
|
|
167
271
|
if (v.value === '') v.value = '+0';
|
|
168
272
|
}
|
|
@@ -172,28 +276,41 @@ function formatDateToParts(dateValue, language, skeleton, timeZone) {
|
|
|
172
276
|
return result;
|
|
173
277
|
}, []);
|
|
174
278
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
*
|
|
282
|
+
* @param dateValue {Date}
|
|
283
|
+
* @param language {string}
|
|
284
|
+
* @param skeleton {string}
|
|
285
|
+
* @param timeZone {string}
|
|
286
|
+
*/
|
|
287
|
+
export function formatDate(dateValue, language, skeleton, timeZone) {
|
|
179
288
|
if (ShorthandStyles.find(type => skeleton.includes(type))) {
|
|
180
289
|
const options = {timeZone};
|
|
290
|
+
// the skeleton could have two keywords -- one for date, one for time
|
|
181
291
|
const parts = skeleton.split(/\s/).filter(s => s.length);
|
|
182
292
|
if (ShorthandStyles.indexOf(parts[0]) > -1) {
|
|
183
|
-
options.dateStyle = parts[0]
|
|
293
|
+
options.dateStyle = parts[0]
|
|
184
294
|
}
|
|
185
295
|
if (parts.length > 1 && ShorthandStyles.indexOf(parts[1]) > -1) {
|
|
186
|
-
options.timeStyle = parts[1]
|
|
296
|
+
options.timeStyle = parts[1]
|
|
187
297
|
}
|
|
188
298
|
return new Intl.DateTimeFormat(language, options).format(dateValue);
|
|
189
299
|
}
|
|
190
300
|
const parts = formatDateToParts(dateValue, language, skeleton, timeZone);
|
|
191
301
|
return parts.map(p => p[1]).join('');
|
|
192
302
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
*
|
|
306
|
+
* @param dateString {string}
|
|
307
|
+
* @param language {string}
|
|
308
|
+
* @param skeleton {string}
|
|
309
|
+
* @param timeZone {string}
|
|
310
|
+
*/
|
|
311
|
+
export function parseDate(dateString, language, skeleton, timeZone, bUseUTC = false) {
|
|
312
|
+
// start by getting all the localized parts of a date/time picture:
|
|
313
|
+
// digits, calendar name
|
|
197
314
|
const lookups = [];
|
|
198
315
|
const regexParts = [];
|
|
199
316
|
const calendar = calendarName(language);
|
|
@@ -204,11 +321,13 @@ function parseDate(dateString, language, skeleton, timeZone, bUseUTC = false) {
|
|
|
204
321
|
let hourCycle = 'h12';
|
|
205
322
|
let _bUseUTC = bUseUTC;
|
|
206
323
|
let _setFullYear = false;
|
|
324
|
+
// functions to process the results of the regex match
|
|
207
325
|
const isSeparator = str => str.length === 1 && ':-/.'.includes(str);
|
|
208
326
|
const monthNumber = str => getNumber(str) - 1;
|
|
209
327
|
const getNumber = str => str.split('').reduce((total, digit) => (total * 10) + digits.indexOf(digit), 0);
|
|
210
328
|
const yearNumber = templateDigits => str => {
|
|
211
329
|
let year = getNumber(str);
|
|
330
|
+
//todo: align with AF
|
|
212
331
|
year = year < 100 && templateDigits === 2 ? year + 2000 : year;
|
|
213
332
|
if (calendar === 'islamic') year = Math.ceil(year * 0.97 + 622);
|
|
214
333
|
if (templateDigits > 2 && year < 100) {
|
|
@@ -217,51 +336,67 @@ function parseDate(dateString, language, skeleton, timeZone, bUseUTC = false) {
|
|
|
217
336
|
return year;
|
|
218
337
|
};
|
|
219
338
|
const monthLookup = list => month => list.indexOf(month);
|
|
339
|
+
|
|
220
340
|
const parsed = parseDateTimeSkeleton(skeleton, language);
|
|
221
341
|
const months = monthNames(language, Object.fromEntries(parsed));
|
|
342
|
+
// build up a regex expression that identifies each option in the skeleton
|
|
343
|
+
// We build two parallel structures:
|
|
344
|
+
// 1. the regex expression that will extract parts of the date/time
|
|
345
|
+
// 2. a lookup array that will convert the matched results into date/time values
|
|
222
346
|
parsed.forEach(([option, value, len]) => {
|
|
347
|
+
// use a generic regex pattern for all single-character separator literals.
|
|
348
|
+
// Then we'll be forgiving when it comes to separators: / vs - vs : etc
|
|
223
349
|
if (option === 'literal') {
|
|
224
350
|
if (isSeparator(value)) regexParts.push(`[^${digits[0]}-${digits[9]}]`);
|
|
225
351
|
else regexParts.push(value);
|
|
352
|
+
|
|
226
353
|
} else if (option === 'month' && ['numeric', '2-digit'].includes(value)) {
|
|
227
354
|
regexParts.push(twoDigit);
|
|
228
355
|
lookups.push(['month', monthNumber]);
|
|
356
|
+
|
|
229
357
|
} else if (option === 'month' && ['formatted', 'long', 'short', 'narrow'].includes(value)) {
|
|
230
358
|
regexParts.push(`(${months.join('|')})`);
|
|
231
359
|
lookups.push(['month', monthLookup(months)]);
|
|
360
|
+
|
|
232
361
|
} else if (['day', 'minute', 'second'].includes(option)) {
|
|
233
362
|
if (option === 'minute' || option === 'second') {
|
|
234
|
-
_bUseUTC = false
|
|
363
|
+
_bUseUTC = false
|
|
235
364
|
}
|
|
236
365
|
regexParts.push(twoDigit);
|
|
237
366
|
lookups.push([option, getNumber]);
|
|
367
|
+
|
|
238
368
|
} else if (option === 'fractionalSecondDigits') {
|
|
239
|
-
_bUseUTC = false
|
|
369
|
+
_bUseUTC = false
|
|
240
370
|
regexParts.push(threeDigit);
|
|
241
371
|
lookups.push([option, (v, obj) => obj.fractionalSecondDigits + getNumber(v)]);
|
|
372
|
+
|
|
242
373
|
} else if (option === 'hour') {
|
|
243
|
-
_bUseUTC = false
|
|
374
|
+
_bUseUTC = false
|
|
244
375
|
regexParts.push(twoDigit);
|
|
245
376
|
lookups.push([option, (v, obj) => obj.hour + getNumber(v)]);
|
|
246
377
|
} else if (option === 'year') {
|
|
247
378
|
regexParts.push('numeric' === value ? fourDigit : twoDigit);
|
|
248
379
|
lookups.push(['year', yearNumber(len)]);
|
|
249
380
|
} else if (option === 'dayPeriod') {
|
|
250
|
-
_bUseUTC = false
|
|
381
|
+
_bUseUTC = false
|
|
251
382
|
const dayPeriod = getDayPeriod(language);
|
|
252
383
|
if (dayPeriod) {
|
|
253
384
|
regexParts.push(dayPeriod.regex);
|
|
254
385
|
lookups.push(['hour', dayPeriod.fn]);
|
|
255
386
|
}
|
|
387
|
+
// Any other part that we don't need, we'll just add a non-greedy consumption
|
|
256
388
|
} else if (option === 'hourCycle') {
|
|
257
|
-
_bUseUTC = false
|
|
389
|
+
_bUseUTC = false
|
|
258
390
|
hourCycle = value;
|
|
259
391
|
} else if (option === 'x-timeZoneName') {
|
|
260
|
-
_bUseUTC = false
|
|
392
|
+
_bUseUTC = false
|
|
393
|
+
// we handle only the GMT offset picture
|
|
261
394
|
regexParts.push('(?:GMT|UTC|Z)?([+\\-−0-9]{0,3}:?[0-9]{0,2})');
|
|
262
395
|
lookups.push([option, (v, obj) => {
|
|
263
396
|
_bUseUTC = true;
|
|
397
|
+
// v could be undefined if we're on GMT time
|
|
264
398
|
if (!v) return;
|
|
399
|
+
// replace the unicode minus, then extract hours [and minutes]
|
|
265
400
|
const timeParts = v.replace(/−/, '-').match(/([+\-\d]{2,3}):?(\d{0,2})/);
|
|
266
401
|
const hours = timeParts[1] * 1;
|
|
267
402
|
obj.hour -= hours;
|
|
@@ -269,14 +404,17 @@ function parseDate(dateString, language, skeleton, timeZone, bUseUTC = false) {
|
|
|
269
404
|
obj.minute -= (hours < 0) ? - mins : mins;
|
|
270
405
|
}]);
|
|
271
406
|
} else if (option !== 'timeZoneName') {
|
|
272
|
-
_bUseUTC = false
|
|
407
|
+
_bUseUTC = false
|
|
273
408
|
regexParts.push('.+?');
|
|
274
409
|
}
|
|
410
|
+
|
|
275
411
|
return regexParts;
|
|
276
412
|
}, []);
|
|
277
413
|
const regex = new RegExp(regexParts.join(''));
|
|
278
414
|
const match = dateString.match(regex);
|
|
279
415
|
if (match === null) return dateString;
|
|
416
|
+
|
|
417
|
+
// now loop through all the matched pieces and build up an object we'll use to create a Date object
|
|
280
418
|
const dateObj = {year: 1972, month: 0, day: 1, hour: 0, minute: 0, second: 0, fractionalSecondDigits: 0};
|
|
281
419
|
match.slice(1).forEach((m, index) => {
|
|
282
420
|
const [element, func] = lookups[index];
|
|
@@ -313,8 +451,7 @@ function parseDate(dateString, language, skeleton, timeZone, bUseUTC = false) {
|
|
|
313
451
|
}
|
|
314
452
|
return timeZone == null ? jsDate : adjustTimeZone(jsDate, timeZone);
|
|
315
453
|
}
|
|
316
|
-
|
|
454
|
+
|
|
455
|
+
export function parseDefaultDate(dateString, language, bUseUTC) {
|
|
317
456
|
return parseDate(dateString, language, 'short', null, false);
|
|
318
457
|
}
|
|
319
|
-
|
|
320
|
-
export { adjustTimeZone, datetimeToNumber, formatDate, numberToDatetime, offsetMS, offsetMSFallback, parseDate, parseDefaultDate };
|