DateTimeFormat.js (23617B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* Portions Copyright Norbert Lindenberg 2011-2012. */ 6 7 /** 8 * 11.1.2 CreateDateTimeFormat ( newTarget, locales, options, required, defaults [ , toLocaleStringTimeZone ] ) 9 * 10 * Compute an internal properties object from |lazyDateTimeFormatData|. 11 */ 12 function resolveDateTimeFormatInternals(lazyDateTimeFormatData) { 13 assert(IsObject(lazyDateTimeFormatData), "lazy data not an object?"); 14 15 // Lazy DateTimeFormat data has the following structure: 16 // 17 // { 18 // requestedLocales: List of locales, 19 // 20 // localeOpt: // *first* opt computed in InitializeDateTimeFormat 21 // { 22 // localeMatcher: "lookup" / "best fit", 23 // 24 // ca: string matching a Unicode extension type, // optional 25 // 26 // nu: string matching a Unicode extension type, // optional 27 // 28 // hc: "h11" / "h12" / "h23" / "h24", // optional 29 // } 30 // 31 // timeZone: IANA time zone name or a normalized time zone offset string, 32 // 33 // formatOptions: // *second* opt computed in InitializeDateTimeFormat 34 // { 35 // // all the properties/values listed in Table 3 36 // // (weekday, era, year, month, day, &c.) 37 // 38 // hour12: true / false, // optional 39 // } 40 // 41 // formatMatcher: "basic" / "best fit", 42 // 43 // dateStyle: "full" / "long" / "medium" / "short" / undefined, 44 // 45 // timeStyle: "full" / "long" / "medium" / "short" / undefined, 46 // 47 // patternOption: 48 // String representing LDML Date Format pattern or undefined 49 // } 50 // 51 // Note that lazy data is only installed as a final step of initialization, 52 // so every DateTimeFormat lazy data object has *all* these properties, 53 // never a subset of them. 54 55 var internalProps = std_Object_create(null); 56 57 var DateTimeFormat = dateTimeFormatInternalProperties; 58 59 // Compute effective locale. 60 61 // Step 17. 62 var localeData = DateTimeFormat.localeData; 63 64 // Step 18. 65 var r = ResolveLocale( 66 "DateTimeFormat", 67 lazyDateTimeFormatData.requestedLocales, 68 lazyDateTimeFormatData.localeOpt, 69 DateTimeFormat.relevantExtensionKeys, 70 localeData 71 ); 72 73 // Changes from "Intl era and monthCode" proposal. 74 // 75 // https://tc39.es/proposal-intl-era-monthcode/#sec-createdatetimeformat 76 if (r.ca === "islamic") { 77 ReportWarning(JSMSG_ISLAMIC_FALLBACK); 78 79 // Fallback to "islamic-tbla" calendar. 80 r.ca = "islamic-tbla"; 81 } else if (r.ca === "islamic-rgsa") { 82 // Fallback to "islamic-tbla" calendar for 147 uplift compatibility. 83 // The above warning text isn't suitable, and per 2025-12 TG2 meeting 84 // treatment as unknown is expected going forward (bug 2005702). 85 r.ca = "islamic-tbla"; 86 } 87 88 // Steps 19-22. 89 internalProps.locale = r.locale; 90 internalProps.calendar = r.ca; 91 internalProps.numberingSystem = r.nu; 92 93 // Step 34. (Reordered) 94 var formatOptions = lazyDateTimeFormatData.formatOptions; 95 96 // Steps 23-29. 97 // 98 // Copy the hourCycle setting, if present, to the format options. But 99 // only do this if no hour12 option is present, because the latter takes 100 // precedence over hourCycle. 101 if (r.hc !== null && formatOptions.hour12 === undefined) { 102 formatOptions.hourCycle = r.hc; 103 } 104 105 // Step 33. 106 internalProps.timeZone = lazyDateTimeFormatData.timeZone; 107 108 // Steps 45-50, more or less. 109 if (lazyDateTimeFormatData.patternOption !== undefined) { 110 internalProps.pattern = lazyDateTimeFormatData.patternOption; 111 } else if ( 112 lazyDateTimeFormatData.dateStyle !== undefined || 113 lazyDateTimeFormatData.timeStyle !== undefined 114 ) { 115 internalProps.hourCycle = formatOptions.hourCycle; 116 internalProps.hour12 = formatOptions.hour12; 117 internalProps.dateStyle = lazyDateTimeFormatData.dateStyle; 118 internalProps.timeStyle = lazyDateTimeFormatData.timeStyle; 119 } else { 120 internalProps.required = lazyDateTimeFormatData.required; 121 internalProps.defaults = lazyDateTimeFormatData.defaults; 122 internalProps.hourCycle = formatOptions.hourCycle; 123 internalProps.hour12 = formatOptions.hour12; 124 internalProps.weekday = formatOptions.weekday; 125 internalProps.era = formatOptions.era; 126 internalProps.year = formatOptions.year; 127 internalProps.month = formatOptions.month; 128 internalProps.day = formatOptions.day; 129 internalProps.dayPeriod = formatOptions.dayPeriod; 130 internalProps.hour = formatOptions.hour; 131 internalProps.minute = formatOptions.minute; 132 internalProps.second = formatOptions.second; 133 internalProps.fractionalSecondDigits = formatOptions.fractionalSecondDigits; 134 internalProps.timeZoneName = formatOptions.timeZoneName; 135 } 136 137 // The caller is responsible for associating |internalProps| with the right 138 // object using |setInternalProperties|. 139 return internalProps; 140 } 141 142 /** 143 * Returns an object containing the DateTimeFormat internal properties of |obj|. 144 */ 145 function getDateTimeFormatInternals(obj) { 146 assert(IsObject(obj), "getDateTimeFormatInternals called with non-object"); 147 assert( 148 intl_GuardToDateTimeFormat(obj) !== null, 149 "getDateTimeFormatInternals called with non-DateTimeFormat" 150 ); 151 152 var internals = getIntlObjectInternals(obj); 153 assert( 154 internals.type === "DateTimeFormat", 155 "bad type escaped getIntlObjectInternals" 156 ); 157 158 // If internal properties have already been computed, use them. 159 var internalProps = maybeInternalProperties(internals); 160 if (internalProps) { 161 return internalProps; 162 } 163 164 // Otherwise it's time to fully create them. 165 internalProps = resolveDateTimeFormatInternals(internals.lazyData); 166 setInternalProperties(internals, internalProps); 167 return internalProps; 168 } 169 170 /** 171 * 12.1.10 UnwrapDateTimeFormat( dtf ) 172 */ 173 function UnwrapDateTimeFormat(dtf) { 174 // Steps 2 and 4 (error handling moved to caller). 175 if ( 176 IsObject(dtf) && 177 intl_GuardToDateTimeFormat(dtf) === null && 178 !intl_IsWrappedDateTimeFormat(dtf) && 179 callFunction( 180 std_Object_isPrototypeOf, 181 GetBuiltinPrototype("DateTimeFormat"), 182 dtf 183 ) 184 ) { 185 dtf = dtf[intlFallbackSymbol()]; 186 } 187 return dtf; 188 } 189 190 /* eslint-disable complexity */ 191 /** 192 * 11.1.2 CreateDateTimeFormat ( newTarget, locales, options, required, defaults [ , toLocaleStringTimeZone ] ) 193 * 194 * Initializes an object as a DateTimeFormat. 195 * 196 * This method is complicated a moderate bit by its implementing initialization 197 * as a *lazy* concept. Everything that must happen now, does -- but we defer 198 * all the work we can until the object is actually used as a DateTimeFormat. 199 * This later work occurs in |resolveDateTimeFormatInternals|; steps not noted 200 * here occur there. 201 */ 202 function InitializeDateTimeFormat( 203 dateTimeFormat, 204 thisValue, 205 locales, 206 options, 207 required, 208 defaults, 209 toLocaleStringTimeZone, 210 mozExtensions 211 ) { 212 assert( 213 IsObject(dateTimeFormat), 214 "InitializeDateTimeFormat called with non-Object" 215 ); 216 assert( 217 intl_GuardToDateTimeFormat(dateTimeFormat) !== null, 218 "InitializeDateTimeFormat called with non-DateTimeFormat" 219 ); 220 assert( 221 required === "date" || required === "time" || required === "any", 222 `InitializeDateTimeFormat called with invalid required value: ${required}` 223 ); 224 assert( 225 defaults === "date" || defaults === "time" || defaults === "all", 226 `InitializeDateTimeFormat called with invalid defaults value: ${defaults}` 227 ); 228 assert( 229 toLocaleStringTimeZone === undefined || typeof toLocaleStringTimeZone === "string", 230 `InitializeDateTimeFormat called with invalid toLocaleStringTimeZone value: ${toLocaleStringTimeZone}` 231 ); 232 233 // Lazy DateTimeFormat data has the following structure: 234 // 235 // { 236 // requestedLocales: List of locales, 237 // 238 // localeOpt: // *first* opt computed in InitializeDateTimeFormat 239 // { 240 // localeMatcher: "lookup" / "best fit", 241 // 242 // ca: string matching a Unicode extension type, // optional 243 // 244 // nu: string matching a Unicode extension type, // optional 245 // 246 // hc: "h11" / "h12" / "h23" / "h24", // optional 247 // } 248 // 249 // timeZone: IANA time zone name or a normalized time zone offset string, 250 // 251 // formatOptions: // *second* opt computed in InitializeDateTimeFormat 252 // { 253 // // all the properties/values listed in Table 3 254 // // (weekday, era, year, month, day, &c.) 255 // 256 // hour12: true / false, // optional 257 // } 258 // 259 // formatMatcher: "basic" / "best fit", 260 // 261 // required: "date" / "time" / "any", // optional 262 // 263 // defaults: "date" / "time" / "all", // optional 264 // } 265 // 266 // Note that lazy data is only installed as a final step of initialization, 267 // so every DateTimeFormat lazy data object has *all* these properties, 268 // never a subset of them. 269 var lazyDateTimeFormatData = std_Object_create(null); 270 271 // Step 1. (Performed in caller) 272 273 // Step 2. 274 var requestedLocales = CanonicalizeLocaleList(locales); 275 lazyDateTimeFormatData.requestedLocales = requestedLocales; 276 277 // Step 3. (Inlined call to CoerceOptionsToObject.) 278 if (options === undefined) { 279 options = std_Object_create(null); 280 } else { 281 options = ToObject(options); 282 } 283 284 // Compute options that impact interpretation of locale. 285 // Step 4. 286 var localeOpt = NEW_RECORD(); 287 lazyDateTimeFormatData.localeOpt = localeOpt; 288 289 // Steps 5-6. 290 var localeMatcher = GetOption( 291 options, 292 "localeMatcher", 293 "string", 294 ["lookup", "best fit"], 295 "best fit" 296 ); 297 localeOpt.localeMatcher = localeMatcher; 298 299 // Step 7. 300 var calendar = GetOption(options, "calendar", "string", undefined, undefined); 301 302 // Step 8. 303 if (calendar !== undefined) { 304 calendar = intl_ValidateAndCanonicalizeUnicodeExtensionType( 305 calendar, 306 "calendar", 307 "ca" 308 ); 309 } 310 311 // Step 9. 312 localeOpt.ca = calendar; 313 314 // Step 10. 315 var numberingSystem = GetOption( 316 options, 317 "numberingSystem", 318 "string", 319 undefined, 320 undefined 321 ); 322 323 // Step 11. 324 if (numberingSystem !== undefined) { 325 numberingSystem = intl_ValidateAndCanonicalizeUnicodeExtensionType( 326 numberingSystem, 327 "numberingSystem", 328 "nu" 329 ); 330 } 331 332 // Step 12. 333 localeOpt.nu = numberingSystem; 334 335 // Step 13. 336 var hour12 = GetOption(options, "hour12", "boolean", undefined, undefined); 337 338 // Step 14. 339 var hourCycle = GetOption( 340 options, 341 "hourCycle", 342 "string", 343 ["h11", "h12", "h23", "h24"], 344 undefined 345 ); 346 347 // Step 15. 348 if (hour12 !== undefined) { 349 // The "hourCycle" option is ignored if "hr12" is also present. 350 hourCycle = null; 351 } 352 353 // Step 16. 354 localeOpt.hc = hourCycle; 355 356 // Steps 17-29 (see resolveDateTimeFormatInternals). 357 358 // Step 29. 359 var timeZone = options.timeZone; 360 361 // Steps 30-34. 362 if (timeZone === undefined) { 363 // Step 30.a. 364 if (toLocaleStringTimeZone !== undefined) { 365 timeZone = toLocaleStringTimeZone; 366 } else { 367 timeZone = intl_DefaultTimeZone(); 368 } 369 370 // Steps 32-34. (Not applicable in our implementation.) 371 } else { 372 // Step 31.a. 373 if (toLocaleStringTimeZone !== undefined) { 374 ThrowTypeError( 375 JSMSG_INVALID_DATETIME_OPTION, 376 "timeZone", 377 "Temporal.ZonedDateTime.toLocaleString" 378 ); 379 } 380 timeZone = ToString(timeZone); 381 382 // Steps 32-34. 383 timeZone = intl_ValidateAndCanonicalizeTimeZone(timeZone); 384 } 385 386 // Step 33. 387 lazyDateTimeFormatData.timeZone = timeZone; 388 389 // Step 34. 390 var formatOptions = NEW_RECORD(); 391 lazyDateTimeFormatData.formatOptions = formatOptions; 392 393 if (mozExtensions) { 394 var pattern = GetOption(options, "pattern", "string", undefined, undefined); 395 lazyDateTimeFormatData.patternOption = pattern; 396 } 397 398 // Step 35. 399 // 400 // Pass hr12 on to ICU. The hour cycle option is passed through |localeOpt|. 401 if (hour12 !== undefined) { 402 formatOptions.hour12 = hour12; 403 } 404 405 // Step 36. (Explicit format component computed in step 43.) 406 407 // Step 37. 408 // 11.5, Table 7: Components of date and time formats. 409 formatOptions.weekday = GetOption( 410 options, 411 "weekday", 412 "string", 413 ["narrow", "short", "long"], 414 undefined 415 ); 416 formatOptions.era = GetOption( 417 options, 418 "era", 419 "string", 420 ["narrow", "short", "long"], 421 undefined 422 ); 423 formatOptions.year = GetOption( 424 options, 425 "year", 426 "string", 427 ["2-digit", "numeric"], 428 undefined 429 ); 430 formatOptions.month = GetOption( 431 options, 432 "month", 433 "string", 434 ["2-digit", "numeric", "narrow", "short", "long"], 435 undefined 436 ); 437 formatOptions.day = GetOption( 438 options, 439 "day", 440 "string", 441 ["2-digit", "numeric"], 442 undefined 443 ); 444 formatOptions.dayPeriod = GetOption( 445 options, 446 "dayPeriod", 447 "string", 448 ["narrow", "short", "long"], 449 undefined 450 ); 451 formatOptions.hour = GetOption( 452 options, 453 "hour", 454 "string", 455 ["2-digit", "numeric"], 456 undefined 457 ); 458 formatOptions.minute = GetOption( 459 options, 460 "minute", 461 "string", 462 ["2-digit", "numeric"], 463 undefined 464 ); 465 formatOptions.second = GetOption( 466 options, 467 "second", 468 "string", 469 ["2-digit", "numeric"], 470 undefined 471 ); 472 formatOptions.fractionalSecondDigits = GetNumberOption( 473 options, 474 "fractionalSecondDigits", 475 1, 476 3, 477 undefined 478 ); 479 formatOptions.timeZoneName = GetOption( 480 options, 481 "timeZoneName", 482 "string", 483 [ 484 "short", 485 "long", 486 "shortOffset", 487 "longOffset", 488 "shortGeneric", 489 "longGeneric", 490 ], 491 undefined 492 ); 493 494 // Step 38. 495 // 496 // For some reason (ICU not exposing enough interface?) we drop the 497 // requested format matcher on the floor after this. In any case, even if 498 // doing so is justified, we have to do this work here in case it triggers 499 // getters or similar. (bug 852837) 500 var formatMatcher = GetOption( 501 options, 502 "formatMatcher", 503 "string", 504 ["basic", "best fit"], 505 "best fit" 506 ); 507 void formatMatcher; 508 509 // Steps 39-40. 510 var dateStyle = GetOption( 511 options, 512 "dateStyle", 513 "string", 514 ["full", "long", "medium", "short"], 515 undefined 516 ); 517 lazyDateTimeFormatData.dateStyle = dateStyle; 518 519 // Steps 41-42. 520 var timeStyle = GetOption( 521 options, 522 "timeStyle", 523 "string", 524 ["full", "long", "medium", "short"], 525 undefined 526 ); 527 lazyDateTimeFormatData.timeStyle = timeStyle; 528 529 // Step 43. 530 if (dateStyle !== undefined || timeStyle !== undefined) { 531 /* eslint-disable no-nested-ternary */ 532 var explicitFormatComponent = 533 formatOptions.weekday !== undefined 534 ? "weekday" 535 : formatOptions.era !== undefined 536 ? "era" 537 : formatOptions.year !== undefined 538 ? "year" 539 : formatOptions.month !== undefined 540 ? "month" 541 : formatOptions.day !== undefined 542 ? "day" 543 : formatOptions.dayPeriod !== undefined 544 ? "dayPeriod" 545 : formatOptions.hour !== undefined 546 ? "hour" 547 : formatOptions.minute !== undefined 548 ? "minute" 549 : formatOptions.second !== undefined 550 ? "second" 551 : formatOptions.fractionalSecondDigits !== undefined 552 ? "fractionalSecondDigits" 553 : formatOptions.timeZoneName !== undefined 554 ? "timeZoneName" 555 : undefined; 556 /* eslint-enable no-nested-ternary */ 557 558 // Step 43.a. 559 if (explicitFormatComponent !== undefined) { 560 ThrowTypeError( 561 JSMSG_INVALID_DATETIME_OPTION, 562 explicitFormatComponent, 563 dateStyle !== undefined ? "dateStyle" : "timeStyle" 564 ); 565 } 566 567 // Step 43.b. 568 if (required === "date" && timeStyle !== undefined) { 569 ThrowTypeError(JSMSG_INVALID_DATETIME_STYLE, "timeStyle", "date"); 570 } 571 572 // Step 43.c. 573 if (required === "time" && dateStyle !== undefined) { 574 ThrowTypeError(JSMSG_INVALID_DATETIME_STYLE, "dateStyle", "time"); 575 } 576 } else { 577 lazyDateTimeFormatData.required = required; 578 lazyDateTimeFormatData.defaults = defaults; 579 580 // Steps 44.f-h provided by ICU, more or less. 581 } 582 583 // Steps 45-50. (see resolveDateTimeFormatInternals). 584 585 // We've done everything that must be done now: mark the lazy data as fully 586 // computed and install it. 587 initializeIntlObject( 588 dateTimeFormat, 589 "DateTimeFormat", 590 lazyDateTimeFormatData 591 ); 592 593 // 11.1.1 Intl.DateTimeFormat, step 3. (Inlined call to ChainDateTimeFormat.) 594 if ( 595 dateTimeFormat !== thisValue && 596 callFunction( 597 std_Object_isPrototypeOf, 598 GetBuiltinPrototype("DateTimeFormat"), 599 thisValue 600 ) 601 ) { 602 DefineDataProperty( 603 thisValue, 604 intlFallbackSymbol(), 605 dateTimeFormat, 606 ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE 607 ); 608 609 return thisValue; 610 } 611 612 // Step 51. 613 return dateTimeFormat; 614 } 615 /* eslint-enable complexity */ 616 617 /** 618 * DateTimeFormat internal properties. 619 * 620 * Spec: ECMAScript Internationalization API Specification, 9.1 and 12.3.3. 621 */ 622 var dateTimeFormatInternalProperties = { 623 localeData: dateTimeFormatLocaleData, 624 relevantExtensionKeys: ["ca", "hc", "nu"], 625 }; 626 627 function dateTimeFormatLocaleData() { 628 return { 629 ca: intl_availableCalendars, 630 nu: getNumberingSystems, 631 hc: () => { 632 return [null, "h11", "h12", "h23", "h24"]; 633 }, 634 default: { 635 ca: intl_defaultCalendar, 636 nu: intl_numberingSystem, 637 hc: () => { 638 return null; 639 }, 640 }, 641 }; 642 } 643 644 /** 645 * Create function to be cached and returned by Intl.DateTimeFormat.prototype.format. 646 * 647 * Spec: ECMAScript Internationalization API Specification, 12.1.5. 648 */ 649 function createDateTimeFormatFormat(dtf) { 650 // This function is not inlined in $Intl_DateTimeFormat_format_get to avoid 651 // creating a call-object on each call to $Intl_DateTimeFormat_format_get. 652 return function(date) { 653 // Step 1 (implicit). 654 655 // Step 2. 656 assert(IsObject(dtf), "dateTimeFormatFormatToBind called with non-Object"); 657 assert( 658 intl_GuardToDateTimeFormat(dtf) !== null, 659 "dateTimeFormatFormatToBind called with non-DateTimeFormat" 660 ); 661 662 // Steps 3-5. 663 return intl_FormatDateTime(dtf, date, /* formatToParts = */ false); 664 }; 665 } 666 667 /** 668 * Returns a function bound to this DateTimeFormat that returns a String value 669 * representing the result of calling ToNumber(date) according to the 670 * effective locale and the formatting options of this DateTimeFormat. 671 * 672 * Spec: ECMAScript Internationalization API Specification, 12.4.3. 673 */ 674 // Uncloned functions with `$` prefix are allocated as extended function 675 // to store the original name in `SetCanonicalName`. 676 function $Intl_DateTimeFormat_format_get() { 677 // Steps 1-3. 678 var thisArg = UnwrapDateTimeFormat(this); 679 var dtf = thisArg; 680 if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) { 681 return callFunction( 682 intl_CallDateTimeFormatMethodIfWrapped, 683 thisArg, 684 "$Intl_DateTimeFormat_format_get" 685 ); 686 } 687 688 var internals = getDateTimeFormatInternals(dtf); 689 690 // Step 4. 691 if (internals.boundFormat === undefined) { 692 // Steps 4.a-c. 693 internals.boundFormat = createDateTimeFormatFormat(dtf); 694 } 695 696 // Step 5. 697 return internals.boundFormat; 698 } 699 SetCanonicalName($Intl_DateTimeFormat_format_get, "get format"); 700 701 /** 702 * Intl.DateTimeFormat.prototype.formatToParts ( date ) 703 * 704 * Spec: ECMAScript Internationalization API Specification, 12.4.4. 705 */ 706 function Intl_DateTimeFormat_formatToParts(date) { 707 // Step 1. 708 var dtf = this; 709 710 // Steps 2-3. 711 if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) { 712 return callFunction( 713 intl_CallDateTimeFormatMethodIfWrapped, 714 this, 715 date, 716 "Intl_DateTimeFormat_formatToParts" 717 ); 718 } 719 720 // Ensure the DateTimeFormat internals are resolved. 721 getDateTimeFormatInternals(dtf); 722 723 // Steps 4-6. 724 return intl_FormatDateTime(dtf, date, /* formatToParts = */ true); 725 } 726 727 /** 728 * Intl.DateTimeFormat.prototype.formatRange ( startDate , endDate ) 729 * 730 * Spec: Intl.DateTimeFormat.prototype.formatRange proposal 731 */ 732 function Intl_DateTimeFormat_formatRange(startDate, endDate) { 733 // Step 1. 734 var dtf = this; 735 736 // Step 2. 737 if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) { 738 return callFunction( 739 intl_CallDateTimeFormatMethodIfWrapped, 740 this, 741 startDate, 742 endDate, 743 "Intl_DateTimeFormat_formatRange" 744 ); 745 } 746 747 // Step 3. 748 if (startDate === undefined || endDate === undefined) { 749 ThrowTypeError( 750 JSMSG_UNDEFINED_DATE, 751 startDate === undefined ? "start" : "end", 752 "formatRange" 753 ); 754 } 755 756 // Ensure the DateTimeFormat internals are resolved. 757 getDateTimeFormatInternals(dtf); 758 759 // Steps 4-6. 760 return intl_FormatDateTimeRange(dtf, startDate, endDate, /* formatToParts = */ false); 761 } 762 763 /** 764 * Intl.DateTimeFormat.prototype.formatRangeToParts ( startDate , endDate ) 765 * 766 * Spec: Intl.DateTimeFormat.prototype.formatRange proposal 767 */ 768 function Intl_DateTimeFormat_formatRangeToParts(startDate, endDate) { 769 // Step 1. 770 var dtf = this; 771 772 // Step 2. 773 if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) { 774 return callFunction( 775 intl_CallDateTimeFormatMethodIfWrapped, 776 this, 777 startDate, 778 endDate, 779 "Intl_DateTimeFormat_formatRangeToParts" 780 ); 781 } 782 783 // Step 3. 784 if (startDate === undefined || endDate === undefined) { 785 ThrowTypeError( 786 JSMSG_UNDEFINED_DATE, 787 startDate === undefined ? "start" : "end", 788 "formatRangeToParts" 789 ); 790 } 791 792 // Ensure the DateTimeFormat internals are resolved. 793 getDateTimeFormatInternals(dtf); 794 795 // Steps 4-6. 796 return intl_FormatDateTimeRange(dtf, startDate, endDate, /* formatToParts = */ true); 797 } 798 799 /** 800 * Returns the resolved options for a DateTimeFormat object. 801 * 802 * Spec: ECMAScript Internationalization API Specification, 12.4.5. 803 */ 804 function Intl_DateTimeFormat_resolvedOptions() { 805 // Steps 1-3. 806 var thisArg = UnwrapDateTimeFormat(this); 807 var dtf = thisArg; 808 if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) { 809 return callFunction( 810 intl_CallDateTimeFormatMethodIfWrapped, 811 thisArg, 812 "Intl_DateTimeFormat_resolvedOptions" 813 ); 814 } 815 816 // Ensure the internals are resolved. 817 var internals = getDateTimeFormatInternals(dtf); 818 819 // Steps 4-5. 820 var result = { 821 locale: internals.locale, 822 calendar: internals.calendar, 823 numberingSystem: internals.numberingSystem, 824 timeZone: internals.timeZone, 825 }; 826 827 if (internals.pattern !== undefined) { 828 // The raw pattern option is only internal to Mozilla, and not part of the 829 // ECMA-402 API. 830 DefineDataProperty(result, "pattern", internals.pattern); 831 } 832 833 var hasDateStyle = internals.dateStyle !== undefined; 834 var hasTimeStyle = internals.timeStyle !== undefined; 835 836 if (hasDateStyle || hasTimeStyle) { 837 if (hasTimeStyle) { 838 // timeStyle (unlike dateStyle) requires resolving the pattern to 839 // ensure "hourCycle" and "hour12" properties are added to |result|. 840 intl_resolveDateTimeFormatComponents( 841 dtf, 842 result, 843 /* includeDateTimeFields = */ false 844 ); 845 } 846 if (hasDateStyle) { 847 DefineDataProperty(result, "dateStyle", internals.dateStyle); 848 } 849 if (hasTimeStyle) { 850 DefineDataProperty(result, "timeStyle", internals.timeStyle); 851 } 852 } else { 853 // Components bag or a (Mozilla-only) raw pattern. 854 intl_resolveDateTimeFormatComponents( 855 dtf, 856 result, 857 /* includeDateTimeFields = */ true 858 ); 859 } 860 861 // Step 6. 862 return result; 863 }