// GENERATED, DO NOT EDIT // file: testIntl.js // Copyright (C) 2011 2012 Norbert Lindenberg. All rights reserved. // Copyright (C) 2012 2013 Mozilla Corporation. All rights reserved. // Copyright (C) 2020 Apple Inc. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- description: | This file contains shared functions for the tests in the conformance test suite for the ECMAScript Internationalization API. author: Norbert Lindenberg defines: - testWithIntlConstructors - taintDataProperty - taintMethod - taintProperties - taintArray - getLocaleSupportInfo - getInvalidLanguageTags - isCanonicalizedStructurallyValidLanguageTag - getInvalidLocaleArguments - testOption - testForUnwantedRegExpChanges - allCalendars - allCollations - allNumberingSystems - isValidNumberingSystem - numberingSystemDigits - allSimpleSanctionedUnits - testNumberFormat - getDateTimeComponents - getDateTimeComponentValues - isCanonicalizedStructurallyValidTimeZoneName - partitionDurationFormatPattern - formatDurationFormatPattern
---*/ /**
*/
/** * @description Calls the provided function for every service constructor in * the Intl object. * @param {Function} f the function to call for each service constructor in * the Intl object. * @param {Function} Constructor the constructor object to test with.
*/ function testWithIntlConstructors(f) { var constructors = ["Collator", "NumberFormat", "DateTimeFormat"];
// Optionally supported Intl constructors. // NB: Intl.Locale isn't an Intl service constructor! // Intl.DisplayNames cannot be called without type in options.
["PluralRules", "RelativeTimeFormat", "ListFormat"].forEach(function(constructor) { if (typeof Intl[constructor] === "function") {
constructors[constructors.length] = constructor;
}
});
/** * Taints a named data property of the given object by installing * a setter that throws an exception. * @param {object} obj the object whose data property to taint * @param {string} property the property to taint
*/ function taintDataProperty(obj, property) {
Object.defineProperty(obj, property, {
set: function(value) { thrownew Test262Error("Client code can adversely affect behavior: setter for " + property + ".");
},
enumerable: false,
configurable: true
});
}
/** * Taints a named method of the given object by replacing it with a function * that throws an exception. * @param {object} obj the object whose method to taint * @param {string} property the name of the method to taint
*/ function taintMethod(obj, property) {
Object.defineProperty(obj, property, {
value: function() { thrownew Test262Error("Client code can adversely affect behavior: method " + property + ".");
},
writable: true,
enumerable: false,
configurable: true
});
}
/** * Taints the given properties (and similarly named properties) by installing * setters on Object.prototype that throw exceptions. * @param {Array} properties an array of property names to taint
*/ function taintProperties(properties) {
properties.forEach(function (property) { var adaptedProperties = [property, "__" + property, "_" + property, property + "_", property + "__"];
adaptedProperties.forEach(function (property) {
taintDataProperty(Object.prototype, property);
});
});
}
/** * Taints the Array object by creating a setter for the property "0" and * replacing some key methods with functions that throw exceptions.
*/ function taintArray() {
taintDataProperty(Array.prototype, "0");
taintMethod(Array.prototype, "indexOf");
taintMethod(Array.prototype, "join");
taintMethod(Array.prototype, "push");
taintMethod(Array.prototype, "slice");
taintMethod(Array.prototype, "sort");
}
/** * Gets locale support info for the given constructor object, which must be one * of Intl constructors. * @param {object} Constructor the constructor for which to get locale support info * @param {object} options the options while calling the constructor * @return {object} locale support info with the following properties: * supported: array of fully supported language tags * byFallback: array of language tags that are supported through fallbacks * unsupported: array of unsupported language tags
*/ function getLocaleSupportInfo(Constructor, options) { var languages = ["zh", "es", "en", "hi", "ur", "ar", "ja", "pa"]; var scripts = ["Latn", "Hans", "Deva", "Arab", "Jpan", "Hant", "Guru"]; var countries = ["CN", "IN", "US", "PK", "JP", "TW", "HK", "SG", "419"];
var allTags = []; var i, j, k; var language, script, country; for (i = 0; i < languages.length; i++) {
language = languages[i];
allTags.push(language); for (j = 0; j < scripts.length; j++) {
script = scripts[j];
allTags.push(language + "-" + script); for (k = 0; k < countries.length; k++) {
country = countries[k];
allTags.push(language + "-" + script + "-" + country);
}
} for (k = 0; k < countries.length; k++) {
country = countries[k];
allTags.push(language + "-" + country);
}
}
var supported = []; var byFallback = []; var unsupported = []; for (i = 0; i < allTags.length; i++) { var request = allTags[i]; var result = new Constructor([request], options).resolvedOptions().locale; if (request === result) {
supported.push(request);
} elseif (request.indexOf(result) === 0) {
byFallback.push(request);
} else {
unsupported.push(request);
}
}
/** * Returns an array of strings for which IsStructurallyValidLanguageTag() returns false
*/ function getInvalidLanguageTags() { var invalidLanguageTags = [ "", // empty tag "i", // singleton alone "x", // private use without subtag "u", // extension singleton in first place "419", // region code in first place "u-nu-latn-cu-bob", // extension sequence without language "hans-cmn-cn", // "hans" could theoretically be a 4-letter language code, // but those can't be followed by extlang codes. "cmn-hans-cn-u-u", // duplicate singleton "cmn-hans-cn-t-u-ca-u", // duplicate singleton "de-gregory-gregory", // duplicate variant "*", // language range "de-*", // language range "中文", // non-ASCII letters "en-ß", // non-ASCII letters "ıd", // non-ASCII letters "es-Latn-latn", // two scripts "pl-PL-pl", // two regions "u-ca-gregory", // extension in first place "de-1996-1996", // duplicate numeric variant "pt-u-ca-gregory-u-nu-latn", // duplicate singleton subtag
// Invalid tags starting with: https://github.com/tc39/ecma402/pull/289 "no-nyn", // regular grandfathered in BCP47, but invalid in UTS35 "i-klingon", // irregular grandfathered in BCP47, but invalid in UTS35 "zh-hak-CN", // language with extlang in BCP47, but invalid in UTS35 "sgn-ils", // language with extlang in BCP47, but invalid in UTS35 "x-foo", // privateuse-only in BCP47, but invalid in UTS35 "x-en-US-12345", // more privateuse-only variants. "x-12345-12345-en-US", "x-en-US-12345-12345", "x-en-u-foo", "x-en-u-foo-u-bar", "x-u-foo",
// underscores in different parts of the language tag "de_DE", "DE_de", "cmn_Hans", "cmn-hans_cn", "es_419", "es-419-u-nu-latn-cu_bob", "i_klingon", "cmn-hans-cn-t-ca-u-ca-x_t-u", "enochian_enochian", "de-gregory_u-ca-gregory",
"en\u0000", // null-terminator sequence " en", // leading whitespace "en ", // trailing whitespace "it-IT-Latn", // country before script tag "de-u", // incomplete Unicode extension sequences "de-u-", "de-u-ca-", "de-u-ca-gregory-", "si-x", // incomplete private-use tags "x-", "x-y-",
];
// make sure the data above is correct for (var i = 0; i < invalidLanguageTags.length; ++i) { var invalidTag = invalidLanguageTags[i]; assert(
!isCanonicalizedStructurallyValidLanguageTag(invalidTag), "Test data \"" + invalidTag + "\" is a canonicalized and structurally valid language tag."
);
}
return invalidLanguageTags;
}
/** * @description Tests whether locale is a String value representing a * structurally valid and canonicalized BCP 47 language tag, as defined in * sections 6.2.2 and 6.2.3 of the ECMAScript Internationalization API * Specification. * @param {String} locale the string to be tested. * @result {Boolean} whether the test succeeded.
*/ function isCanonicalizedStructurallyValidLanguageTag(locale) {
var transformKeyRE = new RegExp("^" + alpha + digit + "$", "i");
/** * Verifies that the given string is a well-formed Unicode BCP 47 Locale Identifier * with no duplicate variant or singleton subtags. * * Spec: ECMAScript Internationalization API Specification, draft, 6.2.2.
*/ function isStructurallyValidLanguageTag(locale) { if (!languageTagRE.test(locale)) { returnfalse;
}
locale = locale.split(/-x-/)[0]; return !duplicateSingletonRE.test(locale) && !duplicateVariantRE.test(locale);
}
/** * Mappings from complete tags to preferred values. * * Spec: http://unicode.org/reports/tr35/#Identifiers * Version: CLDR, version 36.1
*/ var __tagMappings = { // property names must be in lower case; values in canonical form
/** * Mappings from language subtags to preferred values. * * Spec: http://unicode.org/reports/tr35/#Identifiers * Version: CLDR, version 36.1
*/ var __languageMappings = { // property names and values must be in canonical case
/** * Mappings from region subtags to preferred values. * * Spec: http://unicode.org/reports/tr35/#Identifiers * Version: CLDR, version 36.1
*/ var __regionMappings = { // property names and values must be in canonical case
/** * Complex mappings from language subtags to preferred values. * * Spec: http://unicode.org/reports/tr35/#Identifiers * Version: CLDR, version 36.1
*/ var __complexLanguageMappings = { // property names and values must be in canonical case
/** * Complex mappings from region subtags to preferred values. * * Spec: http://unicode.org/reports/tr35/#Identifiers * Version: CLDR, version 36.1
*/ var __complexRegionMappings = { // property names and values must be in canonical case
/** * Mappings from variant subtags to preferred values. * * Spec: http://unicode.org/reports/tr35/#Identifiers * Version: CLDR, version 36.1
*/ var __variantMappings = { // property names and values must be in canonical case
/** * Mappings from Unicode extension subtags to preferred values. * * Spec: http://unicode.org/reports/tr35/#Identifiers * Version: CLDR, version 36.1
*/ var __unicodeMappings = { // property names and values must be in canonical case
/** * Mappings from Unicode extension subtags to preferred values. * * Spec: http://unicode.org/reports/tr35/#Identifiers * Version: CLDR, version 36.1
*/ var __transformMappings = { // property names and values must be in canonical case
/** * Canonicalizes the given well-formed BCP 47 language tag, including regularized case of subtags. * * Spec: ECMAScript Internationalization API Specification, draft, 6.2.3. * Spec: RFC 5646, section 4.5.
*/ function canonicalizeLanguageTag(locale) {
// start with lower case for easier processing, and because most subtags will need to be lower case anyway
locale = locale.toLowerCase();
// handle mappings for complete tags if (__tagMappings.hasOwnProperty(locale)) { return __tagMappings[locale];
}
var subtags = locale.split("-"); var i = 0;
// handle standard part: all subtags before first variant or singleton subtag var language; var script; var region; while (i < subtags.length) { var subtag = subtags[i]; if (i === 0) {
language = subtag;
} elseif (subtag.length === 2 || subtag.length === 3) {
region = subtag.toUpperCase();
} elseif (subtag.length === 4 && !("0" <= subtag[0] && subtag[0] <= "9")) {
script = subtag[0].toUpperCase() + subtag.substring(1).toLowerCase();
} else { break;
}
i++;
}
if (__languageMappings.hasOwnProperty(language)) {
language = __languageMappings[language];
} elseif (__complexLanguageMappings.hasOwnProperty(language)) { var mapping = __complexLanguageMappings[language];
language = mapping.language; if (script === undefined && mapping.hasOwnProperty("script")) {
script = mapping.script;
} if (region === undefined && mapping.hasOwnProperty("region")) {
region = mapping.region;
}
}
if (region !== undefined) { if (__regionMappings.hasOwnProperty(region)) {
region = __regionMappings[region];
} elseif (__complexRegionMappings.hasOwnProperty(region)) { var mapping = __complexRegionMappings[region];
var mappingKey = language; if (script !== undefined) {
mappingKey += "-" + script;
}
if (mapping.hasOwnProperty(mappingKey)) {
region = mapping[mappingKey];
} else {
region = mapping.default;
}
}
}
// handle variants var variants = []; while (i < subtags.length && subtags[i].length > 1) { var variant = subtags[i];
if (__variantMappings.hasOwnProperty(variant)) { var mapping = __variantMappings[variant]; switch (mapping.type) { case"language":
language = mapping.replacement; break;
case"region":
region = mapping.replacement; break;
while (j < i && !transformKeyRE.test(subtags[j])) {
j++;
}
extension = "t";
var transformLanguage = subtags.slice(extensionStart + 1, j).join("-"); if (transformLanguage !== "") {
extension += "-" + canonicalizeLanguageTag(transformLanguage).toLowerCase();
}
while (j < i) { var keyStart = j;
j++;
while (j < i && subtags[j].length > 2) {
j++;
}
var key = subtags[keyStart]; var value = subtags.slice(keyStart + 1, j).join("-");
if (__transformMappings.hasOwnProperty(key)) { var mapping = __transformMappings[key]; if (mapping.hasOwnProperty(value)) {
value = mapping[value];
}
}
var invalidLanguageTags = [
"", // empty tag
"i", // singleton alone
"x", // private use without subtag
"u", // extension singleton in first place
"419", // region code in first place
"u-nu-latn-cu-bob", // extension sequence without language
"hans-cmn-cn", // "hans" could theoretically be a 4-letter language code,
// but those can't be followed by extlang codes.
"abcdefghi", // overlong language
"cmn-hans-cn-u-u", // duplicate singleton
"cmn-hans-cn-t-u-ca-u", // duplicate singleton
"de-gregory-gregory", // duplicate variant
"*", // language range
"de-*", // language range
"中文", // non-ASCII letters
"en-ß", // non-ASCII letters
"ıd" // non-ASCII letters
];
/**
* Tests whether the named options property is correctly handled by the given constructor.
* @param {object} Constructor the constructor to test.
* @param {string} property the name of the options property to test.
* @param {string} type the type that values of the property are expected to have
* @param {Array} [values] an array of allowed values for the property. Not needed for boolean.
* @param {any} fallback the fallback value that the property assumes if not provided.
* @param {object} testOptions additional options:
* @param {boolean} isOptional whether support for this property is optional for implementations.
* @param {boolean} noReturn whether the resulting value of the property is not returned.
* @param {boolean} isILD whether the resulting value of the property is implementation and locale dependent.
* @param {object} extra additional option to pass along, properties are value -> {option: value}.
*/
function testOption(Constructor, property, type, values, fallback, testOptions) {
var isOptional = testOptions !== undefined && testOptions.isOptional === true;
var noReturn = testOptions !== undefined && testOptions.noReturn === true;
var isILD = testOptions !== undefined && testOptions.isILD === true;
function addExtraOptions(options, value, testOptions) {
if (testOptions !== undefined && testOptions.extra !== undefined) {
var extra;
if (value !== undefined && testOptions.extra[value] !== undefined) {
extra = testOptions.extra[value];
} else if (testOptions.extra.any !== undefined) {
extra = testOptions.extra.any;
}
if (extra !== undefined) {
Object.getOwnPropertyNames(extra).forEach(function (prop) {
options[prop] = extra[prop];
});
}
}
}
var testValues, options, obj, expected, actual, error;
// test that the specified values are accepted. Also add values that convert to specified values.
if (type === "boolean") {
if (values === undefined) {
values = [true, false];
}
testValues = values.slice(0);
testValues.push(888);
testValues.push(0);
} else if (type === "string") {
testValues = values.slice(0);
testValues.push({toString: function () { return values[0]; }});
}
testValues.forEach(function (value) {
options = {};
options[property] = value;
addExtraOptions(options, value, testOptions);
obj = new Constructor(undefined, options);
if (noReturn) {
if (obj.resolvedOptions().hasOwnProperty(property)) {
throw new Test262Error("Option property " + property + " is returned, but shouldn't be.");
}
} else {
actual = obj.resolvedOptions()[property];
if (isILD) {
if (actual !== undefined && values.indexOf(actual) === -1) {
throw new Test262Error("Invalid value " + actual + " returned for property " + property + ".");
}
} else {
if (type === "boolean") {
expected = Boolean(value);
} else if (type === "string") {
expected = String(value);
}
if (actual !== expected && !(isOptional && actual === undefined)) {
throw new Test262Error("Option value " + value + " for property " + property +
" was not accepted; got " + actual + " instead.");
}
}
}
});
// test that invalid values are rejected
if (type === "string") {
var invalidValues = ["invalidValue", -1, null];
// assume that we won't have values in caseless scripts
if (values[0].toUpperCase() !== values[0]) {
invalidValues.push(values[0].toUpperCase());
} else {
invalidValues.push(values[0].toLowerCase());
}
invalidValues.forEach(function (value) {
options = {};
options[property] = value;
addExtraOptions(options, value, testOptions);
error = undefined;
try {
obj = new Constructor(undefined, options);
} catch (e) {
error = e;
}
if (error === undefined) {
throw new Test262Error("Invalid option value " + value + " for property " + property + " was not rejected.");
} else if (error.name !== "RangeError") {
throw new Test262Error("Invalid option value " + value + " for property " + property + " was rejected with wrong error " + error.name + ".");
}
});
}
// test that fallback value or another valid value is used if no options value is provided
if (!noReturn) {
options = {};
addExtraOptions(options, undefined, testOptions);
obj = new Constructor(undefined, options);
actual = obj.resolvedOptions()[property];
if (!(isOptional && actual === undefined)) {
if (fallback !== undefined) {
if (actual !== fallback) {
throw new Test262Error("Option fallback value " + fallback + " for property " + property +
" was not used; got " + actual + " instead.");
}
} else {
if (values.indexOf(actual) === -1 && !(isILD && actual === undefined)) {
throw new Test262Error("Invalid value " + actual + " returned for property " + property + ".");
}
}
}
}
}
/**
* Properties of the RegExp constructor that may be affected by use of regular
* expressions, and the default values of these properties. Properties are from
* https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Deprecated_and_obsolete_features#RegExp_Properties
*/
var regExpProperties = ["$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9",
"$_", "$*", "$&", "$+", "$`", "$'",
"input", "lastMatch", "lastParen", "leftContext", "rightContext"
];
/**
* Tests that executing the provided function (which may use regular expressions
* in its implementation) does not create or modify unwanted properties on the
* RegExp constructor.
*/
function testForUnwantedRegExpChanges(testFunc) {
(/(?:)/).test("");
testFunc();
regExpProperties.forEach(function (property) {
if (RegExp[property] !== regExpPropertiesDefaultValues[property]) {
throw new Test262Error("RegExp has unexpected property " + property + " with value " +
RegExp[property] + ".");
}
});
}
/**
* Returns an array of all known calendars.
*/
function allCalendars() {
// source: CLDR file common/bcp47/number.xml; version CLDR 39.
// https://github.com/unicode-org/cldr/blob/master/common/bcp47/calendar.xml
return [
"buddhist",
"chinese",
"coptic",
"dangi",
"ethioaa",
"ethiopic",
"gregory",
"hebrew",
"indian",
"islamic",
"islamic-umalqura",
"islamic-tbla",
"islamic-civil",
"islamic-rgsa",
"iso8601",
"japanese",
"persian",
"roc",
];
}
/**
* Returns an array of all known collations.
*/
function allCollations() {
// source: CLDR file common/bcp47/collation.xml; version CLDR 39.
// https://github.com/unicode-org/cldr/blob/master/common/bcp47/collation.xml
return [
"big5han",
"compat",
"dict",
"direct",
"ducet",
"emoji",
"eor",
"gb2312",
"phonebk",
"phonetic",
"pinyin",
"reformed",
"search",
"searchjl",
"standard",
"stroke",
"trad",
"unihan",
"zhuyin",
];
}
/**
* Tests whether name is a valid BCP 47 numbering system name
* and not excluded from use in the ECMAScript Internationalization API.
* @param {string} name the name to be tested.
* @return {boolean} whether name is a valid BCP 47 numbering system name and
* allowed for use in the ECMAScript Internationalization API.
*/
function isValidNumberingSystem(name) {
var numberingSystems = allNumberingSystems();
var excluded = [
"finance",
"native",
"traditio"
];
/**
* Tests that number formatting is handled correctly. The function checks that the
* digit sequences in formatted output are as specified, converted to the
* selected numbering system, and embedded in consistent localized patterns.
* @param {Array} locales the locales to be tested.
* @param {Array} numberingSystems the numbering systems to be tested.
* @param {Object} options the options to pass to Intl.NumberFormat. Options
* must include {useGrouping: false}, and must cause 1.1 to be formatted
* pre- and post-decimal digits.
* @param {Object} testData maps input data (in ES5 9.3.1 format) to expected output strings
* in unlocalized format with Western digits.
*/
function testNumberFormat(locales, numberingSystems, options, testData) {
locales.forEach(function (locale) {
numberingSystems.forEach(function (numbering) {
var digits = numberingSystemDigits[numbering];
var format = new Intl.NumberFormat([locale + "-u-nu-" + numbering], options);
function getPatternParts(positive) {
var n = positive ? 1.1 : -1.1;
var formatted = format.format(n);
var oneoneRE = "([^" + digits + "]*)[" + digits + "]+([^" + digits + "]+)[" + digits + "]+([^" + digits + "]*)";
var match = formatted.match(new RegExp(oneoneRE));
if (match === null) {
throw new Test262Error("Unexpected formatted " + n + " for " +
format.resolvedOptions().locale + " and options " +
JSON.stringify(options) + ": " + formatted);
}
return match;
}
function toNumbering(raw) {
return raw.replace(/[0-9]/g, function (digit) {
return digits[digit.charCodeAt(0) - "0".charCodeAt(0)];
});
}
function buildExpected(raw, patternParts) {
var period = raw.indexOf(".");
if (period === -1) {
return patternParts[1] + toNumbering(raw) + patternParts[3];
} else {
return patternParts[1] +
toNumbering(raw.substring(0, period)) +
patternParts[2] +
toNumbering(raw.substring(period + 1)) +
patternParts[3];
}
}
if (format.resolvedOptions().numberingSystem === numbering) {
// figure out prefixes, infixes, suffixes for positive and negative values
var posPatternParts = getPatternParts(true);
var negPatternParts = getPatternParts(false);
Object.getOwnPropertyNames(testData).forEach(function (input) {
var rawExpected = testData[input];
var patternParts;
if (rawExpected[0] === "-") {
patternParts = negPatternParts;
rawExpected = rawExpected.substring(1);
} else {
patternParts = posPatternParts;
}
var expected = buildExpected(rawExpected, patternParts);
var actual = format.format(input);
if (actual !== expected) {
throw new Test262Error("Formatted value for " + input + ", " +
format.resolvedOptions().locale + " and options " +
JSON.stringify(options) + " is " + actual + "; expected " + expected + ".");
}
});
}
});
});
}
/**
* Return the components of date-time formats.
* @return {Array} an array with all date-time components.
*/
/**
* Return the valid values for the given date-time component, as specified
* by the table in section 12.1.1.
* @param {string} component a date-time component.
* @return {Array} an array with the valid values for the component.
*/
var result = components[component];
if (result === undefined) {
throw new Test262Error("Internal error: No values defined for date-time component " + component + ".");
}
return result;
}
/**
* @description Tests whether timeZone is a String value representing a
* structurally valid and canonicalized time zone name, as defined in
* sections 6.4.1 and 6.4.2 of the ECMAScript Internationalization API
* Specification.
* @param {String} timeZone the string to be tested.
* @result {Boolean} whether the test succeeded.
*/
function isCanonicalizedStructurallyValidTimeZoneName(timeZone) {
/**
* Regular expression defining IANA Time Zone names.
*
* Spec: IANA Time Zone Database, Theory file
*/
var fileNameComponent = "(?:[A-Za-z_]|\\.(?!\\.?(?:/|$)))[A-Za-z.\\-_]{0,13}";
var fileName = fileNameComponent + "(?:/" + fileNameComponent + ")*";
var etcName = "(?:Etc/)?GMT[+-]\\d{1,2}";
var systemVName = "SystemV/[A-Z]{3}\\d{1,2}(?:[A-Z]{3})?";
var legacyName = etcName + "|" + systemVName + "|CST6CDT|EST5EDT|MST7MDT|PST8PDT|NZ";
var zoneNamePattern = new RegExp("^(?:" + fileName + "|" + legacyName + ")$");
/**
* @description Simplified PartitionDurationFormatPattern implementation which
* only supports the "en" locale.
* @param {Object} durationFormat the duration format object
* @param {Object} duration the duration record
* @result {Array} an array with formatted duration parts
*/
function partitionDurationFormatPattern(durationFormat, duration) {
function durationToFractional(duration, exponent) {
let {
seconds = 0,
milliseconds = 0,
microseconds = 0,
nanoseconds = 0,
} = duration;
// Directly return the duration amount when no sub-seconds are present.
switch (exponent) {
case 9: {
if (milliseconds === 0 && microseconds === 0 && nanoseconds === 0) {
return seconds;
}
break;
}
case 6: {
if (microseconds === 0 && nanoseconds === 0) {
return milliseconds;
}
break;
}
case 3: {
if (nanoseconds === 0) {
return microseconds;
}
break;
}
}
// Otherwise compute the overall amount of nanoseconds using BigInt to avoid
// loss of precision.
let ns = BigInt(nanoseconds);
switch (exponent) {
case 9:
ns += BigInt(seconds) * 1_000_000_000n;
// fallthrough
case 6:
ns += BigInt(milliseconds) * 1_000_000n;
// fallthrough
case 3:
ns += BigInt(microseconds) * 1_000n;
// fallthrough
}
let e = BigInt(10 ** exponent);
// Split the nanoseconds amount into an integer and its fractional part.
let q = ns / e;
let r = ns % e;
// Pad fractional part, without any leading negative sign, to |exponent| digits.
if (r < 0) {
r = -r;
}
r = String(r).padStart(exponent, "0");
// Return the result as a decimal string.
return `${q}.${r}`;
}
// Only "en" is supported.
const locale = "en";
const numberingSystem = "latn";
const timeSeparator = ":";
let result = [];
let needSeparator = false;
let displayNegativeSign = true;
for (let unit of units) {
// Absent units default to zero.
let value = duration[unit] ?? 0;
let style = options[unit];
let display = options[unit + "Display"];
// NumberFormat requires singular unit names.
let numberFormatUnit = unit.slice(0, -1);
// Compute the matching NumberFormat options.
let nfOpts = Object.create(null);
// Numeric seconds and sub-seconds are combined into a single value.
let done = false;
if (unit === "seconds" || unit === "milliseconds" || unit === "microseconds") {
let nextStyle = options[units[units.indexOf(unit) + 1]];
if (nextStyle === "numeric") {
if (unit === "seconds") {
value = durationToFractional(duration, 9);
} else if (unit === "milliseconds") {
value = durationToFractional(duration, 6);
} else {
value = durationToFractional(duration, 3);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.