Skip to content
Permalink
Browse files
[JSC] Implement Intl.DurationFormat
https://bugs.webkit.org/show_bug.cgi?id=214794
<rdar://66436701>

Reviewed by Ross Kirsling.

This patch implements Intl.DurationFormat[1], which is now stage-3 feature.
It is decoupled from Temporal now: it can take an object with `hours` etc. properties, and
generate formatted string for duration. In the future, it will accept Temporal.Duration too.

1. We add new microsecond and nanosecond units because they are necessary to DurationFormat[2].
2. Implement Intl.DurationFormat with UNumberFormatter and UListFormatter.

[1]: https://github.com/tc39/proposal-intl-duration-format
[2]: tc39/ecma402#708

* JSTests/stress/intl-durationformat-basic.js: Added.
(shouldBe):
(shouldBeOneOf):
(shouldBeForICUVersion):
(shouldNotThrow):
(shouldThrow):
* JSTests/stress/intl-durationformat-digital.js: Added.
(shouldBe):
(throw.new.Error):
* JSTests/stress/intl-durationformat-format-to-parts.js: Added.
(shouldBe):
(shouldBeOneOf):
(shouldBeForICUVersion):
(shouldNotThrow):
(shouldThrow):
(throw.new.Error):
* JSTests/stress/intl-durationformat.js: Added.
(shouldBe):
(shouldNotThrow):
(shouldThrow):
(test.DerivedDurationFormat):
(test.get shouldThrow):
(test):
* JSTests/stress/intl-enumeration.js:
* JSTests/test262/config.yaml:
* JSTests/test262/expectations.yaml:
* Source/JavaScriptCore/CMakeLists.txt:
* Source/JavaScriptCore/DerivedSources-input.xcfilelist:
* Source/JavaScriptCore/DerivedSources-output.xcfilelist:
* Source/JavaScriptCore/DerivedSources.make:
* Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj:
* Source/JavaScriptCore/Sources.txt:
* Source/JavaScriptCore/heap/Heap.cpp:
(JSC::Heap::Heap):
* Source/JavaScriptCore/heap/Heap.h:
* Source/JavaScriptCore/heap/HeapSubspaceTypes.h:
* Source/JavaScriptCore/runtime/CommonIdentifiers.h:
* Source/JavaScriptCore/runtime/IntlDurationFormat.cpp: Added.
(JSC::IntlDurationFormat::create):
(JSC::IntlDurationFormat::createStructure):
(JSC::IntlDurationFormat::IntlDurationFormat):
(JSC::IntlDurationFormat::finishCreation):
(JSC::intlDurationUnitOptions):
(JSC::displayName):
(JSC::IntlDurationFormat::initializeDurationFormat):
(JSC::ListFormatInput::ListFormatInput):
(JSC::ListFormatInput::size const):
(JSC::ListFormatInput::stringPointers const):
(JSC::ListFormatInput::stringLengths const):
(JSC::retrieveSeparator):
(JSC::collectElements):
(JSC::IntlDurationFormat::format const):
(JSC::IntlDurationFormat::formatToParts const):
(JSC::IntlDurationFormat::resolvedOptions const):
(JSC::IntlDurationFormat::styleString):
(JSC::IntlDurationFormat::unitStyleString):
(JSC::IntlDurationFormat::displayString):
* Source/JavaScriptCore/runtime/IntlDurationFormat.h: Added.
* Source/JavaScriptCore/runtime/IntlDurationFormatConstructor.cpp: Added.
(JSC::IntlDurationFormatConstructor::create):
(JSC::IntlDurationFormatConstructor::createStructure):
(JSC::IntlDurationFormatConstructor::IntlDurationFormatConstructor):
(JSC::IntlDurationFormatConstructor::finishCreation):
(JSC::JSC_DEFINE_HOST_FUNCTION):
* Source/JavaScriptCore/runtime/IntlDurationFormatConstructor.h: Added.
* Source/JavaScriptCore/runtime/IntlDurationFormatPrototype.cpp: Added.
(JSC::IntlDurationFormatPrototype::create):
(JSC::IntlDurationFormatPrototype::createStructure):
(JSC::IntlDurationFormatPrototype::IntlDurationFormatPrototype):
(JSC::IntlDurationFormatPrototype::finishCreation):
(JSC::JSC_DEFINE_HOST_FUNCTION):
* Source/JavaScriptCore/runtime/IntlDurationFormatPrototype.h: Added.
* Source/JavaScriptCore/runtime/IntlObject.cpp:
(JSC::createDurationFormatConstructor):
(JSC::IntlObject::finishCreation):
* Source/JavaScriptCore/runtime/IntlObject.h:
(JSC::intlDurationFormatAvailableLocales):
* Source/JavaScriptCore/runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* Source/JavaScriptCore/runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::durationFormatStructure):

