Use simplified PartitionDurationFormatPattern to computed expected results

This commit is contained in:
André Bargull 2023-08-23 10:12:52 +02:00 committed by Ms2ger
parent 4da0b8f005
commit 71091f1541
11 changed files with 239 additions and 106 deletions

View File

@ -29,6 +29,8 @@ defines:
- getDateTimeComponents
- getDateTimeComponentValues
- isCanonicalizedStructurallyValidTimeZoneName
- partitionDurationFormatPattern
- formatDurationFormatPattern
---*/
/**
*/
@ -2502,3 +2504,212 @@ function isCanonicalizedStructurallyValidTimeZoneName(timeZone) {
}
return zoneNamePattern.test(timeZone);
}
/**
* @description Simplified PartitionDurationFormatPattern implementation which
* only supports the "en" locale.
* @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) {
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}`;
}
// Only "en" is supported.
const locale = "en";
const numberingSystem = "latn";
const timeSeparator = ":";
let result = [];
let separated = false;
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";
}
// Numeric seconds and sub-seconds are combined into a single value.
if (unit === "seconds") {
value = durationToFractionalSeconds(duration);
}
}
// "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";
}
}
// 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;
}
// The value is formatted as a numeric unit.
nfOpts = {
numberingSystem,
minimumIntegerDigits: (unitStyle === "2-digit" ? 2 : 1),
roundingMode,
minimumFractionDigits,
maximumFractionDigits,
};
}
let nf = new Intl.NumberFormat(locale, nfOpts);
let formatted = nf.formatToParts(value);
// Add |numberFormatUnit| to the formatted number.
let list = [];
for (let {value, type} of formatted) {
list.push({type, value, unit: numberFormatUnit});
}
if (!separated) {
// Prepend the separator before the next numeric unit.
if (unitStyle === "2-digit" || unitStyle === "numeric") {
separated = 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") {
break;
}
}
let lf = new Intl.ListFormat(locale, {
type: "unit",
style: (style !== "digital" ? style : "short"),
});
// 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} 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, "");
}

View File

@ -5,6 +5,7 @@
esid: sec-Intl.DurationFormat.prototype.format
description: Test if format method formats duration correctly with different "style" arguments
locale: [en-US]
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
@ -21,7 +22,7 @@ const duration = {
nanoseconds: 9,
};
const expected = formatDurationFormatPattern(duration);
const df = new Intl.DurationFormat("en");
const expected = "1 yr, 2 mths, 3 wks, 3 days, 4 hr, 5 min, 6 sec, 7 ms, 8 μs, 9 ns";
assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using default style option`);

View File

@ -6,11 +6,11 @@
esid: sec-Intl.DurationFormat.prototype.format
description: Test if format method formats duration correctly with different "style" arguments
locale: [en-US]
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
const style = "digital";
const expected = "1 yr, 2 mths, 3 wks, 3 days, 4:05:06";
const duration = {
years: 1,
@ -25,5 +25,7 @@ const duration = {
nanoseconds: 9,
};
const expected = formatDurationFormatPattern(duration, style);
const df = new Intl.DurationFormat("en", {style});
assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using ${style} style option`);

View File

@ -5,11 +5,11 @@
esid: sec-Intl.DurationFormat.prototype.format
description: Test if format method formats duration correctly with different "style" arguments
locale: [en-US]
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
const style = "long";
const expected = "1 year, 2 months, 3 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds, 8 microseconds, 9 nanoseconds";
const duration = {
years: 1,
@ -24,5 +24,7 @@ const duration = {
nanoseconds: 9,
};
const expected = formatDurationFormatPattern(duration, style);
const df = new Intl.DurationFormat("en", {style});
assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using ${style} style option`);

View File

@ -5,11 +5,11 @@
esid: sec-Intl.DurationFormat.prototype.format
description: Test if format method formats duration correctly with different "style" arguments
locale: [en-US]
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
const style = "narrow";
const expected = "1y 2m 3w 3d 4h 5m 6s 7ms 8μs 9ns";
const duration = {
years: 1,
@ -24,5 +24,7 @@ const duration = {
nanoseconds: 9,
};
const expected = formatDurationFormatPattern(duration, style);
const df = new Intl.DurationFormat("en", {style});
assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using ${style} style option`);

View File

@ -5,11 +5,11 @@
esid: sec-Intl.DurationFormat.prototype.format
description: Test if format method formats duration correctly with different "style" arguments
locale: [en-US]
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
const style = "short";
const expected = "1 yr, 2 mths, 3 wks, 3 days, 4 hr, 5 min, 6 sec, 7 ms, 8 μs, 9 ns";
const duration = {
years: 1,
@ -24,5 +24,7 @@ const duration = {
nanoseconds: 9,
};
const expected = formatDurationFormatPattern(duration, style);
const df = new Intl.DurationFormat("en", {style});
assert.sameValue(df.format(duration), expected, `Assert DurationFormat format output using ${style} style option`);

View File

@ -4,6 +4,7 @@
/*---
esid: sec-Intl.DurationFormat.prototype.formatToParts
description: Checks basic handling of formatToParts, using long, short,narrow and digital styles.
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
@ -37,31 +38,7 @@ const duration = {
nanoseconds: 789,
};
const expected = [
{ type: "integer", value: "7", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "hr", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "integer", value: "8", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "min", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "integer", value: "9", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "sec", unit: "second" },
{ type: "literal", value: ", " },
{ type: "integer", value: "123", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "integer", value: "456", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
{ type: "literal", value: ", " },
{ type: "integer", value: "789", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "ns", unit: "nanosecond" },
];
const expected = partitionDurationFormatPattern(duration);
let df = new Intl.DurationFormat('en');
compare(df.formatToParts(duration), expected, `Using style : default`);

View File

@ -4,6 +4,7 @@
/*---
esid: sec-Intl.DurationFormat.prototype.formatToParts
description: Checks basic handling of formatToParts, using long, short,narrow and digital styles.
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
@ -38,13 +39,8 @@ const duration = {
};
const style = "digital";
const expected = [
{ type: "integer", value: "7", unit: "hour" },
{ type: "literal", value: ":"},
{ type: "integer", value: "08", unit: "minute" },
{ type: "literal", value: ":"},
{ type: "integer", value: "09", unit: "second" },
];
const expected = partitionDurationFormatPattern(duration, style);
let df = new Intl.DurationFormat('en', { style });
compare(df.formatToParts(duration), expected, `Using style : ${style}`);

View File

@ -4,6 +4,7 @@
/*---
esid: sec-Intl.DurationFormat.prototype.formatToParts
description: Checks basic handling of formatToParts, using long, short,narrow and digital styles.
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
@ -38,31 +39,8 @@ const duration = {
};
const style = "long";
const expected = [
{ type: "integer", value: "7", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "hours", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "integer", value: "8", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "minutes", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "integer", value: "9", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "seconds", unit: "second" },
{ type: "literal", value: ", " },
{ type: "integer", value: "123", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "milliseconds", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "integer", value: "456", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "microseconds", unit: "microsecond" },
{ type: "literal", value: ", " },
{ type: "integer", value: "789", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "nanoseconds", unit: "nanosecond" },
];
const expected = partitionDurationFormatPattern(duration, style);
let df = new Intl.DurationFormat('en', { style });
compare(df.formatToParts(duration), expected, `Using style : ${style}`);

View File

@ -4,6 +4,7 @@
/*---
esid: sec-Intl.DurationFormat.prototype.formatToParts
description: Checks basic handling of formatToParts, using long, short,narrow and digital styles.
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
@ -38,25 +39,8 @@ const duration = {
};
const style = "narrow";
const expected = [
{ type: "integer", value: "7", unit: "hour" },
{ type: "unit", value: "h", unit: "hour" },
{ type: "literal", value: " " },
{ type: "integer", value: "8", unit: "minute" },
{ type: "unit", value: "m", unit: "minute" },
{ type: "literal", value: " " },
{ type: "integer", value: "9", unit: "second" },
{ type: "unit", value: "s", unit: "second" },
{ type: "literal", value: " " },
{ type: "integer", value: "123", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: " " },
{ type: "integer", value: "456", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
{ type: "literal", value: " " },
{ type: "integer", value: "789", unit: "nanosecond" },
{ type: "unit", value: "ns", unit: "nanosecond" },
];
const expected = partitionDurationFormatPattern(duration, style);
let df = new Intl.DurationFormat('en', { style });
compare(df.formatToParts(duration), expected, `Using style : ${style}`);

View File

@ -4,6 +4,7 @@
/*---
esid: sec-Intl.DurationFormat.prototype.formatToParts
description: Checks basic handling of formatToParts, using long, short,narrow and digital styles.
includes: [testIntl.js]
features: [Intl.DurationFormat]
---*/
@ -38,31 +39,8 @@ const duration = {
};
const style = "short";
const expected = [
{ type: "integer", value: "7", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "hr", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "integer", value: "8", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "min", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "integer", value: "9", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "sec", unit: "second" },
{ type: "literal", value: ", " },
{ type: "integer", value: "123", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "integer", value: "456", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
{ type: "literal", value: ", " },
{ type: "integer", value: "789", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "ns", unit: "nanosecond" },
];
const expected = partitionDurationFormatPattern(duration, style);
let df = new Intl.DurationFormat('en', { style });
compare(df.formatToParts(duration), expected, `Using style : ${style}`);