// 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];
} elseif (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);
} elseif (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)) { thrownew Test262Error("Option property " + property + " is returned, but shouldn't be.");
}
} else {
actual = obj.resolvedOptions()[property]; if (isILD) { if (actual !== undefined && values.indexOf(actual) === -1) { thrownew Test262Error("Invalid value " + actual + " returned for property " + property + ".");
}
} else { if (type === "boolean") {
expected = Boolean(value);
} elseif (type === "string") {
expected = String(value);
} if (actual !== expected && !(isOptional && actual === undefined)) { thrownew 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) { thrownew Test262Error("Invalid option value " + value + " for property " + property + " was not rejected.");
} elseif (error.name !== "RangeError") { thrownew 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) { thrownew Test262Error("Option fallback value " + fallback + " for property " + property + " was not used; got " + actual + " instead.");
}
} else { if (values.indexOf(actual) === -1 && !(isILD && actual === undefined)) { thrownew 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]) { thrownew 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) { thrownew 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) { thrownew 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) { thrownew 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);
} elseif (unit === "milliseconds") {
value = durationToFractional(duration, 6);
} else {
value = durationToFractional(duration, 3);
}
// Display zero numeric minutes when seconds will be displayed.
let displayRequired = false; if (unit === "minutes" && needSeparator) {
displayRequired = options.secondsDisplay === "always" ||
(duration.seconds ?? 0) !== 0 ||
(duration.milliseconds ?? 0) !== 0 ||
(duration.microseconds ?? 0) !== 0 ||
(duration.nanoseconds ?? 0) !== 0;
}
// "auto" display omits zero units. if (value !== 0 || display !== "auto" || displayRequired) { // Display only the first negative value. if (displayNegativeSign) {
displayNegativeSign = false;
// Set to negative zero to ensure the sign is displayed. if (value === 0) {
let negative = units.some(unit => (duration[unit] ?? 0) < 0); if (negative) {
value = -0;
}
}
} else {
nfOpts.signDisplay = "never";
}
nfOpts.numberingSystem = options.numberingSystem;
// If the value is formatted as a 2-digit numeric value. if (style === "2-digit") {
nfOpts.minimumIntegerDigits = 2;
}
// If the value is formatted as a standalone unit. if (style !== "numeric" && style !== "2-digit") {
nfOpts.style = "unit";
nfOpts.unit = numberFormatUnit;
nfOpts.unitDisplay = style;
} else {
nfOpts.useGrouping = false;
}
let nf = new Intl.NumberFormat(locale, nfOpts);
let list; if (!needSeparator) {
list = [];
} else {
list = result[result.length - 1];
// Prepend the time separator before the formatted number.
list.push({
type: "literal",
value: timeSeparator,
});
}
// Format the numeric value.
let parts = nf.formatToParts(value);
// Add |numberFormatUnit| to the formatted number. for (let {value, type} of parts) {
list.push({type, value, unit: numberFormatUnit});
}
if (!needSeparator) { // Prepend the separator before the next numeric unit. if (style === "2-digit" || style === "numeric") {
needSeparator = true;
}
// Append the formatted number to |result|.
result.push(list);
}
}
if (done) { break;
}
}
let listStyle = options.style; if (listStyle === "digital") {
listStyle = "short";
}
let lf = new Intl.ListFormat(locale, {
type: "unit",
style: listStyle,
});
// Collect all formatted units into a list of strings.
let strings = []; for (let parts of result) {
let string = ""; for (let {value} of parts) {
string += value;
}
strings.push(string);
}
// Format the list of strings and compute the overall result.
let flattened = []; for (let {type, value} of lf.formatToParts(strings)) { if (type === "element") {
flattened.push(...result.shift());
} else {
flattened.push({type, value});
}
} return flattened;
}
/** * @description Return the formatted string from partitionDurationFormatPattern. * @param {Object} durationFormat the duration format object * @param {Object} duration the duration record * @result {String} a string containing the formatted duration
*/
function formatDurationFormatPattern(durationFormat, duration) {
let parts = partitionDurationFormatPattern(durationFormat, duration); return parts.reduce((acc, e) => acc + e.value, "");
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.43 Sekunden
(vorverarbeitet am 2026-05-16)
¤
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.