Canonical link: https://commits.webkit.org/254791@main
  • Loading branch information
Constellation committed Sep 23, 2022
1 parent 64d10be commit 0a1408274330aa1999490790cee7d2b9b3b8ac2b
Show file tree
Hide file tree
Showing 30 changed files with 1,793 additions and 37 deletions.
@@ -0,0 +1,44 @@
//@ requireOptions("--useIntlDurationFormat=1")

function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}

function shouldBeOneOf(actual, expectedArray) {
if (!expectedArray.some((value) => value === actual))
throw new Error('bad value: ' + actual + ' expected values: ' + expectedArray);
}

const icuVersion = $vm.icuVersion();
function shouldBeForICUVersion(minimumVersion, actual, expected) {
if (icuVersion < minimumVersion)
return;

if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}

function shouldNotThrow(func) {
func();
}

function shouldThrow(func, errorType) {
let error;
try {
func();
} catch (e) {
error = e;
}

if (!(error instanceof errorType))
throw new Error(`Expected ${errorType.name}!`);
}

if (Intl.DurationFormat) {
shouldBe(new Intl.DurationFormat("fr-FR", { style: "long" }).format({
hours: 1,
minutes: 46,
seconds: 40,
}), `1 heure, 46 minutes et 40 secondes`);
}
@@ -0,0 +1,127 @@
//@ requireOptions("--useIntlDurationFormat=1")

function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error('bad value: ' + actual);
}

function shouldBeOneOf(actual, expectedArray) {
if (!expectedArray.some((value) => value === actual))
throw new Error('bad value: ' + actual + ' expected values: ' + expectedArray);
}

if (Intl.DurationFormat) {
{
var fmt = new Intl.DurationFormat('en-DK', {
style: 'digital'
});
shouldBeOneOf(fmt.format({ years: 1, months: 2, weeks: 3, days: 4, hours: 10, minutes: 34, seconds: 33, milliseconds: 32 }), [
`1y 2mo 3w 4d 10.34.33`,
`1y 2m 3w 4d 10.34.33`,
]);
}
{
var fmt = new Intl.DurationFormat('en-DK-u-ca-buddhist', {
style: 'digital'
});
shouldBeOneOf(fmt.format({ years: 1, months: 2, weeks: 3, days: 4, hours: 10, minutes: 34, seconds: 33, milliseconds: 32 }), [
`1y 2mo 3w 4d 10.34.33`,
`1y 2m 3w 4d 10.34.33`,
]);
}
{
var fmt = new Intl.DurationFormat('en-DK-u-nu-hanidec', {
style: 'digital'
});
shouldBeOneOf(fmt.format({ years: 1, months: 2, weeks: 3, days: 4, hours: 10, minutes: 34, seconds: 33, milliseconds: 32 }), [
`一y 二mo 三w 四d 一〇:三四:三三`,
`一y 二m 三w 四d 一〇:三四:三三`,
]);
}
{
var fmt = new Intl.DurationFormat('en', {
style: 'digital'
});
shouldBeOneOf(fmt.format({ years: 1, months: 2, weeks: 3, days: 4, hours: 10, minutes: 34, seconds: 33, milliseconds: 32 }), [
`1y 2mo 3w 4d 10:34:33`,
`1y 2m 3w 4d 10:34:33`,
]);
}
{
var fmt = new Intl.DurationFormat('en', {
style: 'digital'
});
shouldBeOneOf(fmt.format({ years: 1, months: 2, weeks: 3, days: 4, hours: 10, minutes: 34, seconds: 33, milliseconds: 32 }), [
`1y 2mo 3w 4d 10:34:33`,
`1y 2m 3w 4d 10:34:33`,
]);
}
{
var fmt = new Intl.DurationFormat('en', {
style: 'digital',
milliseconds: 'numeric',
millisecondsDisplay: 'always',
fractionalDigits: 9
});

shouldBeOneOf(fmt.format({ years: 1, months: 2, weeks: 3, days: 4, hours: 10, minutes: 34, seconds: 33, milliseconds: 32 }), [
`1y 2mo 3w 4d 10:34:33.032000000`,
`1y 2m 3w 4d 10:34:33.032000000`,
]);
}
{
var fmt = new Intl.DurationFormat('en', {
style: 'digital',
milliseconds: 'numeric',
millisecondsDisplay: 'always',
fractionalDigits: 2
});

shouldBeOneOf(fmt.format({ years: 1, months: 2, weeks: 3, days: 4, hours: 10, minutes: 34, seconds: 33, milliseconds: 32 }), [
`1y 2mo 3w 4d 10:34:33.03`,
`1y 2m 3w 4d 10:34:33.03`,
]);
}
{
var fmt = new Intl.DurationFormat('en', {
style: 'digital',
milliseconds: 'numeric',
fractionalDigits: 9
});

shouldBeOneOf(fmt.format({ hours: 10, seconds: 33, milliseconds: 32 }), [
`10 33.032000000`,
]);
}
{
var fmt = new Intl.DurationFormat('en', {
style: 'digital',
milliseconds: 'numeric',
fractionalDigits: 9
});

shouldBeOneOf(fmt.format({ minutes: 10, seconds: 33, milliseconds: 32 }), [
`10:33.032000000`,
]);
}
{
var fmt = new Intl.DurationFormat('en', {
style: 'digital',
milliseconds: 'numeric',
fractionalDigits: 9
});

shouldBeOneOf(fmt.format({ hours: 10, minutes: 10, milliseconds: 32, microseconds: 44, nanoseconds: 55 }), [
`10:10:00.032044055`,
]);
}
{
var fmt = new Intl.DurationFormat('en', {
style: 'digital',
});

shouldBeOneOf(fmt.format({ hours: 10, minutes: 10, milliseconds: 32}), [
`10:10:00`,
]);
}
}
@@ -0,0 +1,61 @@
//@ requireOptions("--useIntlDurationFormat=1")

