1 | // 12.1 The Intl.DateTimeFormat constructor
|
2 | // ==================================
|
3 |
|
4 | import {
|
5 | toLatinUpperCase,
|
6 | } from './6.locales-currencies-tz.js';
|
7 |
|
8 | import {
|
9 | Intl,
|
10 | } from "./8.intl.js";
|
11 |
|
12 | import {
|
13 | CanonicalizeLocaleList,
|
14 | ResolveLocale,
|
15 | GetOption,
|
16 | SupportedLocales,
|
17 | } from "./9.negotiation.js";
|
18 |
|
19 | import {
|
20 | FormatNumber,
|
21 | } from "./11.numberformat.js";
|
22 |
|
23 | import {
|
24 | createDateTimeFormats,
|
25 | } from "./cldr";
|
26 |
|
27 | import {
|
28 | internals,
|
29 | es3,
|
30 | fnBind,
|
31 | defineProperty,
|
32 | toObject,
|
33 | getInternalProperties,
|
34 | createRegExpRestore,
|
35 | secret,
|
36 | Record,
|
37 | List,
|
38 | hop,
|
39 | objCreate,
|
40 | arrPush,
|
41 | arrIndexOf,
|
42 | } from './util.js';
|
43 |
|
44 | // An object map of date component keys, saves using a regex later
|
45 | const dateWidths = objCreate(null, { narrow:{}, short:{}, long:{} });
|
46 |
|
47 | /**
|
48 | * Returns a string for a date component, resolved using multiple inheritance as specified
|
49 | * as specified in the Unicode Technical Standard 35.
|
50 | */
|
51 | function resolveDateString(data, ca, component, width, key) {
|
52 | // From http://www.unicode.org/reports/tr35/tr35.html#Multiple_Inheritance:
|
53 | // 'In clearly specified instances, resources may inherit from within the same locale.
|
54 | // For example, ... the Buddhist calendar inherits from the Gregorian calendar.'
|
55 | let obj = data[ca] && data[ca][component]
|
56 | ? data[ca][component]
|
57 | : data.gregory[component],
|
58 |
|
59 | // "sideways" inheritance resolves strings when a key doesn't exist
|
60 | alts = {
|
61 | narrow: ['short', 'long'],
|
62 | short: ['long', 'narrow'],
|
63 | long: ['short', 'narrow'],
|
64 | },
|
65 |
|
66 | //
|
67 | resolved = hop.call(obj, width)
|
68 | ? obj[width]
|
69 | : hop.call(obj, alts[width][0])
|
70 | ? obj[alts[width][0]]
|
71 | : obj[alts[width][1]];
|
72 |
|
73 | // `key` wouldn't be specified for components 'dayPeriods'
|
74 | return key !== null ? resolved[key] : resolved;
|
75 | }
|
76 |
|
77 | // Define the DateTimeFormat constructor internally so it cannot be tainted
|
78 | export function DateTimeFormatConstructor () {
|
79 | let locales = arguments[0];
|
80 | let options = arguments[1];
|
81 |
|
82 | if (!this || this === Intl) {
|
83 | return new Intl.DateTimeFormat(locales, options);
|
84 | }
|
85 | return InitializeDateTimeFormat(toObject(this), locales, options);
|
86 | }
|
87 |
|
88 | defineProperty(Intl, 'DateTimeFormat', {
|
89 | configurable: true,
|
90 | writable: true,
|
91 | value: DateTimeFormatConstructor,
|
92 | });
|
93 |
|
94 | // Must explicitly set prototypes as unwritable
|
95 | defineProperty(DateTimeFormatConstructor, 'prototype', {
|
96 | writable: false,
|
97 | });
|
98 |
|
99 | /**
|
100 | * The abstract operation InitializeDateTimeFormat accepts the arguments dateTimeFormat
|
101 | * (which must be an object), locales, and options. It initializes dateTimeFormat as a
|
102 | * DateTimeFormat object.
|
103 | */
|
104 | export function/* 12.1.1.1 */InitializeDateTimeFormat (dateTimeFormat, locales, options) {
|
105 | // This will be a internal properties object if we're not already initialized
|
106 | let internal = getInternalProperties(dateTimeFormat);
|
107 |
|
108 | // Create an object whose props can be used to restore the values of RegExp props
|
109 | let regexpState = createRegExpRestore();
|
110 |
|
111 | // 1. If dateTimeFormat has an [[initializedIntlObject]] internal property with
|
112 | // value true, throw a TypeError exception.
|
113 | if (internal['[[initializedIntlObject]]'] === true)
|
114 | throw new TypeError('`this` object has already been initialized as an Intl object');
|
115 |
|
116 | // Need this to access the `internal` object
|
117 | defineProperty(dateTimeFormat, '__getInternalProperties', {
|
118 | value: function () {
|
119 | // NOTE: Non-standard, for internal use only
|
120 | if (arguments[0] === secret)
|
121 | return internal;
|
122 | },
|
123 | });
|
124 |
|
125 | // 2. Set the [[initializedIntlObject]] internal property of numberFormat to true.
|
126 | internal['[[initializedIntlObject]]'] = true;
|
127 |
|
128 | // 3. Let requestedLocales be the result of calling the CanonicalizeLocaleList
|
129 | // abstract operation (defined in 9.2.1) with argument locales.
|
130 | let requestedLocales = CanonicalizeLocaleList(locales);
|
131 |
|
132 | // 4. Let options be the result of calling the ToDateTimeOptions abstract
|
133 | // operation (defined below) with arguments options, "any", and "date".
|
134 | options = ToDateTimeOptions(options, 'any', 'date');
|
135 |
|
136 | // 5. Let opt be a new Record.
|
137 | let opt = new Record();
|
138 |
|
139 | // 6. Let matcher be the result of calling the GetOption abstract operation
|
140 | // (defined in 9.2.9) with arguments options, "localeMatcher", "string", a List
|
141 | // containing the two String values "lookup" and "best fit", and "best fit".
|
142 | let matcher = GetOption(options, 'localeMatcher', 'string', new List('lookup', 'best fit'), 'best fit');
|
143 |
|
144 | // 7. Set opt.[[localeMatcher]] to matcher.
|
145 | opt['[[localeMatcher]]'] = matcher;
|
146 |
|
147 | // 8. Let DateTimeFormat be the standard built-in object that is the initial
|
148 | // value of Intl.DateTimeFormat.
|
149 | let DateTimeFormat = internals.DateTimeFormat; // This is what we *really* need
|
150 |
|
151 | // 9. Let localeData be the value of the [[localeData]] internal property of
|
152 | // DateTimeFormat.
|
153 | let localeData = DateTimeFormat['[[localeData]]'];
|
154 |
|
155 | // 10. Let r be the result of calling the ResolveLocale abstract operation
|
156 | // (defined in 9.2.5) with the [[availableLocales]] internal property of
|
157 | // DateTimeFormat, requestedLocales, opt, the [[relevantExtensionKeys]]
|
158 | // internal property of DateTimeFormat, and localeData.
|
159 | let r = ResolveLocale(DateTimeFormat['[[availableLocales]]'], requestedLocales,
|
160 | opt, DateTimeFormat['[[relevantExtensionKeys]]'], localeData);
|
161 |
|
162 | // 11. Set the [[locale]] internal property of dateTimeFormat to the value of
|
163 | // r.[[locale]].
|
164 | internal['[[locale]]'] = r['[[locale]]'];
|
165 |
|
166 | // 12. Set the [[calendar]] internal property of dateTimeFormat to the value of
|
167 | // r.[[ca]].
|
168 | internal['[[calendar]]'] = r['[[ca]]'];
|
169 |
|
170 | // 13. Set the [[numberingSystem]] internal property of dateTimeFormat to the value of
|
171 | // r.[[nu]].
|
172 | internal['[[numberingSystem]]'] = r['[[nu]]'];
|
173 |
|
174 | // The specification doesn't tell us to do this, but it's helpful later on
|
175 | internal['[[dataLocale]]'] = r['[[dataLocale]]'];
|
176 |
|
177 | // 14. Let dataLocale be the value of r.[[dataLocale]].
|
178 | let dataLocale = r['[[dataLocale]]'];
|
179 |
|
180 | // 15. Let tz be the result of calling the [[Get]] internal method of options with
|
181 | // argument "timeZone".
|
182 | let tz = options.timeZone;
|
183 |
|
184 | // 16. If tz is not undefined, then
|
185 | if (tz !== undefined) {
|
186 | // a. Let tz be ToString(tz).
|
187 | // b. Convert tz to upper case as described in 6.1.
|
188 | // NOTE: If an implementation accepts additional time zone values, as permitted
|
189 | // under certain conditions by the Conformance clause, different casing
|
190 | // rules apply.
|
191 | tz = toLatinUpperCase(tz);
|
192 |
|
193 | // c. If tz is not "UTC", then throw a RangeError exception.
|
194 | // ###TODO: accept more time zones###
|
195 | if (tz !== 'UTC')
|
196 | throw new RangeError('timeZone is not supported.');
|
197 | }
|
198 |
|
199 | // 17. Set the [[timeZone]] internal property of dateTimeFormat to tz.
|
200 | internal['[[timeZone]]'] = tz;
|
201 |
|
202 | // 18. Let opt be a new Record.
|
203 | opt = new Record();
|
204 |
|
205 | // 19. For each row of Table 3, except the header row, do:
|
206 | for (let prop in dateTimeComponents) {
|
207 | if (!hop.call(dateTimeComponents, prop))
|
208 | continue;
|
209 |
|
210 | // 20. Let prop be the name given in the Property column of the row.
|
211 | // 21. Let value be the result of calling the GetOption abstract operation,
|
212 | // passing as argument options, the name given in the Property column of the
|
213 | // row, "string", a List containing the strings given in the Values column of
|
214 | // the row, and undefined.
|
215 | let value = GetOption(options, prop, 'string', dateTimeComponents[prop]);
|
216 |
|
217 | // 22. Set opt.[[<prop>]] to value.
|
218 | opt['[['+prop+']]'] = value;
|
219 | }
|
220 |
|
221 | // Assigned a value below
|
222 | let bestFormat;
|
223 |
|
224 | // 23. Let dataLocaleData be the result of calling the [[Get]] internal method of
|
225 | // localeData with argument dataLocale.
|
226 | let dataLocaleData = localeData[dataLocale];
|
227 |
|
228 | // 24. Let formats be the result of calling the [[Get]] internal method of
|
229 | // dataLocaleData with argument "formats".
|
230 | // Note: we process the CLDR formats into the spec'd structure
|
231 | let formats = ToDateTimeFormats(dataLocaleData.formats);
|
232 |
|
233 | // 25. Let matcher be the result of calling the GetOption abstract operation with
|
234 | // arguments options, "formatMatcher", "string", a List containing the two String
|
235 | // values "basic" and "best fit", and "best fit".
|
236 | matcher = GetOption(options, 'formatMatcher', 'string', new List('basic', 'best fit'), 'best fit');
|
237 |
|
238 | // Optimization: caching the processed formats as a one time operation by
|
239 | // replacing the initial structure from localeData
|
240 | dataLocaleData.formats = formats;
|
241 |
|
242 | // 26. If matcher is "basic", then
|
243 | if (matcher === 'basic') {
|
244 | // 27. Let bestFormat be the result of calling the BasicFormatMatcher abstract
|
245 | // operation (defined below) with opt and formats.
|
246 | bestFormat = BasicFormatMatcher(opt, formats);
|
247 |
|
248 | // 28. Else
|
249 | } else {
|
250 | {
|
251 | // diverging
|
252 | let hr12 = GetOption(options, 'hour12', 'boolean'/*, undefined, undefined*/);
|
253 | opt.hour12 = hr12 === undefined ? dataLocaleData.hour12 : hr12;
|
254 | }
|
255 | // 29. Let bestFormat be the result of calling the BestFitFormatMatcher
|
256 | // abstract operation (defined below) with opt and formats.
|
257 | bestFormat = BestFitFormatMatcher(opt, formats);
|
258 | }
|
259 |
|
260 | // 30. For each row in Table 3, except the header row, do
|
261 | for (let prop in dateTimeComponents) {
|
262 | if (!hop.call(dateTimeComponents, prop))
|
263 | continue;
|
264 |
|
265 | // a. Let prop be the name given in the Property column of the row.
|
266 | // b. Let pDesc be the result of calling the [[GetOwnProperty]] internal method of
|
267 | // bestFormat with argument prop.
|
268 | // c. If pDesc is not undefined, then
|
269 | if (hop.call(bestFormat, prop)) {
|
270 | // i. Let p be the result of calling the [[Get]] internal method of bestFormat
|
271 | // with argument prop.
|
272 | let p = bestFormat[prop];
|
273 | {
|
274 | // diverging
|
275 | p = bestFormat._ && hop.call(bestFormat._, prop) ? bestFormat._[prop] : p;
|
276 | }
|
277 |
|
278 | // ii. Set the [[<prop>]] internal property of dateTimeFormat to p.
|
279 | internal['[['+prop+']]'] = p;
|
280 | }
|
281 | }
|
282 |
|
283 | let pattern; // Assigned a value below
|
284 |
|
285 | // 31. Let hr12 be the result of calling the GetOption abstract operation with
|
286 | // arguments options, "hour12", "boolean", undefined, and undefined.
|
287 | let hr12 = GetOption(options, 'hour12', 'boolean'/*, undefined, undefined*/);
|
288 |
|
289 | // 32. If dateTimeFormat has an internal property [[hour]], then
|
290 | if (internal['[[hour]]']) {
|
291 | // a. If hr12 is undefined, then let hr12 be the result of calling the [[Get]]
|
292 | // internal method of dataLocaleData with argument "hour12".
|
293 | hr12 = hr12 === undefined ? dataLocaleData.hour12 : hr12;
|
294 |
|
295 | // b. Set the [[hour12]] internal property of dateTimeFormat to hr12.
|
296 | internal['[[hour12]]'] = hr12;
|
297 |
|
298 | // c. If hr12 is true, then
|
299 | if (hr12 === true) {
|
300 | // i. Let hourNo0 be the result of calling the [[Get]] internal method of
|
301 | // dataLocaleData with argument "hourNo0".
|
302 | let hourNo0 = dataLocaleData.hourNo0;
|
303 |
|
304 | // ii. Set the [[hourNo0]] internal property of dateTimeFormat to hourNo0.
|
305 | internal['[[hourNo0]]'] = hourNo0;
|
306 |
|
307 | // iii. Let pattern be the result of calling the [[Get]] internal method of
|
308 | // bestFormat with argument "pattern12".
|
309 | pattern = bestFormat.pattern12;
|
310 | }
|
311 |
|
312 | // d. Else
|
313 | else
|
314 | // i. Let pattern be the result of calling the [[Get]] internal method of
|
315 | // bestFormat with argument "pattern".
|
316 | pattern = bestFormat.pattern;
|
317 | }
|
318 |
|
319 | // 33. Else
|
320 | else
|
321 | // a. Let pattern be the result of calling the [[Get]] internal method of
|
322 | // bestFormat with argument "pattern".
|
323 | pattern = bestFormat.pattern;
|
324 |
|
325 | // 34. Set the [[pattern]] internal property of dateTimeFormat to pattern.
|
326 | internal['[[pattern]]'] = pattern;
|
327 |
|
328 | // 35. Set the [[boundFormat]] internal property of dateTimeFormat to undefined.
|
329 | internal['[[boundFormat]]'] = undefined;
|
330 |
|
331 | // 36. Set the [[initializedDateTimeFormat]] internal property of dateTimeFormat to
|
332 | // true.
|
333 | internal['[[initializedDateTimeFormat]]'] = true;
|
334 |
|
335 | // In ES3, we need to pre-bind the format() function
|
336 | if (es3)
|
337 | dateTimeFormat.format = GetFormatDateTime.call(dateTimeFormat);
|
338 |
|
339 | // Restore the RegExp properties
|
340 | regexpState.exp.test(regexpState.input);
|
341 |
|
342 | // Return the newly initialised object
|
343 | return dateTimeFormat;
|
344 | }
|
345 |
|
346 | /**
|
347 | * Several DateTimeFormat algorithms use values from the following table, which provides
|
348 | * property names and allowable values for the components of date and time formats:
|
349 | */
|
350 | let dateTimeComponents = {
|
351 | weekday: [ "narrow", "short", "long" ],
|
352 | era: [ "narrow", "short", "long" ],
|
353 | year: [ "2-digit", "numeric" ],
|
354 | month: [ "2-digit", "numeric", "narrow", "short", "long" ],
|
355 | day: [ "2-digit", "numeric" ],
|
356 | hour: [ "2-digit", "numeric" ],
|
357 | minute: [ "2-digit", "numeric" ],
|
358 | second: [ "2-digit", "numeric" ],
|
359 | timeZoneName: [ "short", "long" ],
|
360 | };
|
361 |
|
362 | /**
|
363 | * When the ToDateTimeOptions abstract operation is called with arguments options,
|
364 | * required, and defaults, the following steps are taken:
|
365 | */
|
366 | function ToDateTimeFormats(formats) {
|
367 | if (Object.prototype.toString.call(formats) === '[object Array]') {
|
368 | return formats;
|
369 | }
|
370 | return createDateTimeFormats(formats);
|
371 | }
|
372 |
|
373 | /**
|
374 | * When the ToDateTimeOptions abstract operation is called with arguments options,
|
375 | * required, and defaults, the following steps are taken:
|
376 | */
|
377 | export function ToDateTimeOptions (options, required, defaults) {
|
378 | // 1. If options is undefined, then let options be null, else let options be
|
379 | // ToObject(options).
|
380 | if (options === undefined)
|
381 | options = null;
|
382 |
|
383 | else {
|
384 | // (#12) options needs to be a Record, but it also needs to inherit properties
|
385 | let opt2 = toObject(options);
|
386 | options = new Record();
|
387 |
|
388 | for (let k in opt2)
|
389 | options[k] = opt2[k];
|
390 | }
|
391 |
|
392 | // 2. Let create be the standard built-in function object defined in ES5, 15.2.3.5.
|
393 | let create = objCreate;
|
394 |
|
395 | // 3. Let options be the result of calling the [[Call]] internal method of create with
|
396 | // undefined as the this value and an argument list containing the single item
|
397 | // options.
|
398 | options = create(options);
|
399 |
|
400 | // 4. Let needDefaults be true.
|
401 | let needDefaults = true;
|
402 |
|
403 | // 5. If required is "date" or "any", then
|
404 | if (required === 'date' || required === 'any') {
|
405 | // a. For each of the property names "weekday", "year", "month", "day":
|
406 | // i. If the result of calling the [[Get]] internal method of options with the
|
407 | // property name is not undefined, then let needDefaults be false.
|
408 | if (options.weekday !== undefined || options.year !== undefined
|
409 | || options.month !== undefined || options.day !== undefined)
|
410 | needDefaults = false;
|
411 | }
|
412 |
|
413 | // 6. If required is "time" or "any", then
|
414 | if (required === 'time' || required === 'any') {
|
415 | // a. For each of the property names "hour", "minute", "second":
|
416 | // i. If the result of calling the [[Get]] internal method of options with the
|
417 | // property name is not undefined, then let needDefaults be false.
|
418 | if (options.hour !== undefined || options.minute !== undefined || options.second !== undefined)
|
419 | needDefaults = false;
|
420 | }
|
421 |
|
422 | // 7. If needDefaults is true and defaults is either "date" or "all", then
|
423 | if (needDefaults && (defaults === 'date' || defaults === 'all'))
|
424 | // a. For each of the property names "year", "month", "day":
|
425 | // i. Call the [[DefineOwnProperty]] internal method of options with the
|
426 | // property name, Property Descriptor {[[Value]]: "numeric", [[Writable]]:
|
427 | // true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
|
428 | options.year = options.month = options.day = 'numeric';
|
429 |
|
430 | // 8. If needDefaults is true and defaults is either "time" or "all", then
|
431 | if (needDefaults && (defaults === 'time' || defaults === 'all'))
|
432 | // a. For each of the property names "hour", "minute", "second":
|
433 | // i. Call the [[DefineOwnProperty]] internal method of options with the
|
434 | // property name, Property Descriptor {[[Value]]: "numeric", [[Writable]]:
|
435 | // true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
|
436 | options.hour = options.minute = options.second = 'numeric';
|
437 |
|
438 | // 9. Return options.
|
439 | return options;
|
440 | }
|
441 |
|
442 | /**
|
443 | * When the BasicFormatMatcher abstract operation is called with two arguments options and
|
444 | * formats, the following steps are taken:
|
445 | */
|
446 | function BasicFormatMatcher (options, formats) {
|
447 | // 1. Let removalPenalty be 120.
|
448 | let removalPenalty = 120;
|
449 |
|
450 | // 2. Let additionPenalty be 20.
|
451 | let additionPenalty = 20;
|
452 |
|
453 | // 3. Let longLessPenalty be 8.
|
454 | let longLessPenalty = 8;
|
455 |
|
456 | // 4. Let longMorePenalty be 6.
|
457 | let longMorePenalty = 6;
|
458 |
|
459 | // 5. Let shortLessPenalty be 6.
|
460 | let shortLessPenalty = 6;
|
461 |
|
462 | // 6. Let shortMorePenalty be 3.
|
463 | let shortMorePenalty = 3;
|
464 |
|
465 | // 7. Let bestScore be -Infinity.
|
466 | let bestScore = -Infinity;
|
467 |
|
468 | // 8. Let bestFormat be undefined.
|
469 | let bestFormat;
|
470 |
|
471 | // 9. Let i be 0.
|
472 | let i = 0;
|
473 |
|
474 | // 10. Assert: formats is an Array object.
|
475 |
|
476 | // 11. Let len be the result of calling the [[Get]] internal method of formats with argument "length".
|
477 | let len = formats.length;
|
478 |
|
479 | // 12. Repeat while i < len:
|
480 | while (i < len) {
|
481 | // a. Let format be the result of calling the [[Get]] internal method of formats with argument ToString(i).
|
482 | let format = formats[i];
|
483 |
|
484 | // b. Let score be 0.
|
485 | let score = 0;
|
486 |
|
487 | // c. For each property shown in Table 3:
|
488 | for (let property in dateTimeComponents) {
|
489 | if (!hop.call(dateTimeComponents, property))
|
490 | continue;
|
491 |
|
492 | // i. Let optionsProp be options.[[<property>]].
|
493 | let optionsProp = options['[['+ property +']]'];
|
494 |
|
495 | // ii. Let formatPropDesc be the result of calling the [[GetOwnProperty]] internal method of format
|
496 | // with argument property.
|
497 | // iii. If formatPropDesc is not undefined, then
|
498 | // 1. Let formatProp be the result of calling the [[Get]] internal method of format with argument property.
|
499 | let formatProp = hop.call(format, property) ? format[property] : undefined;
|
500 |
|
501 | // iv. If optionsProp is undefined and formatProp is not undefined, then decrease score by
|
502 | // additionPenalty.
|
503 | if (optionsProp === undefined && formatProp !== undefined)
|
504 | score -= additionPenalty;
|
505 |
|
506 | // v. Else if optionsProp is not undefined and formatProp is undefined, then decrease score by
|
507 | // removalPenalty.
|
508 | else if (optionsProp !== undefined && formatProp === undefined)
|
509 | score -= removalPenalty;
|
510 |
|
511 | // vi. Else
|
512 | else {
|
513 | // 1. Let values be the array ["2-digit", "numeric", "narrow", "short",
|
514 | // "long"].
|
515 | let values = [ '2-digit', 'numeric', 'narrow', 'short', 'long' ];
|
516 |
|
517 | // 2. Let optionsPropIndex be the index of optionsProp within values.
|
518 | let optionsPropIndex = arrIndexOf.call(values, optionsProp);
|
519 |
|
520 | // 3. Let formatPropIndex be the index of formatProp within values.
|
521 | let formatPropIndex = arrIndexOf.call(values, formatProp);
|
522 |
|
523 | // 4. Let delta be max(min(formatPropIndex - optionsPropIndex, 2), -2).
|
524 | let delta = Math.max(Math.min(formatPropIndex - optionsPropIndex, 2), -2);
|
525 |
|
526 | // 5. If delta = 2, decrease score by longMorePenalty.
|
527 | if (delta === 2)
|
528 | score -= longMorePenalty;
|
529 |
|
530 | // 6. Else if delta = 1, decrease score by shortMorePenalty.
|
531 | else if (delta === 1)
|
532 | score -= shortMorePenalty;
|
533 |
|
534 | // 7. Else if delta = -1, decrease score by shortLessPenalty.
|
535 | else if (delta === -1)
|
536 | score -= shortLessPenalty;
|
537 |
|
538 | // 8. Else if delta = -2, decrease score by longLessPenalty.
|
539 | else if (delta === -2)
|
540 | score -= longLessPenalty;
|
541 | }
|
542 | }
|
543 |
|
544 | // d. If score > bestScore, then
|
545 | if (score > bestScore) {
|
546 | // i. Let bestScore be score.
|
547 | bestScore = score;
|
548 |
|
549 | // ii. Let bestFormat be format.
|
550 | bestFormat = format;
|
551 | }
|
552 |
|
553 | // e. Increase i by 1.
|
554 | i++;
|
555 | }
|
556 |
|
557 | // 13. Return bestFormat.
|
558 | return bestFormat;
|
559 | }
|
560 |
|
561 | /**
|
562 | * When the BestFitFormatMatcher abstract operation is called with two arguments options
|
563 | * and formats, it performs implementation dependent steps, which should return a set of
|
564 | * component representations that a typical user of the selected locale would perceive as
|
565 | * at least as good as the one returned by BasicFormatMatcher.
|
566 | *
|
567 | * This polyfill defines the algorithm to be the same as BasicFormatMatcher,
|
568 | * with the addition of bonus points awarded where the requested format is of
|
569 | * the same data type as the potentially matching format.
|
570 | *
|
571 | * This algo relies on the concept of closest distance matching described here:
|
572 | * http://unicode.org/reports/tr35/tr35-dates.html#Matching_Skeletons
|
573 | * Typically a “best match” is found using a closest distance match, such as:
|
574 | *
|
575 | * Symbols requesting a best choice for the locale are replaced.
|
576 | * j → one of {H, k, h, K}; C → one of {a, b, B}
|
577 | * -> Covered by cldr.js matching process
|
578 | *
|
579 | * For fields with symbols representing the same type (year, month, day, etc):
|
580 | * Most symbols have a small distance from each other.
|
581 | * M ≅ L; E ≅ c; a ≅ b ≅ B; H ≅ k ≅ h ≅ K; ...
|
582 | * -> Covered by cldr.js matching process
|
583 | *
|
584 | * Width differences among fields, other than those marking text vs numeric, are given small distance from each other.
|
585 | * MMM ≅ MMMM
|
586 | * MM ≅ M
|
587 | * Numeric and text fields are given a larger distance from each other.
|
588 | * MMM ≈ MM
|
589 | * Symbols representing substantial differences (week of year vs week of month) are given much larger a distances from each other.
|
590 | * d ≋ D; ...
|
591 | * Missing or extra fields cause a match to fail. (But see Missing Skeleton Fields).
|
592 | *
|
593 | *
|
594 | * For example,
|
595 | *
|
596 | * { month: 'numeric', day: 'numeric' }
|
597 | *
|
598 | * should match
|
599 | *
|
600 | * { month: '2-digit', day: '2-digit' }
|
601 | *
|
602 | * rather than
|
603 | *
|
604 | * { month: 'short', day: 'numeric' }
|
605 | *
|
606 | * This makes sense because a user requesting a formatted date with numeric parts would
|
607 | * not expect to see the returned format containing narrow, short or long part names
|
608 | */
|
609 | function BestFitFormatMatcher (options, formats) {
|
610 |
|
611 | // 1. Let removalPenalty be 120.
|
612 | let removalPenalty = 120;
|
613 |
|
614 | // 2. Let additionPenalty be 20.
|
615 | let additionPenalty = 20;
|
616 |
|
617 | // 3. Let longLessPenalty be 8.
|
618 | let longLessPenalty = 8;
|
619 |
|
620 | // 4. Let longMorePenalty be 6.
|
621 | let longMorePenalty = 6;
|
622 |
|
623 | // 5. Let shortLessPenalty be 6.
|
624 | let shortLessPenalty = 6;
|
625 |
|
626 | // 6. Let shortMorePenalty be 3.
|
627 | let shortMorePenalty = 3;
|
628 |
|
629 | let hour12Penalty = 1;
|
630 |
|
631 | // 7. Let bestScore be -Infinity.
|
632 | let bestScore = -Infinity;
|
633 |
|
634 | // 8. Let bestFormat be undefined.
|
635 | let bestFormat;
|
636 |
|
637 | // 9. Let i be 0.
|
638 | let i = 0;
|
639 |
|
640 | // 10. Assert: formats is an Array object.
|
641 |
|
642 | // 11. Let len be the result of calling the [[Get]] internal method of formats with argument "length".
|
643 | let len = formats.length;
|
644 |
|
645 | // 12. Repeat while i < len:
|
646 | while (i < len) {
|
647 | // a. Let format be the result of calling the [[Get]] internal method of formats with argument ToString(i).
|
648 | let format = formats[i];
|
649 |
|
650 | // b. Let score be 0.
|
651 | let score = 0;
|
652 |
|
653 | // c. For each property shown in Table 3:
|
654 | for (let property in dateTimeComponents) {
|
655 | if (!hop.call(dateTimeComponents, property))
|
656 | continue;
|
657 |
|
658 | // i. Let optionsProp be options.[[<property>]].
|
659 | let optionsProp = options['[['+ property +']]'];
|
660 |
|
661 | // ii. Let formatPropDesc be the result of calling the [[GetOwnProperty]] internal method of format
|
662 | // with argument property.
|
663 | // iii. If formatPropDesc is not undefined, then
|
664 | // 1. Let formatProp be the result of calling the [[Get]] internal method of format with argument property.
|
665 | let formatProp = hop.call(format, property) ? format[property] : undefined;
|
666 |
|
667 | // iv. If optionsProp is undefined and formatProp is not undefined, then decrease score by
|
668 | // additionPenalty.
|
669 | if (optionsProp === undefined && formatProp !== undefined)
|
670 | score -= additionPenalty;
|
671 |
|
672 | // v. Else if optionsProp is not undefined and formatProp is undefined, then decrease score by
|
673 | // removalPenalty.
|
674 | else if (optionsProp !== undefined && formatProp === undefined)
|
675 | score -= removalPenalty;
|
676 |
|
677 | // vi. Else
|
678 | else {
|
679 | // 1. Let values be the array ["2-digit", "numeric", "narrow", "short",
|
680 | // "long"].
|
681 | let values = [ '2-digit', 'numeric', 'narrow', 'short', 'long' ];
|
682 |
|
683 | // 2. Let optionsPropIndex be the index of optionsProp within values.
|
684 | let optionsPropIndex = arrIndexOf.call(values, optionsProp);
|
685 |
|
686 | // 3. Let formatPropIndex be the index of formatProp within values.
|
687 | let formatPropIndex = arrIndexOf.call(values, formatProp);
|
688 |
|
689 | // 4. Let delta be max(min(formatPropIndex - optionsPropIndex, 2), -2).
|
690 | let delta = Math.max(Math.min(formatPropIndex - optionsPropIndex, 2), -2);
|
691 |
|
692 | {
|
693 | // diverging from spec
|
694 | // When the bestFit argument is true, subtract additional penalty where data types are not the same
|
695 | if ((formatPropIndex <= 1 && optionsPropIndex >= 2) || (formatPropIndex >= 2 && optionsPropIndex <= 1)) {
|
696 | // 5. If delta = 2, decrease score by longMorePenalty.
|
697 | if (delta > 0)
|
698 | score -= longMorePenalty;
|
699 | else if (delta < 0)
|
700 | score -= longLessPenalty;
|
701 | } else {
|
702 | // 5. If delta = 2, decrease score by longMorePenalty.
|
703 | if (delta > 1)
|
704 | score -= shortMorePenalty;
|
705 | else if (delta < -1)
|
706 | score -= shortLessPenalty;
|
707 | }
|
708 | }
|
709 | }
|
710 | }
|
711 |
|
712 | {
|
713 | // diverging to also take into consideration differences between 12 or 24 hours
|
714 | // which is special for the best fit only.
|
715 | if (format._.hour12 !== options.hour12) {
|
716 | score -= hour12Penalty;
|
717 | }
|
718 | }
|
719 |
|
720 | // d. If score > bestScore, then
|
721 | if (score > bestScore) {
|
722 | // i. Let bestScore be score.
|
723 | bestScore = score;
|
724 | // ii. Let bestFormat be format.
|
725 | bestFormat = format;
|
726 | }
|
727 |
|
728 | // e. Increase i by 1.
|
729 | i++;
|
730 | }
|
731 |
|
732 | // 13. Return bestFormat.
|
733 | return bestFormat;
|
734 | }
|
735 |
|
736 | /* 12.2.3 */internals.DateTimeFormat = {
|
737 | '[[availableLocales]]': [],
|
738 | '[[relevantExtensionKeys]]': ['ca', 'nu'],
|
739 | '[[localeData]]': {},
|
740 | };
|
741 |
|
742 | /**
|
743 | * When the supportedLocalesOf method of Intl.DateTimeFormat is called, the
|
744 | * following steps are taken:
|
745 | */
|
746 | /* 12.2.2 */
|
747 | defineProperty(Intl.DateTimeFormat, 'supportedLocalesOf', {
|
748 | configurable: true,
|
749 | writable: true,
|
750 | value: fnBind.call(function (locales) {
|
751 | // Bound functions only have the `this` value altered if being used as a constructor,
|
752 | // this lets us imitate a native function that has no constructor
|
753 | if (!hop.call(this, '[[availableLocales]]'))
|
754 | throw new TypeError('supportedLocalesOf() is not a constructor');
|
755 |
|
756 | // Create an object whose props can be used to restore the values of RegExp props
|
757 | let regexpState = createRegExpRestore(),
|
758 |
|
759 | // 1. If options is not provided, then let options be undefined.
|
760 | options = arguments[1],
|
761 |
|
762 | // 2. Let availableLocales be the value of the [[availableLocales]] internal
|
763 | // property of the standard built-in object that is the initial value of
|
764 | // Intl.NumberFormat.
|
765 |
|
766 | availableLocales = this['[[availableLocales]]'],
|
767 |
|
768 | // 3. Let requestedLocales be the result of calling the CanonicalizeLocaleList
|
769 | // abstract operation (defined in 9.2.1) with argument locales.
|
770 | requestedLocales = CanonicalizeLocaleList(locales);
|
771 |
|
772 | // Restore the RegExp properties
|
773 | regexpState.exp.test(regexpState.input);
|
774 |
|
775 | // 4. Return the result of calling the SupportedLocales abstract operation
|
776 | // (defined in 9.2.8) with arguments availableLocales, requestedLocales,
|
777 | // and options.
|
778 | return SupportedLocales(availableLocales, requestedLocales, options);
|
779 | }, internals.NumberFormat),
|
780 | });
|
781 |
|
782 | /**
|
783 | * This named accessor property returns a function that formats a number
|
784 | * according to the effective locale and the formatting options of this
|
785 | * DateTimeFormat object.
|
786 | */
|
787 | /* 12.3.2 */defineProperty(Intl.DateTimeFormat.prototype, 'format', {
|
788 | configurable: true,
|
789 | get: GetFormatDateTime,
|
790 | });
|
791 |
|
792 | defineProperty(Intl.DateTimeFormat.prototype, 'formatToParts', {
|
793 | configurable: true,
|
794 | get: GetFormatToPartsDateTime,
|
795 | });
|
796 |
|
797 | function GetFormatDateTime() {
|
798 | let internal = this !== null && typeof this === 'object' && getInternalProperties(this);
|
799 |
|
800 | // Satisfy test 12.3_b
|
801 | if (!internal || !internal['[[initializedDateTimeFormat]]'])
|
802 | throw new TypeError('`this` value for format() is not an initialized Intl.DateTimeFormat object.');
|
803 |
|
804 | // The value of the [[Get]] attribute is a function that takes the following
|
805 | // steps:
|
806 |
|
807 | // 1. If the [[boundFormat]] internal property of this DateTimeFormat object
|
808 | // is undefined, then:
|
809 | if (internal['[[boundFormat]]'] === undefined) {
|
810 | // a. Let F be a Function object, with internal properties set as
|
811 | // specified for built-in functions in ES5, 15, or successor, and the
|
812 | // length property set to 0, that takes the argument date and
|
813 | // performs the following steps:
|
814 | let F = function () {
|
815 | // i. If date is not provided or is undefined, then let x be the
|
816 | // result as if by the expression Date.now() where Date.now is
|
817 | // the standard built-in function defined in ES5, 15.9.4.4.
|
818 | // ii. Else let x be ToNumber(date).
|
819 | // iii. Return the result of calling the FormatDateTime abstract
|
820 | // operation (defined below) with arguments this and x.
|
821 | let x = Number(arguments.length === 0 ? Date.now() : arguments[0]);
|
822 | return FormatDateTime(this, x);
|
823 | };
|
824 | // b. Let bind be the standard built-in function object defined in ES5,
|
825 | // 15.3.4.5.
|
826 | // c. Let bf be the result of calling the [[Call]] internal method of
|
827 | // bind with F as the this value and an argument list containing
|
828 | // the single item this.
|
829 | let bf = fnBind.call(F, this);
|
830 | // d. Set the [[boundFormat]] internal property of this NumberFormat
|
831 | // object to bf.
|
832 | internal['[[boundFormat]]'] = bf;
|
833 | }
|
834 | // Return the value of the [[boundFormat]] internal property of this
|
835 | // NumberFormat object.
|
836 | return internal['[[boundFormat]]'];
|
837 | }
|
838 |
|
839 | function GetFormatToPartsDateTime() {
|
840 | let internal = this !== null && typeof this === 'object' && getInternalProperties(this);
|
841 |
|
842 | if (!internal || !internal['[[initializedDateTimeFormat]]'])
|
843 | throw new TypeError('`this` value for formatToParts() is not an initialized Intl.DateTimeFormat object.');
|
844 |
|
845 | if (internal['[[boundFormatToParts]]'] === undefined) {
|
846 | let F = function () {
|
847 | let x = Number(arguments.length === 0 ? Date.now() : arguments[0]);
|
848 | return FormatToPartsDateTime(this, x);
|
849 | };
|
850 | let bf = fnBind.call(F, this);
|
851 | internal['[[boundFormatToParts]]'] = bf;
|
852 | }
|
853 | return internal['[[boundFormatToParts]]'];
|
854 | }
|
855 |
|
856 | function CreateDateTimeParts(dateTimeFormat, x) {
|
857 | // 1. If x is not a finite Number, then throw a RangeError exception.
|
858 | if (!isFinite(x))
|
859 | throw new RangeError('Invalid valid date passed to format');
|
860 |
|
861 | let internal = dateTimeFormat.__getInternalProperties(secret);
|
862 |
|
863 | // Creating restore point for properties on the RegExp object... please wait
|
864 | /* let regexpState = */createRegExpRestore(); // ###TODO: review this
|
865 |
|
866 | // 2. Let locale be the value of the [[locale]] internal property of dateTimeFormat.
|
867 | let locale = internal['[[locale]]'];
|
868 |
|
869 | // 3. Let nf be the result of creating a new NumberFormat object as if by the
|
870 | // expression new Intl.NumberFormat([locale], {useGrouping: false}) where
|
871 | // Intl.NumberFormat is the standard built-in constructor defined in 11.1.3.
|
872 | let nf = new Intl.NumberFormat([locale], {useGrouping: false});
|
873 |
|
874 | // 4. Let nf2 be the result of creating a new NumberFormat object as if by the
|
875 | // expression new Intl.NumberFormat([locale], {minimumIntegerDigits: 2, useGrouping:
|
876 | // false}) where Intl.NumberFormat is the standard built-in constructor defined in
|
877 | // 11.1.3.
|
878 | let nf2 = new Intl.NumberFormat([locale], {minimumIntegerDigits: 2, useGrouping: false});
|
879 |
|
880 | // 5. Let tm be the result of calling the ToLocalTime abstract operation (defined
|
881 | // below) with x, the value of the [[calendar]] internal property of dateTimeFormat,
|
882 | // and the value of the [[timeZone]] internal property of dateTimeFormat.
|
883 | let tm = ToLocalTime(x, internal['[[calendar]]'], internal['[[timeZone]]']);
|
884 |
|
885 | // 6. Let result be the value of the [[pattern]] internal property of dateTimeFormat.
|
886 | let pattern = internal['[[pattern]]'];
|
887 |
|
888 | // 7.
|
889 | let result = new List();
|
890 |
|
891 | // 8.
|
892 | let index = 0;
|
893 |
|
894 | // 9.
|
895 | let beginIndex = pattern.indexOf('{');
|
896 |
|
897 | // 10.
|
898 | let endIndex = 0;
|
899 |
|
900 | // Need the locale minus any extensions
|
901 | let dataLocale = internal['[[dataLocale]]'];
|
902 |
|
903 | // Need the calendar data from CLDR
|
904 | let localeData = internals.DateTimeFormat['[[localeData]]'][dataLocale].calendars;
|
905 | let ca = internal['[[calendar]]'];
|
906 |
|
907 | // 11.
|
908 | while (beginIndex !== -1) {
|
909 | let fv;
|
910 | // a.
|
911 | endIndex = pattern.indexOf('}', beginIndex);
|
912 | // b.
|
913 | if (endIndex === -1) {
|
914 | throw new Error('Unclosed pattern');
|
915 | }
|
916 | // c.
|
917 | if (beginIndex > index) {
|
918 | arrPush.call(result, {
|
919 | type: 'literal',
|
920 | value: pattern.substring(index, beginIndex),
|
921 | });
|
922 | }
|
923 | // d.
|
924 | let p = pattern.substring(beginIndex + 1, endIndex);
|
925 | // e.
|
926 | if (dateTimeComponents.hasOwnProperty(p)) {
|
927 | // i. Let f be the value of the [[<p>]] internal property of dateTimeFormat.
|
928 | let f = internal['[['+ p +']]'];
|
929 | // ii. Let v be the value of tm.[[<p>]].
|
930 | let v = tm['[['+ p +']]'];
|
931 | // iii. If p is "year" and v ≤ 0, then let v be 1 - v.
|
932 | if (p === 'year' && v <= 0) {
|
933 | v = 1 - v;
|
934 | }
|
935 | // iv. If p is "month", then increase v by 1.
|
936 | else if (p === 'month') {
|
937 | v++;
|
938 | }
|
939 | // v. If p is "hour" and the value of the [[hour12]] internal property of
|
940 | // dateTimeFormat is true, then
|
941 | else if (p === 'hour' && internal['[[hour12]]'] === true) {
|
942 | // 1. Let v be v modulo 12.
|
943 | v = v % 12;
|
944 | // 2. If v is 0 and the value of the [[hourNo0]] internal property of
|
945 | // dateTimeFormat is true, then let v be 12.
|
946 | if (v === 0 && internal['[[hourNo0]]'] === true) {
|
947 | v = 12;
|
948 | }
|
949 | }
|
950 |
|
951 | // vi. If f is "numeric", then
|
952 | if (f === 'numeric') {
|
953 | // 1. Let fv be the result of calling the FormatNumber abstract operation
|
954 | // (defined in 11.3.2) with arguments nf and v.
|
955 | fv = FormatNumber(nf, v);
|
956 | }
|
957 | // vii. Else if f is "2-digit", then
|
958 | else if (f === '2-digit') {
|
959 | // 1. Let fv be the result of calling the FormatNumber abstract operation
|
960 | // with arguments nf2 and v.
|
961 | fv = FormatNumber(nf2, v);
|
962 | // 2. If the length of fv is greater than 2, let fv be the substring of fv
|
963 | // containing the last two characters.
|
964 | if (fv.length > 2) {
|
965 | fv = fv.slice(-2);
|
966 | }
|
967 | }
|
968 | // viii. Else if f is "narrow", "short", or "long", then let fv be a String
|
969 | // value representing f in the desired form; the String value depends upon
|
970 | // the implementation and the effective locale and calendar of
|
971 | // dateTimeFormat. If p is "month", then the String value may also depend
|
972 | // on whether dateTimeFormat has a [[day]] internal property. If p is
|
973 | // "timeZoneName", then the String value may also depend on the value of
|
974 | // the [[inDST]] field of tm.
|
975 | else if (f in dateWidths) {
|
976 | switch (p) {
|
977 | case 'month':
|
978 | fv = resolveDateString(localeData, ca, 'months', f, tm['[['+ p +']]']);
|
979 | break;
|
980 |
|
981 | case 'weekday':
|
982 | try {
|
983 | fv = resolveDateString(localeData, ca, 'days', f, tm['[['+ p +']]']);
|
984 | // fv = resolveDateString(ca.days, f)[tm['[['+ p +']]']];
|
985 | } catch (e) {
|
986 | throw new Error('Could not find weekday data for locale '+locale);
|
987 | }
|
988 | break;
|
989 |
|
990 | case 'timeZoneName':
|
991 | fv = ''; // ###TODO
|
992 | break;
|
993 |
|
994 | case 'era':
|
995 | try {
|
996 | fv = resolveDateString(localeData, ca, 'eras', f, tm['[['+ p +']]']);
|
997 | } catch (e) {
|
998 | throw new Error('Could not find era data for locale '+locale);
|
999 | }
|
1000 | break;
|
1001 |
|
1002 | default:
|
1003 | fv = tm['[['+ p +']]'];
|
1004 | }
|
1005 | }
|
1006 | // ix
|
1007 | arrPush.call(result, {
|
1008 | type: p,
|
1009 | value: fv,
|
1010 | });
|
1011 | // f.
|
1012 | } else if (p === 'ampm') {
|
1013 | // i.
|
1014 | let v = tm['[[hour]]'];
|
1015 | // ii./iii.
|
1016 | fv = resolveDateString(localeData, ca, 'dayPeriods', v > 11 ? 'pm' : 'am', null);
|
1017 | // iv.
|
1018 | arrPush.call(result, {
|
1019 | type: 'dayPeriod',
|
1020 | value: fv,
|
1021 | });
|
1022 | // g.
|
1023 | } else {
|
1024 | arrPush.call(result, {
|
1025 | type: 'literal',
|
1026 | value: pattern.substring(beginIndex, endIndex + 1),
|
1027 | });
|
1028 | }
|
1029 | // h.
|
1030 | index = endIndex + 1;
|
1031 | // i.
|
1032 | beginIndex = pattern.indexOf('{', index);
|
1033 | }
|
1034 | // 12.
|
1035 | if (endIndex < pattern.length - 1) {
|
1036 | arrPush.call(result, {
|
1037 | type: 'literal',
|
1038 | value: pattern.substr(endIndex + 1),
|
1039 | });
|
1040 | }
|
1041 | // 13.
|
1042 | return result;
|
1043 | }
|
1044 |
|
1045 | /**
|
1046 | * When the FormatDateTime abstract operation is called with arguments dateTimeFormat
|
1047 | * (which must be an object initialized as a DateTimeFormat) and x (which must be a Number
|
1048 | * value), it returns a String value representing x (interpreted as a time value as
|
1049 | * specified in ES5, 15.9.1.1) according to the effective locale and the formatting
|
1050 | * options of dateTimeFormat.
|
1051 | */
|
1052 | export function FormatDateTime(dateTimeFormat, x) {
|
1053 | let parts = CreateDateTimeParts(dateTimeFormat, x);
|
1054 | let result = '';
|
1055 |
|
1056 | for (let i = 0; parts.length > i; i++) {
|
1057 | let part = parts[i];
|
1058 | result += part.value;
|
1059 | }
|
1060 | return result;
|
1061 | }
|
1062 |
|
1063 | function FormatToPartsDateTime(dateTimeFormat, x) {
|
1064 | let parts = CreateDateTimeParts(dateTimeFormat, x);
|
1065 | let result = [];
|
1066 | for (let i = 0; parts.length > i; i++) {
|
1067 | let part = parts[i];
|
1068 | result.push({
|
1069 | type: part.type,
|
1070 | value: part.value,
|
1071 | });
|
1072 | }
|
1073 | return result;
|
1074 | }
|
1075 |
|
1076 |
|
1077 | /**
|
1078 | * When the ToLocalTime abstract operation is called with arguments date, calendar, and
|
1079 | * timeZone, the following steps are taken:
|
1080 | */
|
1081 | function ToLocalTime(date, calendar, timeZone) {
|
1082 | // 1. Apply calendrical calculations on date for the given calendar and time zone to
|
1083 | // produce weekday, era, year, month, day, hour, minute, second, and inDST values.
|
1084 | // The calculations should use best available information about the specified
|
1085 | // calendar and time zone. If the calendar is "gregory", then the calculations must
|
1086 | // match the algorithms specified in ES5, 15.9.1, except that calculations are not
|
1087 | // bound by the restrictions on the use of best available information on time zones
|
1088 | // for local time zone adjustment and daylight saving time adjustment imposed by
|
1089 | // ES5, 15.9.1.7 and 15.9.1.8.
|
1090 | // ###TODO###
|
1091 | let d = new Date(date),
|
1092 | m = 'get' + (timeZone || '');
|
1093 |
|
1094 | // 2. Return a Record with fields [[weekday]], [[era]], [[year]], [[month]], [[day]],
|
1095 | // [[hour]], [[minute]], [[second]], and [[inDST]], each with the corresponding
|
1096 | // calculated value.
|
1097 | return new Record({
|
1098 | '[[weekday]]': d[m + 'Day'](),
|
1099 | '[[era]]' : +(d[m + 'FullYear']() >= 0),
|
1100 | '[[year]]' : d[m + 'FullYear'](),
|
1101 | '[[month]]' : d[m + 'Month'](),
|
1102 | '[[day]]' : d[m + 'Date'](),
|
1103 | '[[hour]]' : d[m + 'Hours'](),
|
1104 | '[[minute]]' : d[m + 'Minutes'](),
|
1105 | '[[second]]' : d[m + 'Seconds'](),
|
1106 | '[[inDST]]' : false, // ###TODO###
|
1107 | });
|
1108 | }
|
1109 |
|
1110 | /**
|
1111 | * The function returns a new object whose properties and attributes are set as if
|
1112 | * constructed by an object literal assigning to each of the following properties the
|
1113 | * value of the corresponding internal property of this DateTimeFormat object (see 12.4):
|
1114 | * locale, calendar, numberingSystem, timeZone, hour12, weekday, era, year, month, day,
|
1115 | * hour, minute, second, and timeZoneName. Properties whose corresponding internal
|
1116 | * properties are not present are not assigned.
|
1117 | */
|
1118 | /* 12.3.3 */defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {
|
1119 | writable: true,
|
1120 | configurable: true,
|
1121 | value: function () {
|
1122 | let prop,
|
1123 | descs = new Record(),
|
1124 | props = [
|
1125 | 'locale', 'calendar', 'numberingSystem', 'timeZone', 'hour12', 'weekday',
|
1126 | 'era', 'year', 'month', 'day', 'hour', 'minute', 'second', 'timeZoneName',
|
1127 | ],
|
1128 | internal = this !== null && typeof this === 'object' && getInternalProperties(this);
|
1129 |
|
1130 | // Satisfy test 12.3_b
|
1131 | if (!internal || !internal['[[initializedDateTimeFormat]]'])
|
1132 | throw new TypeError('`this` value for resolvedOptions() is not an initialized Intl.DateTimeFormat object.');
|
1133 |
|
1134 | for (let i = 0, max = props.length; i < max; i++) {
|
1135 | if (hop.call(internal, prop = '[[' + props[i] + ']]'))
|
1136 | descs[props[i]] = { value: internal[prop], writable: true, configurable: true, enumerable: true };
|
1137 | }
|
1138 |
|
1139 | return objCreate({}, descs);
|
1140 | },
|
1141 | });
|