tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }