summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/intl/DateTimeFormat.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/intl/DateTimeFormat.js')
-rw-r--r--js/src/builtin/intl/DateTimeFormat.js1170
1 files changed, 1170 insertions, 0 deletions
diff --git a/js/src/builtin/intl/DateTimeFormat.js b/js/src/builtin/intl/DateTimeFormat.js
new file mode 100644
index 0000000000..bb6d202a3a
--- /dev/null
+++ b/js/src/builtin/intl/DateTimeFormat.js
@@ -0,0 +1,1170 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Portions Copyright Norbert Lindenberg 2011-2012. */
+
+/**
+ * 11.1.2 CreateDateTimeFormat ( newTarget, locales, options, required, defaults )
+ *
+ * Compute an internal properties object from |lazyDateTimeFormatData|.
+ */
+function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
+ assert(IsObject(lazyDateTimeFormatData), "lazy data not an object?");
+
+ // Lazy DateTimeFormat data has the following structure:
+ //
+ // {
+ // requestedLocales: List of locales,
+ //
+ // localeOpt: // *first* opt computed in InitializeDateTimeFormat
+ // {
+ // localeMatcher: "lookup" / "best fit",
+ //
+ // ca: string matching a Unicode extension type, // optional
+ //
+ // nu: string matching a Unicode extension type, // optional
+ //
+ // hc: "h11" / "h12" / "h23" / "h24", // optional
+ // }
+ //
+ // timeZone: IANA time zone name or a normalized time zone offset string,
+ //
+ // formatOptions: // *second* opt computed in InitializeDateTimeFormat
+ // {
+ // // all the properties/values listed in Table 3
+ // // (weekday, era, year, month, day, &c.)
+ //
+ // hour12: true / false, // optional
+ // }
+ //
+ // formatMatcher: "basic" / "best fit",
+ //
+ // dateStyle: "full" / "long" / "medium" / "short" / undefined,
+ //
+ // timeStyle: "full" / "long" / "medium" / "short" / undefined,
+ //
+ // patternOption:
+ // String representing LDML Date Format pattern or undefined
+ // }
+ //
+ // Note that lazy data is only installed as a final step of initialization,
+ // so every DateTimeFormat lazy data object has *all* these properties,
+ // never a subset of them.
+
+ var internalProps = std_Object_create(null);
+
+ var DateTimeFormat = dateTimeFormatInternalProperties;
+
+ // Compute effective locale.
+
+ // Step 17.
+ var localeData = DateTimeFormat.localeData;
+
+ // Step 18.
+ var r = ResolveLocale(
+ "DateTimeFormat",
+ lazyDateTimeFormatData.requestedLocales,
+ lazyDateTimeFormatData.localeOpt,
+ DateTimeFormat.relevantExtensionKeys,
+ localeData
+ );
+
+ // Steps 19-22.
+ internalProps.locale = r.locale;
+ internalProps.calendar = r.ca;
+ internalProps.numberingSystem = r.nu;
+
+ // Step 34. (Reordered)
+ var formatOptions = lazyDateTimeFormatData.formatOptions;
+
+ // Steps 23-29.
+ //
+ // Copy the hourCycle setting, if present, to the format options. But
+ // only do this if no hour12 option is present, because the latter takes
+ // precedence over hourCycle.
+ if (r.hc !== null && formatOptions.hour12 === undefined) {
+ formatOptions.hourCycle = r.hc;
+ }
+
+ // Step 33.
+ internalProps.timeZone = lazyDateTimeFormatData.timeZone;
+
+ // Steps 45-50, more or less.
+ if (lazyDateTimeFormatData.patternOption !== undefined) {
+ internalProps.pattern = lazyDateTimeFormatData.patternOption;
+ } else if (
+ lazyDateTimeFormatData.dateStyle !== undefined ||
+ lazyDateTimeFormatData.timeStyle !== undefined
+ ) {
+ internalProps.hourCycle = formatOptions.hourCycle;
+ internalProps.hour12 = formatOptions.hour12;
+ internalProps.dateStyle = lazyDateTimeFormatData.dateStyle;
+ internalProps.timeStyle = lazyDateTimeFormatData.timeStyle;
+ } else {
+ internalProps.hourCycle = formatOptions.hourCycle;
+ internalProps.hour12 = formatOptions.hour12;
+ internalProps.weekday = formatOptions.weekday;
+ internalProps.era = formatOptions.era;
+ internalProps.year = formatOptions.year;
+ internalProps.month = formatOptions.month;
+ internalProps.day = formatOptions.day;
+ internalProps.dayPeriod = formatOptions.dayPeriod;
+ internalProps.hour = formatOptions.hour;
+ internalProps.minute = formatOptions.minute;
+ internalProps.second = formatOptions.second;
+ internalProps.fractionalSecondDigits = formatOptions.fractionalSecondDigits;
+ internalProps.timeZoneName = formatOptions.timeZoneName;
+ }
+
+ // The caller is responsible for associating |internalProps| with the right
+ // object using |setInternalProperties|.
+ return internalProps;
+}
+
+/**
+ * Returns an object containing the DateTimeFormat internal properties of |obj|.
+ */
+function getDateTimeFormatInternals(obj) {
+ assert(IsObject(obj), "getDateTimeFormatInternals called with non-object");
+ assert(
+ intl_GuardToDateTimeFormat(obj) !== null,
+ "getDateTimeFormatInternals called with non-DateTimeFormat"
+ );
+
+ var internals = getIntlObjectInternals(obj);
+ assert(
+ internals.type === "DateTimeFormat",
+ "bad type escaped getIntlObjectInternals"
+ );
+
+ // If internal properties have already been computed, use them.
+ var internalProps = maybeInternalProperties(internals);
+ if (internalProps) {
+ return internalProps;
+ }
+
+ // Otherwise it's time to fully create them.
+ internalProps = resolveDateTimeFormatInternals(internals.lazyData);
+ setInternalProperties(internals, internalProps);
+ return internalProps;
+}
+
+/**
+ * 12.1.10 UnwrapDateTimeFormat( dtf )
+ */
+function UnwrapDateTimeFormat(dtf) {
+ // Steps 2 and 4 (error handling moved to caller).
+ if (
+ IsObject(dtf) &&
+ intl_GuardToDateTimeFormat(dtf) === null &&
+ !intl_IsWrappedDateTimeFormat(dtf) &&
+ callFunction(
+ std_Object_isPrototypeOf,
+ GetBuiltinPrototype("DateTimeFormat"),
+ dtf
+ )
+ ) {
+ dtf = dtf[intlFallbackSymbol()];
+ }
+ return dtf;
+}
+
+/**
+ * 6.4.2 CanonicalizeTimeZoneName ( timeZone )
+ *
+ * Canonicalizes the given IANA time zone name.
+ *
+ * ES2017 Intl draft rev 4a23f407336d382ed5e3471200c690c9b020b5f3
+ */
+function CanonicalizeTimeZoneName(timeZone) {
+ assert(typeof timeZone === "string", "CanonicalizeTimeZoneName");
+
+ // Step 1. (Not applicable, the input is already a valid IANA time zone.)
+ assert(timeZone !== "Etc/Unknown", "Invalid time zone");
+ assert(
+ timeZone === intl_IsValidTimeZoneName(timeZone),
+ "Time zone name not normalized"
+ );
+
+ // Step 2.
+ var ianaTimeZone = intl_canonicalizeTimeZone(timeZone);
+ assert(ianaTimeZone !== "Etc/Unknown", "Invalid canonical time zone");
+ assert(
+ ianaTimeZone === intl_IsValidTimeZoneName(ianaTimeZone),
+ "Unsupported canonical time zone"
+ );
+
+ // Step 3.
+ if (ianaTimeZone === "Etc/UTC" || ianaTimeZone === "Etc/GMT") {
+ ianaTimeZone = "UTC";
+ }
+
+ // Step 4.
+ return ianaTimeZone;
+}
+
+var timeZoneCache = {
+ icuDefaultTimeZone: undefined,
+ defaultTimeZone: undefined,
+};
+
+/**
+ * 6.4.3 DefaultTimeZone ()
+ *
+ * Returns the IANA time zone name for the host environment's current time zone.
+ *
+ * ES2017 Intl draft rev 4a23f407336d382ed5e3471200c690c9b020b5f3
+ */
+function DefaultTimeZone() {
+ if (intl_isDefaultTimeZone(timeZoneCache.icuDefaultTimeZone)) {
+ return timeZoneCache.defaultTimeZone;
+ }
+
+ // Verify that the current ICU time zone is a valid ECMA-402 time zone.
+ var icuDefaultTimeZone = intl_defaultTimeZone();
+ var timeZone = intl_IsValidTimeZoneName(icuDefaultTimeZone);
+ if (timeZone === null) {
+ // Before defaulting to "UTC", try to represent the default time zone
+ // using the Etc/GMT + offset format. This format only accepts full
+ // hour offsets.
+ var msPerHour = 60 * 60 * 1000;
+ var offset = intl_defaultTimeZoneOffset();
+ assert(
+ offset === (offset | 0),
+ "milliseconds offset shouldn't be able to exceed int32_t range"
+ );
+ var offsetHours = offset / msPerHour;
+ var offsetHoursFraction = offset % msPerHour;
+ if (offsetHoursFraction === 0) {
+ // Etc/GMT + offset uses POSIX-style signs, i.e. a positive offset
+ // means a location west of GMT.
+ timeZone =
+ "Etc/GMT" + (offsetHours < 0 ? "+" : "-") + std_Math_abs(offsetHours);
+
+ // Check if the fallback is valid.
+ timeZone = intl_IsValidTimeZoneName(timeZone);
+ }
+
+ // Fallback to "UTC" if everything else fails.
+ if (timeZone === null) {
+ timeZone = "UTC";
+ }
+ }
+
+ // Canonicalize the ICU time zone, e.g. change Etc/UTC to UTC.
+ var defaultTimeZone = CanonicalizeTimeZoneName(timeZone);
+
+ timeZoneCache.defaultTimeZone = defaultTimeZone;
+ timeZoneCache.icuDefaultTimeZone = icuDefaultTimeZone;
+
+ return defaultTimeZone;
+}
+
+/**
+ * 21.4.1.33.1 IsTimeZoneOffsetString ( offsetString )
+ * 21.4.1.33.2 ParseTimeZoneOffsetString ( offsetString )
+ * 11.1.3 FormatOffsetTimeZoneIdentifier ( offsetMinutes )
+ *
+ * Function to parse, validate, and normalize time zone offset strings.
+ *
+ * ES2024 draft rev 10d44bfce4640894a0ed366bb769f2700cc8839a
+ * ES2024 Intl draft rev 2f002b2000bf8b908efb793767bcfd23620e06db
+ */
+function TimeZoneOffsetString(offsetString) {
+ assert(typeof(offsetString) === "string", "offsetString is a string");
+
+ // UTCOffset :::
+ // TemporalSign Hour
+ // TemporalSign Hour HourSubcomponents[+Extended]
+ // TemporalSign Hour HourSubcomponents[~Extended]
+ //
+ // TemporalSign :::
+ // ASCIISign
+ // <MINUS>
+ //
+ // With <MINUS> = U+2212
+ //
+ // ASCIISign ::: one of
+ // + -
+ //
+ // Hour :::
+ // 0 DecimalDigit
+ // 1 DecimalDigit
+ // 20
+ // 21
+ // 22
+ // 23
+ //
+ // HourSubcomponents[Extended] :::
+ // TimeSeparator[?Extended] MinuteSecond
+ //
+ // TimeSeparator[Extended] :::
+ // [+Extended] :
+ // [~Extended] [empty]
+ //
+ // MinuteSecond :::
+ // 0 DecimalDigit
+ // 1 DecimalDigit
+ // 2 DecimalDigit
+ // 3 DecimalDigit
+ // 4 DecimalDigit
+ // 5 DecimalDigit
+
+ // Return if there are too few or too many characters for an offset string.
+ if (offsetString.length < 3 || offsetString.length > 6) {
+ return null;
+ }
+
+ // Self-hosted code only supports Latin-1 permanent atoms, so the Unicode <MINUS>
+ // can't be used in a string literal "\u2212". That means the first character has
+ // to be checked using the character code instead of performing a normal string
+ // comparison. Alternatively <MINUS> could be generated at runtime through
+ // |std_String_fromCharCode(0x2212)|, but that means allocating a string just for
+ // the comparison. And for consistency also check the remaining characters through
+ // their character code.
+
+ #define PLUS_SIGN 0x2b
+ #define HYPHEN_MINUS 0x2d
+ #define MINUS 0x2212
+ #define COLON 0x3a
+ #define DIGIT_ZERO 0x30
+ #define DIGIT_TWO 0x32
+ #define DIGIT_THREE 0x33
+ #define DIGIT_FIVE 0x35
+ #define DIGIT_NINE 0x39
+
+ /* global PLUS_SIGN, HYPHEN_MINUS, MINUS, COLON */
+ /* global DIGIT_ZERO, DIGIT_TWO, DIGIT_THREE, DIGIT_FIVE, DIGIT_NINE */
+
+ // The first character must match |TemporalSign|.
+ var sign = callFunction(std_String_charCodeAt, offsetString, 0);
+ if (sign !== PLUS_SIGN && sign !== HYPHEN_MINUS && sign !== MINUS) {
+ return null;
+ }
+
+ // Read the next two characters for the |Hour| grammar production.
+ var hourTens = callFunction(std_String_charCodeAt, offsetString, 1);
+ var hourOnes = callFunction(std_String_charCodeAt, offsetString, 2);
+
+ // Read the remaining characters for the optional |MinuteSecond| grammar production.
+ var minutesTens = DIGIT_ZERO;
+ var minutesOnes = DIGIT_ZERO;
+ if (offsetString.length > 3) {
+ // |TimeSeparator| is optional.
+ var separatorLength = offsetString[3] === ":" ? 1 : 0;
+
+ // Return if there are too many characters for an offset string.
+ if (offsetString.length !== (5 + separatorLength)) {
+ return null;
+ }
+
+ minutesTens = callFunction(
+ std_String_charCodeAt,
+ offsetString,
+ 3 + separatorLength,
+ );
+ minutesOnes = callFunction(
+ std_String_charCodeAt,
+ offsetString,
+ 4 + separatorLength,
+ );
+ }
+
+ // Validate the characters match the |Hour| and |MinuteSecond| productions:
+ // - hours must be in the range 0..23
+ // - minutes must in the range 0..59
+ if (
+ hourTens < DIGIT_ZERO ||
+ hourOnes < DIGIT_ZERO ||
+ minutesTens < DIGIT_ZERO ||
+ minutesOnes < DIGIT_ZERO ||
+ hourTens > DIGIT_TWO ||
+ hourOnes > DIGIT_NINE ||
+ minutesTens > DIGIT_FIVE ||
+ minutesOnes > DIGIT_NINE ||
+ (hourTens === DIGIT_TWO && hourOnes > DIGIT_THREE)
+ ) {
+ return null;
+ }
+
+ // FormatOffsetTimeZoneIdentifier, steps 1-5.
+ if (
+ hourTens === DIGIT_ZERO &&
+ hourOnes === DIGIT_ZERO &&
+ minutesTens === DIGIT_ZERO &&
+ minutesOnes === DIGIT_ZERO
+ ) {
+ sign = PLUS_SIGN;
+ } else if (sign === MINUS) {
+ sign = HYPHEN_MINUS;
+ }
+
+ return std_String_fromCharCode(
+ sign,
+ hourTens,
+ hourOnes,
+ COLON,
+ minutesTens,
+ minutesOnes,
+ );
+
+ #undef PLUS_SIGN
+ #undef HYPHEN_MINUS
+ #undef MINUS
+ #undef COLON
+ #undef DIGIT_ZERO
+ #undef DIGIT_TWO
+ #undef DIGIT_THREE
+ #undef DIGIT_FIVE
+ #undef DIGIT_NINE
+}
+
+/* eslint-disable complexity */
+/**
+ * 11.1.2 CreateDateTimeFormat ( newTarget, locales, options, required, defaults )
+ *
+ * Initializes an object as a DateTimeFormat.
+ *
+ * This method is complicated a moderate bit by its implementing initialization
+ * as a *lazy* concept. Everything that must happen now, does -- but we defer
+ * all the work we can until the object is actually used as a DateTimeFormat.
+ * This later work occurs in |resolveDateTimeFormatInternals|; steps not noted
+ * here occur there.
+ */
+function InitializeDateTimeFormat(
+ dateTimeFormat,
+ thisValue,
+ locales,
+ options,
+ required,
+ defaults,
+ mozExtensions
+) {
+ assert(
+ IsObject(dateTimeFormat),
+ "InitializeDateTimeFormat called with non-Object"
+ );
+ assert(
+ intl_GuardToDateTimeFormat(dateTimeFormat) !== null,
+ "InitializeDateTimeFormat called with non-DateTimeFormat"
+ );
+ assert(
+ required === "date" || required === "time" || required === "any",
+ `InitializeDateTimeFormat called with invalid required value: ${required}`
+ );
+ assert(
+ defaults === "date" || defaults === "time" || defaults === "all",
+ `InitializeDateTimeFormat called with invalid defaults value: ${defaults}`
+ );
+
+ // Lazy DateTimeFormat data has the following structure:
+ //
+ // {
+ // requestedLocales: List of locales,
+ //
+ // localeOpt: // *first* opt computed in InitializeDateTimeFormat
+ // {
+ // localeMatcher: "lookup" / "best fit",
+ //
+ // ca: string matching a Unicode extension type, // optional
+ //
+ // nu: string matching a Unicode extension type, // optional
+ //
+ // hc: "h11" / "h12" / "h23" / "h24", // optional
+ // }
+ //
+ // timeZone: IANA time zone name or a normalized time zone offset string,
+ //
+ // formatOptions: // *second* opt computed in InitializeDateTimeFormat
+ // {
+ // // all the properties/values listed in Table 3
+ // // (weekday, era, year, month, day, &c.)
+ //
+ // hour12: true / false, // optional
+ // }
+ //
+ // formatMatcher: "basic" / "best fit",
+ // }
+ //
+ // Note that lazy data is only installed as a final step of initialization,
+ // so every DateTimeFormat lazy data object has *all* these properties,
+ // never a subset of them.
+ var lazyDateTimeFormatData = std_Object_create(null);
+
+ // Step 1. (Performed in caller)
+
+ // Step 2.
+ var requestedLocales = CanonicalizeLocaleList(locales);
+ lazyDateTimeFormatData.requestedLocales = requestedLocales;
+
+ // Step 3. (Inlined call to CoerceOptionsToObject.)
+ if (options === undefined) {
+ options = std_Object_create(null);
+ } else {
+ options = ToObject(options);
+ }
+
+ // Compute options that impact interpretation of locale.
+ // Step 4.
+ var localeOpt = new_Record();
+ lazyDateTimeFormatData.localeOpt = localeOpt;
+
+ // Steps 5-6.
+ var localeMatcher = GetOption(
+ options,
+ "localeMatcher",
+ "string",
+ ["lookup", "best fit"],
+ "best fit"
+ );
+ localeOpt.localeMatcher = localeMatcher;
+
+ // Step 7.
+ var calendar = GetOption(options, "calendar", "string", undefined, undefined);
+
+ // Step 8.
+ if (calendar !== undefined) {
+ calendar = intl_ValidateAndCanonicalizeUnicodeExtensionType(
+ calendar,
+ "calendar",
+ "ca"
+ );
+ }
+
+ // Step 9.
+ localeOpt.ca = calendar;
+
+ // Step 10.
+ var numberingSystem = GetOption(
+ options,
+ "numberingSystem",
+ "string",
+ undefined,
+ undefined
+ );
+
+ // Step 11.
+ if (numberingSystem !== undefined) {
+ numberingSystem = intl_ValidateAndCanonicalizeUnicodeExtensionType(
+ numberingSystem,
+ "numberingSystem",
+ "nu"
+ );
+ }
+
+ // Step 12.
+ localeOpt.nu = numberingSystem;
+
+ // Step 13.
+ var hour12 = GetOption(options, "hour12", "boolean", undefined, undefined);
+
+ // Step 14.
+ var hourCycle = GetOption(
+ options,
+ "hourCycle",
+ "string",
+ ["h11", "h12", "h23", "h24"],
+ undefined
+ );
+
+ // Step 15.
+ if (hour12 !== undefined) {
+ // The "hourCycle" option is ignored if "hr12" is also present.
+ hourCycle = null;
+ }
+
+ // Step 16.
+ localeOpt.hc = hourCycle;
+
+ // Steps 17-29 (see resolveDateTimeFormatInternals).
+
+ // Step 29.
+ var timeZone = options.timeZone;
+
+ // Steps 30-34.
+ if (timeZone === undefined) {
+ // Step 30.a.
+ timeZone = DefaultTimeZone();
+
+ // Steps 32-34. (Not applicable in our implementation.)
+ } else {
+ // Step 31.a.
+ timeZone = ToString(timeZone);
+
+ // Steps 32-34.
+ var offsetString = TimeZoneOffsetString(timeZone);
+ if (offsetString !== null) {
+ // Steps 32.a-g. (Performed in TimeZoneOffsetString in our implementation.)
+ timeZone = offsetString;
+ } else {
+ // Steps 33-34.
+ var validTimeZone = intl_IsValidTimeZoneName(timeZone);
+ if (validTimeZone !== null) {
+ // Step 33.a.
+ timeZone = CanonicalizeTimeZoneName(validTimeZone);
+ } else {
+ // Step 34.a.
+ ThrowRangeError(JSMSG_INVALID_TIME_ZONE, timeZone);
+ }
+ }
+ }
+
+ // Step 33.
+ lazyDateTimeFormatData.timeZone = timeZone;
+
+ // Step 34.
+ var formatOptions = new_Record();
+ lazyDateTimeFormatData.formatOptions = formatOptions;
+
+ if (mozExtensions) {
+ var pattern = GetOption(options, "pattern", "string", undefined, undefined);
+ lazyDateTimeFormatData.patternOption = pattern;
+ }
+
+ // Step 35.
+ //
+ // Pass hr12 on to ICU. The hour cycle option is passed through |localeOpt|.
+ if (hour12 !== undefined) {
+ formatOptions.hour12 = hour12;
+ }
+
+ // Step 36. (Explicit format component computed in step 43.)
+
+ // Step 37.
+ // 11.5, Table 7: Components of date and time formats.
+ formatOptions.weekday = GetOption(
+ options,
+ "weekday",
+ "string",
+ ["narrow", "short", "long"],
+ undefined
+ );
+ formatOptions.era = GetOption(
+ options,
+ "era",
+ "string",
+ ["narrow", "short", "long"],
+ undefined
+ );
+ formatOptions.year = GetOption(
+ options,
+ "year",
+ "string",
+ ["2-digit", "numeric"],
+ undefined
+ );
+ formatOptions.month = GetOption(
+ options,
+ "month",
+ "string",
+ ["2-digit", "numeric", "narrow", "short", "long"],
+ undefined
+ );
+ formatOptions.day = GetOption(
+ options,
+ "day",
+ "string",
+ ["2-digit", "numeric"],
+ undefined
+ );
+ formatOptions.dayPeriod = GetOption(
+ options,
+ "dayPeriod",
+ "string",
+ ["narrow", "short", "long"],
+ undefined
+ );
+ formatOptions.hour = GetOption(
+ options,
+ "hour",
+ "string",
+ ["2-digit", "numeric"],
+ undefined
+ );
+ formatOptions.minute = GetOption(
+ options,
+ "minute",
+ "string",
+ ["2-digit", "numeric"],
+ undefined
+ );
+ formatOptions.second = GetOption(
+ options,
+ "second",
+ "string",
+ ["2-digit", "numeric"],
+ undefined
+ );
+ formatOptions.fractionalSecondDigits = GetNumberOption(
+ options,
+ "fractionalSecondDigits",
+ 1,
+ 3,
+ undefined
+ );
+ formatOptions.timeZoneName = GetOption(
+ options,
+ "timeZoneName",
+ "string",
+ [
+ "short",
+ "long",
+ "shortOffset",
+ "longOffset",
+ "shortGeneric",
+ "longGeneric",
+ ],
+ undefined
+ );
+
+ // Step 38.
+ //
+ // For some reason (ICU not exposing enough interface?) we drop the
+ // requested format matcher on the floor after this. In any case, even if
+ // doing so is justified, we have to do this work here in case it triggers
+ // getters or similar. (bug 852837)
+ var formatMatcher = GetOption(
+ options,
+ "formatMatcher",
+ "string",
+ ["basic", "best fit"],
+ "best fit"
+ );
+ void formatMatcher;
+
+ // Steps 39-40.
+ var dateStyle = GetOption(
+ options,
+ "dateStyle",
+ "string",
+ ["full", "long", "medium", "short"],
+ undefined
+ );
+ lazyDateTimeFormatData.dateStyle = dateStyle;
+
+ // Steps 41-42.
+ var timeStyle = GetOption(
+ options,
+ "timeStyle",
+ "string",
+ ["full", "long", "medium", "short"],
+ undefined
+ );
+ lazyDateTimeFormatData.timeStyle = timeStyle;
+
+ // Step 43.
+ if (dateStyle !== undefined || timeStyle !== undefined) {
+ /* eslint-disable no-nested-ternary */
+ var explicitFormatComponent =
+ formatOptions.weekday !== undefined
+ ? "weekday"
+ : formatOptions.era !== undefined
+ ? "era"
+ : formatOptions.year !== undefined
+ ? "year"
+ : formatOptions.month !== undefined
+ ? "month"
+ : formatOptions.day !== undefined
+ ? "day"
+ : formatOptions.dayPeriod !== undefined
+ ? "dayPeriod"
+ : formatOptions.hour !== undefined
+ ? "hour"
+ : formatOptions.minute !== undefined
+ ? "minute"
+ : formatOptions.second !== undefined
+ ? "second"
+ : formatOptions.fractionalSecondDigits !== undefined
+ ? "fractionalSecondDigits"
+ : formatOptions.timeZoneName !== undefined
+ ? "timeZoneName"
+ : undefined;
+ /* eslint-enable no-nested-ternary */
+
+ // Step 43.a.
+ if (explicitFormatComponent !== undefined) {
+ ThrowTypeError(
+ JSMSG_INVALID_DATETIME_OPTION,
+ explicitFormatComponent,
+ dateStyle !== undefined ? "dateStyle" : "timeStyle"
+ );
+ }
+
+ // Step 43.b.
+ if (required === "date" && timeStyle !== undefined) {
+ ThrowTypeError(
+ JSMSG_INVALID_DATETIME_STYLE,
+ "timeStyle",
+ "toLocaleDateString"
+ );
+ }
+
+ // Step 43.c.
+ if (required === "time" && dateStyle !== undefined) {
+ ThrowTypeError(
+ JSMSG_INVALID_DATETIME_STYLE,
+ "dateStyle",
+ "toLocaleTimeString"
+ );
+ }
+ } else {
+ // Step 44.a.
+ var needDefaults = true;
+
+ // Step 44.b.
+ if (required === "date" || required === "any") {
+ needDefaults =
+ formatOptions.weekday === undefined &&
+ formatOptions.year === undefined &&
+ formatOptions.month === undefined &&
+ formatOptions.day === undefined;
+ }
+
+ // Step 44.c.
+ if (required === "time" || required === "any") {
+ needDefaults =
+ needDefaults &&
+ formatOptions.dayPeriod === undefined &&
+ formatOptions.hour === undefined &&
+ formatOptions.minute === undefined &&
+ formatOptions.second === undefined &&
+ formatOptions.fractionalSecondDigits === undefined;
+ }
+
+ // Step 44.d.
+ if (needDefaults && (defaults === "date" || defaults === "all")) {
+ formatOptions.year = "numeric";
+ formatOptions.month = "numeric";
+ formatOptions.day = "numeric";
+ }
+
+ // Step 44.e.
+ if (needDefaults && (defaults === "time" || defaults === "all")) {
+ formatOptions.hour = "numeric";
+ formatOptions.minute = "numeric";
+ formatOptions.second = "numeric";
+ }
+
+ // Steps 44.f-h provided by ICU, more or less.
+ }
+
+ // Steps 45-50. (see resolveDateTimeFormatInternals).
+
+ // We've done everything that must be done now: mark the lazy data as fully
+ // computed and install it.
+ initializeIntlObject(
+ dateTimeFormat,
+ "DateTimeFormat",
+ lazyDateTimeFormatData
+ );
+
+ // 11.1.1 Intl.DateTimeFormat, step 3. (Inlined call to ChainDateTimeFormat.)
+ if (
+ dateTimeFormat !== thisValue &&
+ callFunction(
+ std_Object_isPrototypeOf,
+ GetBuiltinPrototype("DateTimeFormat"),
+ thisValue
+ )
+ ) {
+ DefineDataProperty(
+ thisValue,
+ intlFallbackSymbol(),
+ dateTimeFormat,
+ ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE
+ );
+
+ return thisValue;
+ }
+
+ // Step 51.
+ return dateTimeFormat;
+}
+/* eslint-enable complexity */
+
+/**
+ * Returns the subset of the given locale list for which this locale list has a
+ * matching (possibly fallback) locale. Locales appear in the same order in the
+ * returned list as in the input list.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 12.3.2.
+ */
+function Intl_DateTimeFormat_supportedLocalesOf(locales /*, options*/) {
+ var options = ArgumentsLength() > 1 ? GetArgument(1) : undefined;
+
+ // Step 1.
+ var availableLocales = "DateTimeFormat";
+
+ // Step 2.
+ var requestedLocales = CanonicalizeLocaleList(locales);
+
+ // Step 3.
+ return SupportedLocales(availableLocales, requestedLocales, options);
+}
+
+/**
+ * DateTimeFormat internal properties.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 9.1 and 12.3.3.
+ */
+var dateTimeFormatInternalProperties = {
+ localeData: dateTimeFormatLocaleData,
+ relevantExtensionKeys: ["ca", "hc", "nu"],
+};
+
+function dateTimeFormatLocaleData() {
+ return {
+ ca: intl_availableCalendars,
+ nu: getNumberingSystems,
+ hc: () => {
+ return [null, "h11", "h12", "h23", "h24"];
+ },
+ default: {
+ ca: intl_defaultCalendar,
+ nu: intl_numberingSystem,
+ hc: () => {
+ return null;
+ },
+ },
+ };
+}
+
+/**
+ * Create function to be cached and returned by Intl.DateTimeFormat.prototype.format.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 12.1.5.
+ */
+function createDateTimeFormatFormat(dtf) {
+ // This function is not inlined in $Intl_DateTimeFormat_format_get to avoid
+ // creating a call-object on each call to $Intl_DateTimeFormat_format_get.
+ return function(date) {
+ // Step 1 (implicit).
+
+ // Step 2.
+ assert(IsObject(dtf), "dateTimeFormatFormatToBind called with non-Object");
+ assert(
+ intl_GuardToDateTimeFormat(dtf) !== null,
+ "dateTimeFormatFormatToBind called with non-DateTimeFormat"
+ );
+
+ // Steps 3-4.
+ var x = date === undefined ? std_Date_now() : ToNumber(date);
+
+ // Step 5.
+ return intl_FormatDateTime(dtf, x, /* formatToParts = */ false);
+ };
+}
+
+/**
+ * Returns a function bound to this DateTimeFormat that returns a String value
+ * representing the result of calling ToNumber(date) according to the
+ * effective locale and the formatting options of this DateTimeFormat.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 12.4.3.
+ */
+// Uncloned functions with `$` prefix are allocated as extended function
+// to store the original name in `SetCanonicalName`.
+function $Intl_DateTimeFormat_format_get() {
+ // Steps 1-3.
+ var thisArg = UnwrapDateTimeFormat(this);
+ var dtf = thisArg;
+ if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) {
+ return callFunction(
+ intl_CallDateTimeFormatMethodIfWrapped,
+ thisArg,
+ "$Intl_DateTimeFormat_format_get"
+ );
+ }
+
+ var internals = getDateTimeFormatInternals(dtf);
+
+ // Step 4.
+ if (internals.boundFormat === undefined) {
+ // Steps 4.a-c.
+ internals.boundFormat = createDateTimeFormatFormat(dtf);
+ }
+
+ // Step 5.
+ return internals.boundFormat;
+}
+SetCanonicalName($Intl_DateTimeFormat_format_get, "get format");
+
+/**
+ * Intl.DateTimeFormat.prototype.formatToParts ( date )
+ *
+ * Spec: ECMAScript Internationalization API Specification, 12.4.4.
+ */
+function Intl_DateTimeFormat_formatToParts(date) {
+ // Step 1.
+ var dtf = this;
+
+ // Steps 2-3.
+ if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) {
+ return callFunction(
+ intl_CallDateTimeFormatMethodIfWrapped,
+ this,
+ date,
+ "Intl_DateTimeFormat_formatToParts"
+ );
+ }
+
+ // Steps 4-5.
+ var x = date === undefined ? std_Date_now() : ToNumber(date);
+
+ // Ensure the DateTimeFormat internals are resolved.
+ getDateTimeFormatInternals(dtf);
+
+ // Step 6.
+ return intl_FormatDateTime(dtf, x, /* formatToParts = */ true);
+}
+
+/**
+ * Intl.DateTimeFormat.prototype.formatRange ( startDate , endDate )
+ *
+ * Spec: Intl.DateTimeFormat.prototype.formatRange proposal
+ */
+function Intl_DateTimeFormat_formatRange(startDate, endDate) {
+ // Step 1.
+ var dtf = this;
+
+ // Step 2.
+ if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) {
+ return callFunction(
+ intl_CallDateTimeFormatMethodIfWrapped,
+ this,
+ startDate,
+ endDate,
+ "Intl_DateTimeFormat_formatRange"
+ );
+ }
+
+ // Step 3.
+ if (startDate === undefined || endDate === undefined) {
+ ThrowTypeError(
+ JSMSG_UNDEFINED_DATE,
+ startDate === undefined ? "start" : "end",
+ "formatRange"
+ );
+ }
+
+ // Step 4.
+ var x = ToNumber(startDate);
+
+ // Step 5.
+ var y = ToNumber(endDate);
+
+ // Ensure the DateTimeFormat internals are resolved.
+ getDateTimeFormatInternals(dtf);
+
+ // Step 6.
+ return intl_FormatDateTimeRange(dtf, x, y, /* formatToParts = */ false);
+}
+
+/**
+ * Intl.DateTimeFormat.prototype.formatRangeToParts ( startDate , endDate )
+ *
+ * Spec: Intl.DateTimeFormat.prototype.formatRange proposal
+ */
+function Intl_DateTimeFormat_formatRangeToParts(startDate, endDate) {
+ // Step 1.
+ var dtf = this;
+
+ // Step 2.
+ if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) {
+ return callFunction(
+ intl_CallDateTimeFormatMethodIfWrapped,
+ this,
+ startDate,
+ endDate,
+ "Intl_DateTimeFormat_formatRangeToParts"
+ );
+ }
+
+ // Step 3.
+ if (startDate === undefined || endDate === undefined) {
+ ThrowTypeError(
+ JSMSG_UNDEFINED_DATE,
+ startDate === undefined ? "start" : "end",
+ "formatRangeToParts"
+ );
+ }
+
+ // Step 4.
+ var x = ToNumber(startDate);
+
+ // Step 5.
+ var y = ToNumber(endDate);
+
+ // Ensure the DateTimeFormat internals are resolved.
+ getDateTimeFormatInternals(dtf);
+
+ // Step 6.
+ return intl_FormatDateTimeRange(dtf, x, y, /* formatToParts = */ true);
+}
+
+/**
+ * Returns the resolved options for a DateTimeFormat object.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 12.4.5.
+ */
+function Intl_DateTimeFormat_resolvedOptions() {
+ // Steps 1-3.
+ var thisArg = UnwrapDateTimeFormat(this);
+ var dtf = thisArg;
+ if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) {
+ return callFunction(
+ intl_CallDateTimeFormatMethodIfWrapped,
+ thisArg,
+ "Intl_DateTimeFormat_resolvedOptions"
+ );
+ }
+
+ // Ensure the internals are resolved.
+ var internals = getDateTimeFormatInternals(dtf);
+
+ // Steps 4-5.
+ var result = {
+ locale: internals.locale,
+ calendar: internals.calendar,
+ numberingSystem: internals.numberingSystem,
+ timeZone: internals.timeZone,
+ };
+
+ if (internals.pattern !== undefined) {
+ // The raw pattern option is only internal to Mozilla, and not part of the
+ // ECMA-402 API.
+ DefineDataProperty(result, "pattern", internals.pattern);
+ }
+
+ var hasDateStyle = internals.dateStyle !== undefined;
+ var hasTimeStyle = internals.timeStyle !== undefined;
+
+ if (hasDateStyle || hasTimeStyle) {
+ if (hasTimeStyle) {
+ // timeStyle (unlike dateStyle) requires resolving the pattern to
+ // ensure "hourCycle" and "hour12" properties are added to |result|.
+ intl_resolveDateTimeFormatComponents(
+ dtf,
+ result,
+ /* includeDateTimeFields = */ false
+ );
+ }
+ if (hasDateStyle) {
+ DefineDataProperty(result, "dateStyle", internals.dateStyle);
+ }
+ if (hasTimeStyle) {
+ DefineDataProperty(result, "timeStyle", internals.timeStyle);
+ }
+ } else {
+ // Components bag or a (Mozilla-only) raw pattern.
+ intl_resolveDateTimeFormatComponents(
+ dtf,
+ result,
+ /* includeDateTimeFields = */ true
+ );
+ }
+
+ // Step 6.
+ return result;
+}