Source code

Revision control

Copy as Markdown

Other Tools

/* 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;
}