function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}

function shouldBeOneOf(actual, expectedArray) {
if (!expectedArray.some((value) => value === actual))
throw new Error('bad value: ' + actual + ' expected values: ' + expectedArray);
}

const icuVersion = $vm.icuVersion();
function shouldBeForICUVersion(minimumVersion, actual, expected) {
if (icuVersion < minimumVersion)
return;

if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}

function shouldNotThrow(func) {
func();
}

function shouldThrow(func, errorType) {
let error;
try {
func();
} catch (e) {
error = e;
}

if (!(error instanceof errorType))
throw new Error(`Expected ${errorType.name}!`);
}

if (Intl.DurationFormat) {
{
var fmt = new Intl.DurationFormat('en');
shouldBe(JSON.stringify(fmt.formatToParts({
years: 1,
hours: 22,
minutes: 30,
seconds: 34,
})), `[{"type":"years","value":"1 yr"},{"type":"literal","value":", "},{"type":"hours","value":"22 hr"},{"type":"literal","value":", "},{"type":"minutes","value":"30 min"},{"type":"literal","value":", "},{"type":"seconds","value":"34 sec"}]`);
}
{
var fmt = new Intl.DurationFormat('en', {
style: 'digital',
milliseconds: 'numeric',
millisecondsDisplay: 'always',
fractionalDigits: 2
});

shouldBeOneOf(JSON.stringify(fmt.formatToParts({ years: 1, months: 2, weeks: 3, days: 4, hours: 10, minutes: 34, seconds: 33, milliseconds: 32 })), [
`[{"type":"years","value":"1y"},{"type":"literal","value":" "},{"type":"months","value":"2mo"},{"type":"literal","value":" "},{"type":"weeks","value":"3w"},{"type":"literal","value":" "},{"type":"days","value":"4d"},{"type":"literal","value":" "},{"type":"hours","value":"10"},{"type":"literal","value":":"},{"type":"minutes","value":"34"},{"type":"literal","value":":"},{"type":"seconds","value":"33.03"}]`,
`[{"type":"years","value":"1y"},{"type":"literal","value":" "},{"type":"months","value":"2m"},{"type":"literal","value":" "},{"type":"weeks","value":"3w"},{"type":"literal","value":" "},{"type":"days","value":"4d"},{"type":"literal","value":" "},{"type":"hours","value":"10"},{"type":"literal","value":":"},{"type":"minutes","value":"34"},{"type":"literal","value":":"},{"type":"seconds","value":"33.03"}]`,
]);
}
}

0 comments on commit 0a14082

Please sign in to comment.