mirror of
https://github.com/tc39/test262.git
synced 2025-04-08 19:35:28 +02:00
Sync partitionDurationFormatPattern with latest spec draft
Sync `partitionDurationFormatPattern` with the latest spec draft and change it to use an `Intl.DurationFormat` object as the input, so it's easier to compare it against the spec text and because it allows to test more inputs. Includes the fixes for: - https://github.com/tc39/proposal-intl-duration-format/pull/183 - https://github.com/tc39/proposal-intl-duration-format/pull/184
This commit is contained in:
parent
b37947f3a4
commit
330ecdd016
@ -2509,12 +2509,73 @@ 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") {
|
||||
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}`;
|
||||
}
|
||||
|
||||
const units = [
|
||||
"years",
|
||||
"months",
|
||||
@ -2528,39 +2589,7 @@ function partitionDurationFormatPattern(duration, style = "short") {
|
||||
"nanoseconds",
|
||||
];
|
||||
|
||||
function durationToFractionalSeconds(duration) {
|
||||
let {
|
||||
seconds = 0,
|
||||
milliseconds = 0,
|
||||
microseconds = 0,
|
||||
nanoseconds = 0,
|
||||
} = duration;
|
||||
|
||||
// Directly return seconds when no sub-seconds are present.
|
||||
if (milliseconds === 0 && microseconds === 0 && nanoseconds === 0) {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Split the nanoseconds amount into seconds and sub-seconds.
|
||||
let q = ns / 1_000_000_000n;
|
||||
let r = ns % 1_000_000_000n;
|
||||
|
||||
// Pad sub-seconds, without any leading negative sign, to nine digits.
|
||||
if (r < 0) {
|
||||
r = -r;
|
||||
}
|
||||
r = String(r).padStart(9, "0");
|
||||
|
||||
// Return seconds with fractional part as a decimal string.
|
||||
return `${q}.${r}`;
|
||||
}
|
||||
let options = durationFormat.resolvedOptions();
|
||||
|
||||
// Only "en" is supported.
|
||||
const locale = "en";
|
||||
@ -2568,116 +2597,131 @@ function partitionDurationFormatPattern(duration, style = "short") {
|
||||
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"];
|
||||
|
||||
// Numeric seconds and sub-seconds are combined into a single value.
|
||||
if (unit === "seconds") {
|
||||
value = durationToFractionalSeconds(duration);
|
||||
// NumberFormat requires singular unit names.
|
||||
let numberFormatUnit = unit.slice(0, -1);
|
||||
|
||||
// Compute the matching NumberFormat options.
|
||||
let nfOpts = Object.create(null);
|
||||
|
||||
// Numeric seconds and sub-seconds are combined into a single value.
|
||||
let done = false;
|
||||
if (unit === "seconds" || unit === "milliseconds" || unit === "microseconds") {
|
||||
let nextStyle = options[units[units.indexOf(unit) + 1]];
|
||||
if (nextStyle === "numeric") {
|
||||
if (unit === "seconds") {
|
||||
value = durationToFractional(duration, 9);
|
||||
} else if (unit === "milliseconds") {
|
||||
value = durationToFractional(duration, 6);
|
||||
} else {
|
||||
value = durationToFractional(duration, 3);
|
||||
}
|
||||
|
||||
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);
|
||||
nfOpts.numberingSystem = options.numberingSystem;
|
||||
|
||||
// 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;
|
||||
// If the value is formatted as a 2-digit numeric value.
|
||||
if (style === "2-digit") {
|
||||
nfOpts.minimumIntegerDigits = 2;
|
||||
}
|
||||
|
||||
// Numeric seconds include any sub-seconds.
|
||||
if (style === "digital" && unit === "seconds") {
|
||||
roundingMode = "trunc";
|
||||
minimumFractionDigits = 0;
|
||||
maximumFractionDigits = 9;
|
||||
}
|
||||
|
||||
// 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, "");
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -84,7 +84,7 @@ const durations = [
|
||||
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,
|
||||
|
@ -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`);
|
||||
|
@ -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`);
|
||||
|
@ -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`);
|
||||
|
@ -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`);
|
||||
|
@ -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`);
|
||||
|
@ -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`);
|
||||
|
@ -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}`);
|
||||
|
@ -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}`);
|
||||
|
@ -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}`);
|
||||
|
@ -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}`);
|
||||
|
@ -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`);
|
||||
|
@ -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}`);
|
||||
|
@ -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}`);
|
||||
|
@ -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}`);
|
||||
|
@ -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}`);
|
||||
|
Loading…
x
Reference in New Issue
Block a user