diff --git a/harness/testIntl.js b/harness/testIntl.js index 6798d58248f..f7a7036c134 100644 --- a/harness/testIntl.js +++ b/harness/testIntl.js @@ -2509,26 +2509,13 @@ function isCanonicalizedStructurallyValidTimeZoneName(timeZone) { /** * @description Simplified PartitionDurationFormatPattern implementation which * only supports the "en" locale. + * @param {Object} durationFormat the duration format object * @param {Object} duration the duration record - * @param {String} style the duration format style * @result {Array} an array with formatted duration parts */ -function partitionDurationFormatPattern(duration, style = "short") { - const units = [ - "years", - "months", - "weeks", - "days", - "hours", - "minutes", - "seconds", - "milliseconds", - "microseconds", - "nanoseconds", - ]; - - function durationToFractionalSeconds(duration) { +function partitionDurationFormatPattern(durationFormat, duration) { + function durationToFractional(duration, exponent) { let { seconds = 0, milliseconds = 0, @@ -2536,148 +2523,205 @@ function partitionDurationFormatPattern(duration, style = "short") { nanoseconds = 0, } = duration; - // Directly return seconds when no sub-seconds are present. - if (milliseconds === 0 && microseconds === 0 && nanoseconds === 0) { - return seconds; + // 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_sec = BigInt(seconds) * 1_000_000_000n; - let ns_ms = BigInt(milliseconds) * 1_000_000n; - let ns_us = BigInt(microseconds) * 1_000n; - let ns = ns_sec + ns_ms + ns_us + BigInt(nanoseconds); + 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 seconds and sub-seconds. - let q = ns / 1_000_000_000n; - let r = ns % 1_000_000_000n; + // Split the nanoseconds amount into an integer and its fractional part. + let q = ns / e; + let r = ns % e; - // Pad sub-seconds, without any leading negative sign, to nine digits. + // Pad fractional part, without any leading negative sign, to |exponent| digits. if (r < 0) { r = -r; } - r = String(r).padStart(9, "0"); + r = String(r).padStart(exponent, "0"); - // Return seconds with fractional part as a decimal string. + // Return the result as a decimal string. return `${q}.${r}`; } + const units = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", + ]; + + let options = durationFormat.resolvedOptions(); + // Only "en" is supported. const locale = "en"; const numberingSystem = "latn"; const timeSeparator = ":"; let result = []; - let separated = false; + let needSeparator = false; + let displayNegativeSign = true; for (let unit of units) { // Absent units default to zero. let value = duration[unit] ?? 0; - let display = "auto"; - if (style === "digital") { - // Always display numeric units per GetDurationUnitOptions. - if (unit === "hours" || unit === "minutes" || unit === "seconds") { - display = "always"; - } + let style = options[unit]; + let display = options[unit + "Display"]; + + // NumberFormat requires singular unit names. + let numberFormatUnit = unit.slice(0, -1); - // Numeric seconds and sub-seconds are combined into a single value. - if (unit === "seconds") { - value = durationToFractionalSeconds(duration); + // 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); + } + + nfOpts.maximumFractionDigits = options.fractionalDigits ?? 9; + nfOpts.minimumFractionDigits = options.fractionalDigits ?? 0; + nfOpts.roundingMode = "trunc"; + + done = true; } } + // 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") { - // Map the DurationFormat style to a NumberFormat style. - let unitStyle = style; - if (style === "digital") { - if (unit === "hours") { - unitStyle = "numeric"; - } else if (unit === "minutes" || unit === "seconds") { - unitStyle = "2-digit"; - } else { - unitStyle = "short"; + 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"; } - // NumberFormat requires singular unit names. - let numberFormatUnit = unit.slice(0, -1); - - // Compute the matching NumberFormat options. - let nfOpts; - if (unitStyle !== "numeric" && unitStyle !== "2-digit") { - // The value is formatted as a standalone unit. - nfOpts = { - numberingSystem, - style: "unit", - unit: numberFormatUnit, - unitDisplay: unitStyle, - }; - } else { - let roundingMode = undefined; - let minimumFractionDigits = undefined; - let maximumFractionDigits = undefined; - - // Numeric seconds include any sub-seconds. - if (style === "digital" && unit === "seconds") { - roundingMode = "trunc"; - minimumFractionDigits = 0; - maximumFractionDigits = 9; - } + nfOpts.numberingSystem = options.numberingSystem; + + // If the value is formatted as a 2-digit numeric value. + if (style === "2-digit") { + nfOpts.minimumIntegerDigits = 2; + } - // The value is formatted as a numeric unit. - nfOpts = { - numberingSystem, - minimumIntegerDigits: (unitStyle === "2-digit" ? 2 : 1), - roundingMode, - minimumFractionDigits, - maximumFractionDigits, - }; + // If the value is formatted as a standalone unit. + if (style !== "numeric" && style !== "2-digit") { + nfOpts.style = "unit"; + nfOpts.unit = numberFormatUnit; + nfOpts.unitDisplay = style; } let nf = new Intl.NumberFormat(locale, nfOpts); - let formatted = nf.formatToParts(value); + + 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. - let list = []; - for (let {value, type} of formatted) { + for (let {value, type} of parts) { list.push({type, value, unit: numberFormatUnit}); } - if (!separated) { + if (!needSeparator) { // Prepend the separator before the next numeric unit. - if (unitStyle === "2-digit" || unitStyle === "numeric") { - separated = true; + if (style === "2-digit" || style === "numeric") { + needSeparator = true; } // Append the formatted number to |result|. result.push(list); - } else { - let last = result[result.length - 1]; - - // Prepend the time separator before the formatted number. - last.push({ - type: "literal", - value: timeSeparator, - }); - - // Concatenate |last| and the formatted number. - last.push(...list); } - } else { - separated = false; } - // No further units possible after "seconds" when style is "digital". - if (style === "digital" && unit === "seconds") { + if (done) { break; } } + let listStyle = options.style; + if (listStyle === "digital") { + listStyle = "short"; + } + let lf = new Intl.ListFormat(locale, { type: "unit", - style: (style !== "digital" ? style : "short"), + style: listStyle, }); // Collect all formatted units into a list of strings. @@ -2705,11 +2749,12 @@ function partitionDurationFormatPattern(duration, style = "short") { /** * @description Return the formatted string from partitionDurationFormatPattern. + * @param {Object} durationFormat the duration format object * @param {Object} duration the duration record - * @param {String} style the duration format style * @result {String} a string containing the formatted duration */ -function formatDurationFormatPattern(duration, style) { - return partitionDurationFormatPattern(duration, style).reduce((acc, e) => acc + e.value, ""); +function formatDurationFormatPattern(durationFormat, duration) { + let parts = partitionDurationFormatPattern(durationFormat, duration); + return parts.reduce((acc, e) => acc + e.value, ""); } diff --git a/test/intl402/DurationFormat/prototype/format/duration-out-of-range-1.js b/test/intl402/DurationFormat/prototype/format/duration-out-of-range-1.js new file mode 100644 index 00000000000..e96c327210a --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/duration-out-of-range-1.js @@ -0,0 +1,84 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + IsValidDurationRecord rejects too large "years", "months", and "weeks" values. +info: | + Intl.DurationFormat.prototype.format ( duration ) + ... + 3. Let record be ? ToDurationRecord(duration). + ... + + ToDurationRecord ( input ) + ... + 24. If IsValidDurationRecord(result) is false, throw a RangeError exception. + ... + + IsValidDurationRecord ( record ) + ... + 6. If abs(years) ≥ 2^32, return false. + 7. If abs(months) ≥ 2^32, return false. + 8. If abs(weeks) ≥ 2^32, return false. + ... + +features: [Intl.DurationFormat] +---*/ + +const df = new Intl.DurationFormat(); + +const units = [ + "years", + "months", + "weeks", +]; + +const invalidValues = [ + 2**32, + 2**32 + 1, + Number.MAX_SAFE_INTEGER, + Number.MAX_VALUE, +]; + +const validValues = [ + 2**32 - 1, +]; + +for (let unit of units) { + for (let value of invalidValues) { + let positive = {[unit]: value}; + assert.throws( + RangeError, + () => df.format(positive), + `Duration "${unit}" throws when value is ${value}` + ); + + // Also test with flipped sign. + let negative = {[unit]: -value}; + assert.throws( + RangeError, + () => df.format(negative), + `Duration "${unit}" throws when value is ${-value}` + ); + } + + for (let value of validValues) { + // We don't care about the exact contents of the returned string, the call + // just shouldn't throw an exception. + let positive = {[unit]: value}; + assert.sameValue( + typeof df.format(positive), + "string", + `Duration "${unit}" doesn't throw when value is ${value}` + ); + + // Also test with flipped sign. + let negative = {[unit]: -value}; + assert.sameValue( + typeof df.format(negative), + "string", + `Duration "${unit}" doesn't throw when value is ${-value}` + ); + } +} diff --git a/test/intl402/DurationFormat/prototype/format/duration-out-of-range-2.js b/test/intl402/DurationFormat/prototype/format/duration-out-of-range-2.js new file mode 100644 index 00000000000..4b533e9369f --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/duration-out-of-range-2.js @@ -0,0 +1,138 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + IsValidDurationRecord rejects too large "days", "hours", ... values. +info: | + Intl.DurationFormat.prototype.format ( duration ) + ... + 3. Let record be ? ToDurationRecord(duration). + ... + + ToDurationRecord ( input ) + ... + 24. If IsValidDurationRecord(result) is false, throw a RangeError exception. + ... + + IsValidDurationRecord ( record ) + ... + 16. Let normalizedSeconds be days × 86,400 + hours × 3600 + minutes × 60 + seconds + + milliseconds × 10^-3 + microseconds × 10^-6 + nanoseconds × 10^-9. + 17. If abs(normalizedSeconds) ≥ 2^53, return false. + ... + +features: [Intl.DurationFormat] +---*/ + +// Return the next Number value in direction to -Infinity. +function nextDown(num) { + if (!Number.isFinite(num)) { + return num; + } + if (num === 0) { + return -Number.MIN_VALUE; + } + + let f64 = new Float64Array([num]); + let u64 = new BigUint64Array(f64.buffer); + u64[0] += (num < 0 ? 1n : -1n); + return f64[0]; +} + +const df = new Intl.DurationFormat(); + +const invalidValues = { + days: [ + Math.ceil((Number.MAX_SAFE_INTEGER + 1) / 86400), + ], + hours: [ + Math.ceil((Number.MAX_SAFE_INTEGER + 1) / 3600), + ], + minutes: [ + Math.ceil((Number.MAX_SAFE_INTEGER + 1) / 60), + ], + seconds: [ + Number.MAX_SAFE_INTEGER + 1, + ], + milliseconds: [ + (Number.MAX_SAFE_INTEGER + 1) * 1e3, + 9007199254740992_000, + ], + microseconds: [ + (Number.MAX_SAFE_INTEGER + 1) * 1e6, + 9007199254740992_000_000, + ], + nanoseconds: [ + (Number.MAX_SAFE_INTEGER + 1) * 1e9, + 9007199254740992_000_000_000, + ], +}; + +const validValues = { + days: [ + Math.floor(Number.MAX_SAFE_INTEGER / 86400), + ], + hours: [ + Math.floor(Number.MAX_SAFE_INTEGER / 3600), + ], + minutes: [ + Math.floor(Number.MAX_SAFE_INTEGER / 60), + ], + seconds: [ + Number.MAX_SAFE_INTEGER, + ], + milliseconds: [ + Number.MAX_SAFE_INTEGER * 1e3, + nextDown(9007199254740992_000), + ], + microseconds: [ + Number.MAX_SAFE_INTEGER * 1e6, + nextDown(9007199254740992_000_000), + ], + nanoseconds: [ + Number.MAX_SAFE_INTEGER * 1e9, + nextDown(9007199254740992_000_000_000), + ], +}; + +for (let [unit, values] of Object.entries(invalidValues)) { + for (let value of values) { + let positive = {[unit]: value}; + assert.throws( + RangeError, + () => df.format(positive), + `Duration "${unit}" throws when value is ${value}` + ); + + // Also test with flipped sign. + let negative = {[unit]: -value}; + assert.throws( + RangeError, + () => df.format(negative), + `Duration "${unit}" throws when value is ${-value}` + ); + } +} + +for (let [unit, values] of Object.entries(validValues)) { + for (let value of values) { + // We don't care about the exact contents of the returned string, the call + // just shouldn't throw an exception. + let positive = {[unit]: value}; + assert.sameValue( + typeof df.format(positive), + "string", + `Duration "${unit}" doesn't throw when value is ${value}` + ); + + // Also test with flipped sign. + let negative = {[unit]: -value}; + assert.sameValue( + typeof df.format(negative), + "string", + `Duration "${unit}" doesn't throw when value is ${-value}` + ); + } +} diff --git a/test/intl402/DurationFormat/prototype/format/duration-out-of-range-3.js b/test/intl402/DurationFormat/prototype/format/duration-out-of-range-3.js new file mode 100644 index 00000000000..4c5cc0fc5bc --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/duration-out-of-range-3.js @@ -0,0 +1,194 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + IsValidDurationRecord rejects too large time duration units. +info: | + Intl.DurationFormat.prototype.format ( duration ) + ... + 3. Let record be ? ToDurationRecord(duration). + ... + + ToDurationRecord ( input ) + ... + 24. If IsValidDurationRecord(result) is false, throw a RangeError exception. + ... + + IsValidDurationRecord ( record ) + ... + 16. Let normalizedSeconds be days × 86,400 + hours × 3600 + minutes × 60 + seconds + + milliseconds × 10^-3 + microseconds × 10^-6 + nanoseconds × 10^-9. + 17. If abs(normalizedSeconds) ≥ 2^53, return false. + ... + +features: [Intl.DurationFormat] +---*/ + +// Return the next Number value in direction to -Infinity. +function nextDown(num) { + if (!Number.isFinite(num)) { + return num; + } + if (num === 0) { + return -Number.MIN_VALUE; + } + + var f64 = new Float64Array([num]); + var u64 = new BigUint64Array(f64.buffer); + u64[0] += (num < 0 ? 1n : -1n); + return f64[0]; +} + +// Negate |duration| similar to Temporal.Duration.prototype.negated. +function negatedDuration(duration) { + let result = {...duration}; + for (let key of Object.keys(result)) { + // Add +0 to normalize -0 to +0. + result[key] = -result[key] + 0; + } + return result; +} + +function fromNanoseconds(unit, value) { + switch (unit) { + case "days": + return value / (86400n * 1_000_000_000n); + case "hours": + return value / (3600n * 1_000_000_000n); + case "minutes": + return value / (60n * 1_000_000_000n); + case "seconds": + return value / 1_000_000_000n; + case "milliseconds": + return value / 1_000_000n; + case "microseconds": + return value / 1_000n; + case "nanoseconds": + return value; + } + throw new Error("invalid unit:" + unit); +} + +function toNanoseconds(unit, value) { + switch (unit) { + case "days": + return value * 86400n * 1_000_000_000n; + case "hours": + return value * 3600n * 1_000_000_000n; + case "minutes": + return value * 60n * 1_000_000_000n; + case "seconds": + return value * 1_000_000_000n; + case "milliseconds": + return value * 1_000_000n; + case "microseconds": + return value * 1_000n; + case "nanoseconds": + return value; + } + throw new Error("invalid unit:" + unit); +} + +const df = new Intl.DurationFormat(); + +const units = [ + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", +]; + +const zeroDuration = { + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0, + microseconds: 0, + nanoseconds: 0, +}; + +const maxTimeDuration = BigInt(Number.MAX_SAFE_INTEGER) * 1_000_000_000n + 999_999_999n; + +// Iterate over all time duration units and create the largest possible duration. +for (let i = 0; i < units.length; ++i) { + let unit = units[i]; + + // Test not only the next smallest unit, but all smaller units. + for (let j = i + 1; j < units.length; ++j) { + // Maximum duration value for |unit|. + let maxUnit = fromNanoseconds(unit, maxTimeDuration); + + // Adjust |maxUnit| when the value is too large for Number. + let adjusted = BigInt(Number(maxUnit)); + if (adjusted <= maxUnit) { + maxUnit = adjusted; + } else { + maxUnit = BigInt(nextDown(Number(maxUnit))); + } + + // Remaining number of nanoseconds. + let remaining = maxTimeDuration - toNanoseconds(unit, maxUnit); + + // Create the maximum valid duration. + let maxDuration = { + ...zeroDuration, + [unit]: Number(maxUnit), + }; + for (let k = j; k < units.length; ++k) { + let smallerUnit = units[k]; + + // Remaining number of nanoseconds in |smallerUnit|. + let remainingSmallerUnit = fromNanoseconds(smallerUnit, remaining); + maxDuration[smallerUnit] = Number(remainingSmallerUnit); + + remaining -= toNanoseconds(smallerUnit, remainingSmallerUnit); + } + assert.sameValue(remaining, 0n, "zero remaining nanoseconds"); + + // We don't care about the exact contents of the returned string, the call + // just shouldn't throw an exception. + assert.sameValue( + typeof df.format(maxDuration), + "string", + `Duration "${JSON.stringify(maxDuration)}" doesn't throw` + ); + + // Also test with flipped sign. + let minDuration = negatedDuration(maxDuration); + + // We don't care about the exact contents of the returned string, the call + // just shouldn't throw an exception. + assert.sameValue( + typeof df.format(minDuration), + "string", + `Duration "${JSON.stringify(minDuration)}" doesn't throw` + ); + + // Adding a single nanoseconds creates a too large duration. + let tooLargeDuration = { + ...maxDuration, + nanoseconds: maxDuration.nanoseconds + 1, + }; + + assert.throws( + RangeError, + () => df.format(tooLargeDuration), + `Duration "${JSON.stringify(tooLargeDuration)}" throws` + ); + + // Also test with flipped sign. + let tooSmallDuration = negatedDuration(tooLargeDuration); + + assert.throws( + RangeError, + () => df.format(tooSmallDuration), + `Duration "${JSON.stringify(tooSmallDuration)}" throws` + ); + } +} diff --git a/test/intl402/DurationFormat/prototype/format/duration-out-of-range-4.js b/test/intl402/DurationFormat/prototype/format/duration-out-of-range-4.js new file mode 100644 index 00000000000..80ee3782e89 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/duration-out-of-range-4.js @@ -0,0 +1,63 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + IsValidDurationRecord rejects too large time duration units. +info: | + Intl.DurationFormat.prototype.format ( duration ) + ... + 3. Let record be ? ToDurationRecord(duration). + ... + + ToDurationRecord ( input ) + ... + 24. If IsValidDurationRecord(result) is false, throw a RangeError exception. + ... + + IsValidDurationRecord ( record ) + ... + 16. Let normalizedSeconds be days × 86,400 + hours × 3600 + minutes × 60 + seconds + + milliseconds × 10^-3 + microseconds × 10^-6 + nanoseconds × 10^-9. + 17. If abs(normalizedSeconds) ≥ 2^53, return false. + ... + +features: [Intl.DurationFormat] +---*/ + +const df = new Intl.DurationFormat(); + +const duration = { + // Actual value is: 4503599627370497024 + milliseconds: 4503599627370497_000, + + // Actual value is: 4503599627370494951424 + microseconds: 4503599627370495_000000, +}; + +// The naive approach to compute the duration seconds leads to an incorrect result. +let durationSecondsNaive = Math.trunc(duration.milliseconds / 1e3 + duration.microseconds / 1e6); +assert.sameValue( + Number.isSafeInteger(durationSecondsNaive), + false, + "Naive approach incorrectly computes duration seconds as out-of-range" +); + +// The exact approach to compute the duration seconds leads to the correct result. +let durationSecondsExact = Number(BigInt(duration.milliseconds) / 1_000n) + + Number(BigInt(duration.microseconds) / 1_000_000n) + + Math.trunc(((duration.milliseconds % 1e3) * 1e3 + (duration.microseconds % 1e6)) / 1e6); +assert.sameValue( + Number.isSafeInteger(Number(durationSecondsExact)), + true, + "Exact approach correctly computes duration seconds as in-range" +); + +// We don't care about the exact contents of the returned string, the call +// just shouldn't throw an exception. +assert.sameValue( + typeof df.format(duration), + "string", + `Duration "${JSON.stringify(duration)}" doesn't throw` +); diff --git a/test/intl402/DurationFormat/prototype/format/negative-duration-style-default-en.js b/test/intl402/DurationFormat/prototype/format/negative-duration-style-default-en.js index 955d9f08ee9..338fb962b8f 100644 --- a/test/intl402/DurationFormat/prototype/format/negative-duration-style-default-en.js +++ b/test/intl402/DurationFormat/prototype/format/negative-duration-style-default-en.js @@ -23,9 +23,10 @@ const duration = { nanoseconds: -9, }; -const expected = formatDurationFormatPattern(duration); - const df = new Intl.DurationFormat("en"); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue( df.format(duration), expected, diff --git a/test/intl402/DurationFormat/prototype/format/negative-duration-style-short-en.js b/test/intl402/DurationFormat/prototype/format/negative-duration-style-short-en.js index f92540e6a54..8ebdcd3e705 100644 --- a/test/intl402/DurationFormat/prototype/format/negative-duration-style-short-en.js +++ b/test/intl402/DurationFormat/prototype/format/negative-duration-style-short-en.js @@ -25,9 +25,10 @@ const duration = { nanoseconds: -9, }; -const expected = formatDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", {style}); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue( df.format(duration), expected, diff --git a/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-default-en.js b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-default-en.js new file mode 100644 index 00000000000..531bda46a4d --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-default-en.js @@ -0,0 +1,26 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + Test format method with negative duration and leading zero using the default style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {hoursDisplay: "always"}); + +const expected = formatDurationFormatPattern(df, duration); + +assert.sameValue( + df.format(duration), + expected, + `DurationFormat format output using default style option` +); diff --git a/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-digital-en.js b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-digital-en.js new file mode 100644 index 00000000000..caaf42179d8 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-digital-en.js @@ -0,0 +1,28 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + Test format method with negative duration and leading zero using the "digital" style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +const style = "digital"; + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {style, hoursDisplay: "always"}); + +const expected = formatDurationFormatPattern(df, duration); + +assert.sameValue( + df.format(duration), + expected, + `DurationFormat format output using ${style} style option` +); diff --git a/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-long-en.js b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-long-en.js new file mode 100644 index 00000000000..35a80e94865 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-long-en.js @@ -0,0 +1,28 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + Test format method with negative duration and leading zero using the "long" style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +const style = "long"; + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {style, hoursDisplay: "always"}); + +const expected = formatDurationFormatPattern(df, duration); + +assert.sameValue( + df.format(duration), + expected, + `DurationFormat format output using ${style} style option` +); diff --git a/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-narrow-en.js b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-narrow-en.js new file mode 100644 index 00000000000..c5e7924ef93 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-narrow-en.js @@ -0,0 +1,28 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + Test format method with negative duration and leading zero using the "narrow" style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +const style = "narrow"; + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {style, hoursDisplay: "always"}); + +const expected = formatDurationFormatPattern(df, duration); + +assert.sameValue( + df.format(duration), + expected, + `DurationFormat format output using ${style} style option` +); diff --git a/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-short-en.js b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-short-en.js new file mode 100644 index 00000000000..898063ac7c8 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/negative-duration-with-leading-zero-style-short-en.js @@ -0,0 +1,28 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + Test format method with negative duration and leading zero using the "short" style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +const style = "short"; + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {style, hoursDisplay: "always"}); + +const expected = formatDurationFormatPattern(df, duration); + +assert.sameValue( + df.format(duration), + expected, + `DurationFormat format output using ${style} style option` +); diff --git a/test/intl402/DurationFormat/prototype/format/negative-durationstyle-digital-en.js b/test/intl402/DurationFormat/prototype/format/negative-durationstyle-digital-en.js index 8c90ea49423..07d984eec64 100644 --- a/test/intl402/DurationFormat/prototype/format/negative-durationstyle-digital-en.js +++ b/test/intl402/DurationFormat/prototype/format/negative-durationstyle-digital-en.js @@ -25,9 +25,10 @@ const duration = { nanoseconds: -9, }; -const expected = formatDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", {style}); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue( df.format(duration), expected, diff --git a/test/intl402/DurationFormat/prototype/format/negative-durationstyle-long-en.js b/test/intl402/DurationFormat/prototype/format/negative-durationstyle-long-en.js index 97f577caf61..1831b383b05 100644 --- a/test/intl402/DurationFormat/prototype/format/negative-durationstyle-long-en.js +++ b/test/intl402/DurationFormat/prototype/format/negative-durationstyle-long-en.js @@ -25,9 +25,10 @@ const duration = { nanoseconds: -9, }; -const expected = formatDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", {style}); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue( df.format(duration), expected, diff --git a/test/intl402/DurationFormat/prototype/format/negative-durationstyle-narrow-en.js b/test/intl402/DurationFormat/prototype/format/negative-durationstyle-narrow-en.js index 49dd23792d6..f9a6bbd3f72 100644 --- a/test/intl402/DurationFormat/prototype/format/negative-durationstyle-narrow-en.js +++ b/test/intl402/DurationFormat/prototype/format/negative-durationstyle-narrow-en.js @@ -25,9 +25,10 @@ const duration = { nanoseconds: -9, }; -const expected = formatDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", {style}); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue( df.format(duration), expected, diff --git a/test/intl402/DurationFormat/prototype/format/negative-zero.js b/test/intl402/DurationFormat/prototype/format/negative-zero.js new file mode 100644 index 00000000000..85e84dc2803 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/negative-zero.js @@ -0,0 +1,58 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + Test format method with negative zero as the input. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +const units = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", +]; + +for (let unit of units) { + let positiveZero = { + [unit]: +0, + }; + + let negativeZero = { + [unit]: -0, + }; + + let auto = new Intl.DurationFormat("en", {[unit + "Display"]: "auto"}); + + assert.sameValue( + auto.format(positiveZero), + "", + `+0 ${unit} is the empty string when display is "auto"` + ); + + assert.sameValue( + auto.format(negativeZero), + "", + `-0 ${unit} is the empty string when display is "auto"` + ); + + let always = new Intl.DurationFormat("en", {[unit + "Display"]: "always"}); + + let expected = formatDurationFormatPattern(always, positiveZero); + + assert.sameValue( + always.format(negativeZero), + expected, + `-0 ${unit} produces the same output as +0 ${unit} when display is "always"` + ); +} diff --git a/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds-with-auto-display-and-zero-fractional.js b/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds-with-auto-display-and-zero-fractional.js new file mode 100644 index 00000000000..b26d8a13d08 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds-with-auto-display-and-zero-fractional.js @@ -0,0 +1,55 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + The correct separator is used for numeric hours with zero minutes and non-zero (sub-)seconds. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +const df = new Intl.DurationFormat("en", { + // hours must be numeric, so that a time separator is used for the following units. + hours: "numeric", + + // Use "auto" display for sub-hours units. + minutesDisplay: "auto", + secondsDisplay: "auto", + + // Zero fractional digits to omit sub-second units. + fractionalDigits: 0, +}); + +const durations = [ + // Test all eight possible combinations for zero and non-zero hours, minutes, + // and seconds. + {hours: 0, minutes: 0, seconds: 0}, + {hours: 0, minutes: 0, seconds: 1}, + {hours: 0, minutes: 1, seconds: 0}, + {hours: 0, minutes: 1, seconds: 1}, + {hours: 1, minutes: 0, seconds: 0}, + {hours: 1, minutes: 0, seconds: 1}, + {hours: 1, minutes: 1, seconds: 0}, + {hours: 1, minutes: 1, seconds: 1}, + + // Additionally test when hours is non-zero and a sub-seconds unit is non-zero, + // but minutes and seconds are both zero. + // + // Note: The "fractionalDigits" option is not taken into account when computing + // whether the seconds unit should appear in the output. + {hours: 1, minutes: 0, seconds: 0, milliseconds: 1}, + {hours: 1, minutes: 0, seconds: 0, microseconds: 1}, + {hours: 1, minutes: 0, seconds: 0, nanoseconds: 1}, +]; + +for (const duration of durations) { + const expected = formatDurationFormatPattern(df, duration); + + assert.sameValue( + df.format(duration), + expected, + `Duration is ${JSON.stringify(duration)}` + ); +} diff --git a/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds-with-auto-display.js b/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds-with-auto-display.js new file mode 100644 index 00000000000..9e45b22e829 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds-with-auto-display.js @@ -0,0 +1,49 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.format +description: > + The correct separator is used for numeric hours with zero minutes and non-zero (sub-)seconds. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +const df = new Intl.DurationFormat("en", { + // hours must be numeric, so that a time separator is used for the following units. + hours: "numeric", + + // Use "auto" display for sub-hours units. + minutesDisplay: "auto", + secondsDisplay: "auto", +}); + +const durations = [ + // Test all eight possible combinations for zero and non-zero hours, minutes, + // and seconds. + {hours: 0, minutes: 0, seconds: 0}, + {hours: 0, minutes: 0, seconds: 1}, + {hours: 0, minutes: 1, seconds: 0}, + {hours: 0, minutes: 1, seconds: 1}, + {hours: 1, minutes: 0, seconds: 0}, + {hours: 1, minutes: 0, seconds: 1}, + {hours: 1, minutes: 1, seconds: 0}, + {hours: 1, minutes: 1, seconds: 1}, + + // Additionally test when hours is non-zero and a sub-seconds unit is non-zero, + // but minutes and seconds are both zero. + {hours: 1, minutes: 0, seconds: 0, milliseconds: 1}, + {hours: 1, minutes: 0, seconds: 0, microseconds: 1}, + {hours: 1, minutes: 0, seconds: 0, nanoseconds: 1}, +]; + +for (const duration of durations) { + const expected = formatDurationFormatPattern(df, duration); + + assert.sameValue( + df.format(duration), + expected, + `Duration is ${JSON.stringify(duration)}` + ); +} diff --git a/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds.js b/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds.js index 81546854eb0..6ea7ba6c4f2 100644 --- a/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds.js +++ b/test/intl402/DurationFormat/prototype/format/numeric-hour-with-zero-minutes-and-non-zero-seconds.js @@ -4,8 +4,9 @@ /*--- esid: sec-Intl.DurationFormat.prototype.format description: > - The correct separator is used for numeric hours with zero minutes and non-zero seconds. + The correct separator is used for numeric hours with zero minutes and non-zero (sub-)seconds. locale: [en-US] +includes: [testIntl.js] features: [Intl.DurationFormat] ---*/ @@ -14,28 +15,31 @@ const df = new Intl.DurationFormat("en", { hours: "numeric", }); -const lf = new Intl.ListFormat("en", { - type: "unit", - style: "short", -}); - -const duration = { - hours: 1, - - // Minutes is omitted from the output when its value is zero. - minutes: 0, - - // Either seconds or sub-seconds must be non-zero. - seconds: 3, -}; - -const expected = lf.format([ - new Intl.NumberFormat("en", {minimumIntegerDigits: 1}).format(duration.hours), - new Intl.NumberFormat("en", {minimumIntegerDigits: 2}).format(duration.seconds), -]); - -assert.sameValue( - df.format(duration), - expected, - `No time separator is used when minutes is zero` -); +const durations = [ + // Test all eight possible combinations for zero and non-zero hours, minutes, + // and seconds. + {hours: 0, minutes: 0, seconds: 0}, + {hours: 0, minutes: 0, seconds: 1}, + {hours: 0, minutes: 1, seconds: 0}, + {hours: 0, minutes: 1, seconds: 1}, + {hours: 1, minutes: 0, seconds: 0}, + {hours: 1, minutes: 0, seconds: 1}, + {hours: 1, minutes: 1, seconds: 0}, + {hours: 1, minutes: 1, seconds: 1}, + + // Additionally test when hours is non-zero and a sub-seconds unit is non-zero, + // but minutes and seconds are both zero. + {hours: 1, minutes: 0, seconds: 0, milliseconds: 1}, + {hours: 1, minutes: 0, seconds: 0, microseconds: 1}, + {hours: 1, minutes: 0, seconds: 0, nanoseconds: 1}, +]; + +for (const duration of durations) { + const expected = formatDurationFormatPattern(df, duration); + + assert.sameValue( + df.format(duration), + expected, + `Duration is ${JSON.stringify(duration)}` + ); +} diff --git a/test/intl402/DurationFormat/prototype/format/precision-exact-mathematical-values.js b/test/intl402/DurationFormat/prototype/format/precision-exact-mathematical-values.js index dab01d7dddc..00404ad5a63 100644 --- a/test/intl402/DurationFormat/prototype/format/precision-exact-mathematical-values.js +++ b/test/intl402/DurationFormat/prototype/format/precision-exact-mathematical-values.js @@ -37,21 +37,6 @@ const durations = [ nanoseconds: 1, }, - // 9007199254740991 + (9007199254740991 / 10^3) + (9007199254740991 / 10^6) + (9007199254740991 / 10^9) - // = 9.016215470202185986731991 × 10^15 - { - seconds: Number.MAX_SAFE_INTEGER, - milliseconds: Number.MAX_SAFE_INTEGER, - microseconds: Number.MAX_SAFE_INTEGER, - nanoseconds: Number.MAX_SAFE_INTEGER, - }, - { - seconds: Number.MIN_SAFE_INTEGER, - milliseconds: Number.MIN_SAFE_INTEGER, - microseconds: Number.MIN_SAFE_INTEGER, - nanoseconds: Number.MIN_SAFE_INTEGER, - }, - // 1 + (2 / 10^3) + (3 / 10^6) + (9007199254740991 / 10^9) // = 9.007200256743991 × 10^6 { @@ -61,30 +46,22 @@ const durations = [ nanoseconds: Number.MAX_SAFE_INTEGER, }, - // 9007199254740991 + (10^3 / 10^3) + (10^6 / 10^6) + (10^9 / 10^9) - // = 9007199254740991 + 3 - // = 9007199254740994 + // (4503599627370497024 / 10^3) + (4503599627370494951424 / 10^6) + // = 4503599627370497.024 + 4503599627370494.951424 + // = 9007199254740991.975424 { - seconds: Number.MAX_SAFE_INTEGER, - milliseconds: 10 ** 3, - microseconds: 10 ** 6, - nanoseconds: 10 ** 9, - }, + // Actual value is: 4503599627370497024 + milliseconds: 4503599627370497_000, - // ~1.7976931348623157e+308 / 10^9 - // = ~1.7976931348623157 × 10^299 - { - seconds: 0, - milliseconds: 0, - microseconds: 0, - nanoseconds: Number.MAX_VALUE, + // Actual value is: 4503599627370494951424 + microseconds: 4503599627370495_000000, }, ]; const df = new Intl.DurationFormat("en", {style: "digital"}); for (let duration of durations) { - let expected = formatDurationFormatPattern(duration, "digital"); + let expected = formatDurationFormatPattern(df, duration); assert.sameValue( df.format(duration), expected, diff --git a/test/intl402/DurationFormat/prototype/format/style-default-en.js b/test/intl402/DurationFormat/prototype/format/style-default-en.js index 7c2966f34df..18331a27f75 100644 --- a/test/intl402/DurationFormat/prototype/format/style-default-en.js +++ b/test/intl402/DurationFormat/prototype/format/style-default-en.js @@ -22,7 +22,8 @@ const duration = { nanoseconds: 9, }; -const expected = formatDurationFormatPattern(duration); - const df = new Intl.DurationFormat("en"); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using default style option`); diff --git a/test/intl402/DurationFormat/prototype/format/style-digital-en.js b/test/intl402/DurationFormat/prototype/format/style-digital-en.js index 8bb77a63883..2ceccc403b8 100644 --- a/test/intl402/DurationFormat/prototype/format/style-digital-en.js +++ b/test/intl402/DurationFormat/prototype/format/style-digital-en.js @@ -25,7 +25,8 @@ const duration = { nanoseconds: 9, }; -const expected = formatDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", {style}); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using ${style} style option`); diff --git a/test/intl402/DurationFormat/prototype/format/style-digital-fractionalDigits-undefined.js b/test/intl402/DurationFormat/prototype/format/style-digital-fractionalDigits-undefined.js index 26e449586e1..714c0302368 100644 --- a/test/intl402/DurationFormat/prototype/format/style-digital-fractionalDigits-undefined.js +++ b/test/intl402/DurationFormat/prototype/format/style-digital-fractionalDigits-undefined.js @@ -34,7 +34,7 @@ const durationMicro = { microseconds: 222 }; -const durationMill = { +const durationMilli = { hours: 1, minutes: 22, seconds: 33, @@ -47,6 +47,14 @@ const durationNoSubsecond = { seconds: 33 }; +const durationFiveFractional = { + hours: 2, + minutes: 30, + seconds: 10, + milliseconds: 111, + microseconds: 220, +}; + const durationSevenFractional = { hours: 2, minutes: 30, @@ -64,5 +72,5 @@ assert.sameValue(df.format(durationMicro), "1:22:33.111222", `format output with assert.sameValue(df.format(durationMilli), "1:22:33.111", `format output with millisecond digits and fractionalDigits: undefined using ${style} style option`); assert.sameValue(df.format(durationNoSubsecond), "1:22:33", `format output with no subsecond digits and fractionalDigits: undefined using ${style} style option`); -assert.sameValue(df.format(durationFiveFractional), "2:30:11122", `format output with five subsecond digits and fractionalDigits: undefined using ${style} style option`); -assert.sameValue(df.format(durationSevenFractional), "2:30:1112203", `format output with seven subsecond digits and fractionalDigits: undefined using ${style} style option`); +assert.sameValue(df.format(durationFiveFractional), "2:30:10.11122", `format output with five subsecond digits and fractionalDigits: undefined using ${style} style option`); +assert.sameValue(df.format(durationSevenFractional), "2:30:10.1112203", `format output with seven subsecond digits and fractionalDigits: undefined using ${style} style option`); diff --git a/test/intl402/DurationFormat/prototype/format/style-long-en.js b/test/intl402/DurationFormat/prototype/format/style-long-en.js index 19209f767ba..4bd61b122dc 100644 --- a/test/intl402/DurationFormat/prototype/format/style-long-en.js +++ b/test/intl402/DurationFormat/prototype/format/style-long-en.js @@ -24,7 +24,8 @@ const duration = { nanoseconds: 9, }; -const expected = formatDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", {style}); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using ${style} style option`); diff --git a/test/intl402/DurationFormat/prototype/format/style-narrow-en.js b/test/intl402/DurationFormat/prototype/format/style-narrow-en.js index b0b06c1312b..060d6b5b20e 100644 --- a/test/intl402/DurationFormat/prototype/format/style-narrow-en.js +++ b/test/intl402/DurationFormat/prototype/format/style-narrow-en.js @@ -24,7 +24,8 @@ const duration = { nanoseconds: 9, }; -const expected = formatDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", {style}); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using ${style} style option`); diff --git a/test/intl402/DurationFormat/prototype/format/style-short-en.js b/test/intl402/DurationFormat/prototype/format/style-short-en.js index 5ba8ce4a2f7..a83b35fd51c 100644 --- a/test/intl402/DurationFormat/prototype/format/style-short-en.js +++ b/test/intl402/DurationFormat/prototype/format/style-short-en.js @@ -24,7 +24,8 @@ const duration = { nanoseconds: 9, }; -const expected = formatDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", {style}); + +const expected = formatDurationFormatPattern(df, duration); + assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using ${style} style option`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-default-en.js b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-default-en.js index 1e0e41ca904..917f6787add 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-default-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-default-en.js @@ -38,7 +38,8 @@ const duration = { nanoseconds: 789, }; -const expected = partitionDurationFormatPattern(duration); +const df = new Intl.DurationFormat('en'); + +const expected = partitionDurationFormatPattern(df, duration); -let df = new Intl.DurationFormat('en'); compare(df.formatToParts(duration), expected, `Using style : default`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-digital-en.js b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-digital-en.js index 3b55eaf6772..58a7faecbf6 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-digital-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-digital-en.js @@ -40,7 +40,8 @@ const duration = { const style = "digital"; -const expected = partitionDurationFormatPattern(duration, style); +const df = new Intl.DurationFormat('en', { style }); + +const expected = partitionDurationFormatPattern(df, duration); -let df = new Intl.DurationFormat('en', { style }); compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-long-en.js b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-long-en.js index b23d3180d1a..8da5ed6e892 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-long-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-long-en.js @@ -40,7 +40,8 @@ const duration = { const style = "long"; -const expected = partitionDurationFormatPattern(duration, style); +const df = new Intl.DurationFormat('en', { style }); + +const expected = partitionDurationFormatPattern(df, duration); -let df = new Intl.DurationFormat('en', { style }); compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-narrow-en.js b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-narrow-en.js index 5540ccab216..6c32f3f9849 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-narrow-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-narrow-en.js @@ -40,7 +40,8 @@ const duration = { const style = "narrow"; -const expected = partitionDurationFormatPattern(duration, style); +const df = new Intl.DurationFormat('en', { style }); + +const expected = partitionDurationFormatPattern(df, duration); -let df = new Intl.DurationFormat('en', { style }); compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-short-en.js b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-short-en.js index bde0bf60b9f..616735c06bd 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-short-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/formatToParts-style-short-en.js @@ -40,7 +40,8 @@ const duration = { const style = "short"; -const expected = partitionDurationFormatPattern(duration, style); +const df = new Intl.DurationFormat('en', { style }); + +const expected = partitionDurationFormatPattern(df, duration); -let df = new Intl.DurationFormat('en', { style }); compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-default-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-default-en.js index 5c4a230c913..50e2752a0c7 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-default-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-default-en.js @@ -41,7 +41,8 @@ const duration = { nanoseconds: -789, }; -const expected = partitionDurationFormatPattern(duration); - const df = new Intl.DurationFormat("en"); + +const expected = partitionDurationFormatPattern(df, duration); + compare(df.formatToParts(duration), expected, `Using style : default`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-digital-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-digital-en.js index dc42b6591bc..9493f2ebbb5 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-digital-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-digital-en.js @@ -43,7 +43,8 @@ const duration = { nanoseconds: -789, }; -const expected = partitionDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", { style }); + +const expected = partitionDurationFormatPattern(df, duration); + compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-long-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-long-en.js index 58f16514e75..e8540a5cccc 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-long-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-long-en.js @@ -43,7 +43,8 @@ const duration = { nanoseconds: -789, }; -const expected = partitionDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", { style }); + +const expected = partitionDurationFormatPattern(df, duration); + compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-narrow-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-narrow-en.js index 8aca8b85680..9451567378e 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-narrow-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-narrow-en.js @@ -43,7 +43,8 @@ const duration = { nanoseconds: -789, }; -const expected = partitionDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", { style }); + +const expected = partitionDurationFormatPattern(df, duration); + compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-short-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-short-en.js index 44d1b75e3cb..c87c7a9bdb4 100644 --- a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-short-en.js +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-formatToParts-style-short-en.js @@ -43,7 +43,8 @@ const duration = { nanoseconds: -789, }; -const expected = partitionDurationFormatPattern(duration, style); - const df = new Intl.DurationFormat("en", { style }); + +const expected = partitionDurationFormatPattern(df, duration); + compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-default-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-default-en.js new file mode 100644 index 00000000000..629b478df78 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-default-en.js @@ -0,0 +1,40 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.formatToParts +description: > + Test formatToParts method with negative duration and leading zero using the default style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +function compare(actual, expected, message) { + assert.sameValue(Array.isArray(expected), true, `${message}: expected is Array`); + assert.sameValue(Array.isArray(actual), true, `${message}: actual is Array`); + assert.sameValue(actual.length, expected.length, `${message}: length`); + + for (let i = 0; i < expected.length; ++i) { + let actualEntry = actual[i]; + let expectedEntry = expected[i]; + + assert.sameValue(actualEntry.type, expectedEntry.type, `type for entry ${i}`); + assert.sameValue(actualEntry.value, expectedEntry.value, `value for entry ${i}`); + assert.sameValue("unit" in actualEntry, "unit" in expectedEntry, `unit for entry ${i}`); + if ("unit" in expectedEntry) { + assert.sameValue(actualEntry.unit, expectedEntry.unit, `unit for entry ${i}`); + } + } +} + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {hoursDisplay: "always"}); + +const expected = partitionDurationFormatPattern(df, duration); + +compare(df.formatToParts(duration), expected, `Using style : default`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-digital-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-digital-en.js new file mode 100644 index 00000000000..f97268ec725 --- /dev/null +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-digital-en.js @@ -0,0 +1,42 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.formatToParts +description: > + Test formatToParts method with negative duration and leading zero using the "digital" style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +function compare(actual, expected, message) { + assert.sameValue(Array.isArray(expected), true, `${message}: expected is Array`); + assert.sameValue(Array.isArray(actual), true, `${message}: actual is Array`); + assert.sameValue(actual.length, expected.length, `${message}: length`); + + for (let i = 0; i < expected.length; ++i) { + let actualEntry = actual[i]; + let expectedEntry = expected[i]; + + assert.sameValue(actualEntry.type, expectedEntry.type, `type for entry ${i}`); + assert.sameValue(actualEntry.value, expectedEntry.value, `value for entry ${i}`); + assert.sameValue("unit" in actualEntry, "unit" in expectedEntry, `unit for entry ${i}`); + if ("unit" in expectedEntry) { + assert.sameValue(actualEntry.unit, expectedEntry.unit, `unit for entry ${i}`); + } + } +} + +const style = "digital"; + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {style, hoursDisplay: "always"}); + +const expected = partitionDurationFormatPattern(df, duration); + +compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-long-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-long-en.js new file mode 100644 index 00000000000..cca1f06794a --- /dev/null +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-long-en.js @@ -0,0 +1,42 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.formatToParts +description: > + Test formatToParts method with negative duration and leading zero using the "long" style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +function compare(actual, expected, message) { + assert.sameValue(Array.isArray(expected), true, `${message}: expected is Array`); + assert.sameValue(Array.isArray(actual), true, `${message}: actual is Array`); + assert.sameValue(actual.length, expected.length, `${message}: length`); + + for (let i = 0; i < expected.length; ++i) { + let actualEntry = actual[i]; + let expectedEntry = expected[i]; + + assert.sameValue(actualEntry.type, expectedEntry.type, `type for entry ${i}`); + assert.sameValue(actualEntry.value, expectedEntry.value, `value for entry ${i}`); + assert.sameValue("unit" in actualEntry, "unit" in expectedEntry, `unit for entry ${i}`); + if ("unit" in expectedEntry) { + assert.sameValue(actualEntry.unit, expectedEntry.unit, `unit for entry ${i}`); + } + } +} + +const style = "long"; + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {style, hoursDisplay: "always"}); + +const expected = partitionDurationFormatPattern(df, duration); + +compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-narrow-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-narrow-en.js new file mode 100644 index 00000000000..a654bf17d1e --- /dev/null +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-narrow-en.js @@ -0,0 +1,42 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.formatToParts +description: > + Test formatToParts method with negative duration and leading zero using the "narrow" style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +function compare(actual, expected, message) { + assert.sameValue(Array.isArray(expected), true, `${message}: expected is Array`); + assert.sameValue(Array.isArray(actual), true, `${message}: actual is Array`); + assert.sameValue(actual.length, expected.length, `${message}: length`); + + for (let i = 0; i < expected.length; ++i) { + let actualEntry = actual[i]; + let expectedEntry = expected[i]; + + assert.sameValue(actualEntry.type, expectedEntry.type, `type for entry ${i}`); + assert.sameValue(actualEntry.value, expectedEntry.value, `value for entry ${i}`); + assert.sameValue("unit" in actualEntry, "unit" in expectedEntry, `unit for entry ${i}`); + if ("unit" in expectedEntry) { + assert.sameValue(actualEntry.unit, expectedEntry.unit, `unit for entry ${i}`); + } + } +} + +const style = "narrow"; + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {style, hoursDisplay: "always"}); + +const expected = partitionDurationFormatPattern(df, duration); + +compare(df.formatToParts(duration), expected, `Using style : ${style}`); diff --git a/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-short-en.js b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-short-en.js new file mode 100644 index 00000000000..a59eced308b --- /dev/null +++ b/test/intl402/DurationFormat/prototype/formatToParts/negative-duration-with-leading-zero-style-short-en.js @@ -0,0 +1,42 @@ +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-Intl.DurationFormat.prototype.formatToParts +description: > + Test formatToParts method with negative duration and leading zero using the "short" style. +locale: [en-US] +includes: [testIntl.js] +features: [Intl.DurationFormat] +---*/ + +function compare(actual, expected, message) { + assert.sameValue(Array.isArray(expected), true, `${message}: expected is Array`); + assert.sameValue(Array.isArray(actual), true, `${message}: actual is Array`); + assert.sameValue(actual.length, expected.length, `${message}: length`); + + for (let i = 0; i < expected.length; ++i) { + let actualEntry = actual[i]; + let expectedEntry = expected[i]; + + assert.sameValue(actualEntry.type, expectedEntry.type, `type for entry ${i}`); + assert.sameValue(actualEntry.value, expectedEntry.value, `value for entry ${i}`); + assert.sameValue("unit" in actualEntry, "unit" in expectedEntry, `unit for entry ${i}`); + if ("unit" in expectedEntry) { + assert.sameValue(actualEntry.unit, expectedEntry.unit, `unit for entry ${i}`); + } + } +} + +const style = "short"; + +const duration = { + hours: 0, + seconds: -1, +}; + +const df = new Intl.DurationFormat("en", {style, hoursDisplay: "always"}); + +const expected = partitionDurationFormatPattern(df, duration); + +compare(df.formatToParts(duration), expected, `Using style : ${style}`);