LibJS: Implement the latest Intl.DurationFormat proposal

The proposal has undergone quite a few normative changes since we last
synced with it. There was a time when it could not be implemented as it
was written, which is no longer the case. The resulting proposal has had
so many changes compared to our implementation, that it wouldn't make
sense to implement them commit-by-commit as we normally do. So instead,
this just implements the HEAD revision of the spec in one pass.
This commit is contained in:
Timothy Flynn 2024-06-13 16:05:57 -04:00 committed by Andreas Kling
parent 0e5cd2c45a
commit d634039c10
Notes: sideshowbarker 2024-07-16 23:52:22 +09:00
8 changed files with 1111 additions and 432 deletions

View file

@ -47,6 +47,8 @@
M(IndexOutOfRange, "Index {} is out of range of array length {}") \
M(InOperatorWithObject, "'in' operator must be used on an object") \
M(InstanceOfOperatorBadPrototype, "'prototype' property of {} is not an object") \
M(IntlFractionalUnitFollowedByNonFractionalUnit, "Non-fractional unit {} is not allowed after a fractional unit") \
M(IntlFractionalUnitsMixedWithAlwaysDisplay, "Fractional unit {} may not be used with {} value of 'always'") \
M(IntlInvalidDateTimeFormatOption, "Option {} cannot be set when also providing {}") \
M(IntlInvalidKey, "{} is not a valid key") \
M(IntlInvalidLanguageTag, "{} is not a structurally valid language tag") \
@ -58,8 +60,8 @@
M(IntlMinimumExceedsMaximum, "Minimum value {} is larger than maximum value {}") \
M(IntlNumberIsNaNOrOutOfRange, "Value {} is NaN or is not between {} and {}") \
M(IntlOptionUndefined, "Option {} must be defined when option {} is {}") \
M(IntlNonNumericOr2DigitAfterNumericOr2Digit, "Styles other than 'numeric' and '2-digit' may not be used in smaller units after " \
"being used in larger units") \
M(IntlNonNumericOr2DigitAfterNumericOr2Digit, "Styles other than 'fractional', numeric', or '2-digit' may not be used in smaller " \
"units after being used in larger units") \
M(InvalidAssignToConst, "Invalid assignment to const variable") \
M(InvalidCodePoint, "Invalid code point {}, must be an integer no less than 0 and no greater than 0x10FFFF") \
M(InvalidFormat, "Invalid {} format") \

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,6 @@
#include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibLocale/ListFormat.h>
namespace JS::Intl {
@ -33,7 +32,8 @@ public:
Short,
Narrow,
Numeric,
TwoDigit
TwoDigit,
Fractional,
};
static_assert(to_underlying(ValueStyle::Long) == to_underlying(::Locale::Style::Long));
static_assert(to_underlying(ValueStyle::Short) == to_underlying(::Locale::Style::Short));
@ -62,6 +62,15 @@ public:
void set_numbering_system(String numbering_system) { m_numbering_system = move(numbering_system); }
String const& numbering_system() const { return m_numbering_system; }
void set_hours_minutes_separator(String hours_minutes_separator) { m_hours_minutes_separator = move(hours_minutes_separator); }
String const& hours_minutes_separator() const { return m_hours_minutes_separator; }
void set_minutes_seconds_separator(String minutes_seconds_separator) { m_minutes_seconds_separator = move(minutes_seconds_separator); }
String const& minutes_seconds_separator() const { return m_minutes_seconds_separator; }
void set_two_digit_hours(bool two_digit_hours) { m_two_digit_hours = two_digit_hours; }
bool two_digit_hours() const { return m_two_digit_hours; }
void set_style(StringView style) { m_style = style_from_string(style); }
Style style() const { return m_style; }
StringView style_string() const { return style_to_string(m_style); }
@ -162,9 +171,13 @@ private:
static Display display_from_string(StringView display);
static StringView display_to_string(Display);
String m_locale; // [[Locale]]
String m_data_locale; // [[DataLocale]]
String m_numbering_system; // [[NumberingSystem]]
String m_locale; // [[Locale]]
String m_data_locale; // [[DataLocale]]
String m_numbering_system; // [[NumberingSystem]]
String m_hours_minutes_separator; // [[HourMinutesSeparator]]
String m_minutes_seconds_separator; // [[MinutesSecondsSeparator]]
bool m_two_digit_hours { false }; // [[TwoDigitHours]]
Style m_style { Style::Long }; // [[Style]]
ValueStyle m_years_style { ValueStyle::Long }; // [[YearsStyle]]
Display m_years_display { Display::Auto }; // [[YearsDisplay]]
@ -223,10 +236,21 @@ struct DurationUnitOptions {
String display;
};
struct DurationFormatPart {
StringView type;
String value;
StringView unit;
};
ThrowCompletionOr<Temporal::DurationRecord> to_duration_record(VM&, Value input);
i8 duration_record_sign(Temporal::DurationRecord const&);
bool is_valid_duration_record(Temporal::DurationRecord const&);
ThrowCompletionOr<DurationUnitOptions> get_duration_unit_options(VM&, String const& unit, Object const& options, StringView base_style, ReadonlySpan<StringView> styles_list, StringView digital_base, StringView previous_style);
Vector<::Locale::ListFormatPart> partition_duration_format_pattern(VM&, DurationFormat const&, Temporal::DurationRecord const& duration);
ThrowCompletionOr<DurationUnitOptions> get_duration_unit_options(VM&, String const& unit, Object const& options, StringView base_style, ReadonlySpan<StringView> styles_list, StringView digital_base, StringView previous_style, bool two_digit_hours);
double add_fractional_digits(DurationFormat const&, Temporal::DurationRecord const&);
bool next_unit_fractional(DurationFormat const&, StringView unit);
Vector<DurationFormatPart> format_numeric_hours(VM&, DurationFormat const&, double hours_value, bool sign_displayed);
Vector<DurationFormatPart> format_numeric_minutes(VM&, DurationFormat const&, double minutes_value, bool hours_displayed, bool sign_displayed);
Vector<DurationFormatPart> format_numeric_seconds(VM&, DurationFormat const&, double seconds_value, bool minutes_displayed, bool sign_displayed);
Vector<DurationFormatPart> format_numeric_units(VM&, DurationFormat const&, Temporal::DurationRecord const&, StringView first_numeric_unit, bool sign_displayed);
Vector<DurationFormatPart> list_format_parts(VM&, DurationFormat const&, Vector<Vector<DurationFormatPart>>& partitioned_parts_list);
Vector<DurationFormatPart> partition_duration_format_pattern(VM&, DurationFormat const&, Temporal::DurationRecord const&);
}

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2022-2023, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -11,6 +11,7 @@
#include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Intl/DurationFormat.h>
#include <LibJS/Runtime/Intl/DurationFormatConstructor.h>
#include <LibLocale/DurationFormat.h>
namespace JS::Intl {
@ -51,7 +52,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DurationFormatConstructor::construct(Fun
auto locales = vm.argument(0);
auto options_value = vm.argument(1);
// 2. Let durationFormat be ? OrdinaryCreateFromConstructor(NewTarget, "%DurationFormatPrototype%", « [[InitializedDurationFormat]], [[Locale]], [[DataLocale]], [[NumberingSystem]], [[Style]], [[YearsStyle]], [[YearsDisplay]], [[MonthsStyle]], [[MonthsDisplay]] , [[WeeksStyle]], [[WeeksDisplay]] , [[DaysStyle]], [[DaysDisplay]] , [[HoursStyle]], [[HoursDisplay]] , [[MinutesStyle]], [[MinutesDisplay]] , [[SecondsStyle]], [[SecondsDisplay]] , [[MillisecondsStyle]], [[MillisecondsDisplay]] , [[MicrosecondsStyle]], [[MicrosecondsDisplay]] , [[NanosecondsStyle]], [[NanosecondsDisplay]], [[FractionalDigits]] »).
// 2. Let durationFormat be ? OrdinaryCreateFromConstructor(NewTarget, "%DurationFormatPrototype%", « [[InitializedDurationFormat]], [[Locale]], [[DataLocale]], [[NumberingSystem]], [[Style]], [[YearsStyle]], [[YearsDisplay]], [[MonthsStyle]], [[MonthsDisplay]], [[WeeksStyle]], [[WeeksDisplay]], [[DaysStyle]], [[DaysDisplay]], [[HoursStyle]], [[HoursDisplay]], [[MinutesStyle]], [[MinutesDisplay]], [[SecondsStyle]], [[SecondsDisplay]], [[MillisecondsStyle]], [[MillisecondsDisplay]], [[MicrosecondsStyle]], [[MicrosecondsDisplay]], [[NanosecondsStyle]], [[NanosecondsDisplay]], [[HoursMinutesSeparator]], [[MinutesSecondsSeparator]], [[FractionalDigits]], [[TwoDigitHours]] »).
auto duration_format = TRY(ordinary_create_from_constructor<DurationFormat>(vm, new_target, &Intrinsics::intl_duration_format_prototype));
// 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
@ -63,7 +64,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DurationFormatConstructor::construct(Fun
// 5. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit").
auto matcher = TRY(get_option(vm, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv));
// 6. Let numberingSystem be ? GetOption(options, "numberingSystem", string, empty, undefined).
// 6. Let numberingSystem be ? GetOption(options, "numberingSystem", string, undefined, undefined).
auto numbering_system = TRY(get_option(vm, *options, vm.names.numberingSystem, OptionType::String, {}, Empty {}));
// 7. If numberingSystem is not undefined, then
@ -76,7 +77,8 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DurationFormatConstructor::construct(Fun
// 8. Let opt be the Record { [[localeMatcher]]: matcher, [[nu]]: numberingSystem }.
LocaleOptions opt {};
opt.locale_matcher = matcher;
opt.nu = numbering_system.is_undefined() ? Optional<String>() : numbering_system.as_string().utf8_string();
if (!numbering_system.is_undefined())
opt.nu = numbering_system.as_string().utf8_string();
// 9. Let r be ResolveLocale(%DurationFormat%.[[AvailableLocales]], requestedLocales, opt, %DurationFormat%.[[RelevantExtensionKeys]], %DurationFormat%.[[LocaleData]]).
auto result = resolve_locale(requested_locales, opt, DurationFormat::relevant_extension_keys());
@ -87,23 +89,40 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DurationFormatConstructor::construct(Fun
// 11. Set durationFormat.[[Locale]] to locale.
duration_format->set_locale(move(locale));
// 12. Set durationFormat.[[NumberingSystem]] to r.[[nu]].
// 12. Set durationFormat.[[DataLocale]] to r.[[dataLocale]].
duration_format->set_data_locale(move(result.data_locale));
// 13. Let dataLocale be durationFormat.[[DataLocale]].
// 14. Let dataLocaleData be durationFormat.[[LocaleData]].[[<dataLocale>]].
// 15. Let digitalFormat be dataLocaleData.[[DigitalFormat]].
auto digital_format = ::Locale::digital_format(duration_format->data_locale());
// 16. Let twoDigitHours be digitalFormat.[[TwoDigitHours]].
// 17. Set durationFormat.[[TwoDigitHours]] to twoDigitHours.
duration_format->set_two_digit_hours(digital_format.uses_two_digit_hours);
// 18. Let hoursMinutesSeparator be digitalFormat.[[HoursMinutesSeparator]].
// 19. Set durationFormat.[[HoursMinutesSeparator]] to hoursMinutesSeparator.
duration_format->set_hours_minutes_separator(move(digital_format.hours_minutes_separator));
// 20. Let minutesSecondsSeparator be digitalFormat.[[MinutesSecondsSeparator]].
// 21. Set durationFormat.[[MinutesSecondsSeparator]] to minutesSecondsSeparator.
duration_format->set_minutes_seconds_separator(move(digital_format.minutes_seconds_separator));
// 22. Set durationFormat.[[NumberingSystem]] to r.[[nu]].
if (result.nu.has_value())
duration_format->set_numbering_system(result.nu.release_value());
// 13. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow", "digital" », "short").
// 23. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow", "digital" », "short").
auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv, "digital"sv }, "short"sv));
// 14. Set durationFormat.[[Style]] to style.
// 24. Set durationFormat.[[Style]] to style.
duration_format->set_style(style.as_string().utf8_string_view());
// 15. Set durationFormat.[[DataLocale]] to r.[[dataLocale]].
duration_format->set_data_locale(move(result.data_locale));
// 16. Let prevStyle be the empty String.
// 25. Let prevStyle be the empty String.
String previous_style {};
// 17. For each row of Table 1, except the header row, in table order, do
// 26. For each row of Table 3, except the header row, in table order, do
for (auto const& duration_instances_component : duration_instances_components) {
// a. Let styleSlot be the Style Slot value of the current row.
auto style_slot = duration_instances_component.set_style_slot;
@ -111,17 +130,17 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DurationFormatConstructor::construct(Fun
// b. Let displaySlot be the Display Slot value of the current row.
auto display_slot = duration_instances_component.set_display_slot;
// c. Let unit be the Unit value.
// c. Let unit be the Unit value of the current row.
auto unit = MUST(String::from_utf8(duration_instances_component.unit));
// d. Let valueList be the Values value.
// d. Let valueList be the Values value of the current row.
auto value_list = duration_instances_component.values;
// e. Let digitalBase be the Digital Default value.
// e. Let digitalBase be the Digital Default value of the current row.
auto digital_base = duration_instances_component.digital_default;
// f. Let unitOptions be ? GetDurationUnitOptions(unit, options, style, valueList, digitalBase, prevStyle).
auto unit_options = TRY(get_duration_unit_options(vm, unit, *options, style.as_string().utf8_string_view(), value_list, digital_base, previous_style));
// f. Let unitOptions be ? GetDurationUnitOptions(unit, options, style, valueList, digitalBase, prevStyle, twoDigitHours).
auto unit_options = TRY(get_duration_unit_options(vm, unit, *options, duration_format->style_string(), value_list, digital_base, previous_style, duration_format->two_digit_hours()));
// g. Set the value of the styleSlot slot of durationFormat to unitOptions.[[Style]].
(duration_format->*style_slot)(unit_options.style);
@ -132,14 +151,14 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DurationFormatConstructor::construct(Fun
// i. If unit is one of "hours", "minutes", "seconds", "milliseconds", or "microseconds", then
if (unit.is_one_of("hours"sv, "minutes"sv, "seconds"sv, "milliseconds"sv, "microseconds"sv)) {
// i. Set prevStyle to unitOptions.[[Style]].
previous_style = unit_options.style;
previous_style = move(unit_options.style);
}
}
// 18. Set durationFormat.[[FractionalDigits]] to ? GetNumberOption(options, "fractionalDigits", 0, 9, 0).
duration_format->set_fractional_digits(Optional<u8>(TRY(get_number_option(vm, *options, vm.names.fractionalDigits, 0, 9, 0))));
// 27. Set durationFormat.[[FractionalDigits]] to ? GetNumberOption(options, "fractionalDigits", 0, 9, undefined).
duration_format->set_fractional_digits(Optional<u8>(TRY(get_number_option(vm, *options, vm.names.fractionalDigits, 0, 9, {}))));
// 19. Return durationFormat.
// 28. Return durationFormat.
return duration_format;
}

View file

@ -1,10 +1,11 @@
/*
* Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Enumerate.h>
#include <AK/StringBuilder.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
@ -81,9 +82,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationFormatPrototype::format_to_parts)
// 6. Let n be 0.
// 7. For each { [[Type]], [[Value]] } part in parts, do
for (size_t n = 0; n < parts.size(); ++n) {
auto const& part = parts[n];
for (auto [n, part] : enumerate(parts)) {
// a. Let obj be OrdinaryObjectCreate(%ObjectPrototype%).
auto object = Object::create(realm, realm.intrinsics().object_prototype());
@ -91,12 +90,16 @@ JS_DEFINE_NATIVE_FUNCTION(DurationFormatPrototype::format_to_parts)
MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, part.type)));
// c. Perform ! CreateDataPropertyOrThrow(obj, "value", part.[[Value]]).
MUST(object->create_data_property_or_throw(vm.names.value, PrimitiveString::create(vm, part.value)));
MUST(object->create_data_property_or_throw(vm.names.value, PrimitiveString::create(vm, move(part.value))));
// d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), obj).
// d. If part.[[Unit]] is not empty, perform ! CreateDataPropertyOrThrow(obj, "unit", part.[[Unit]]).
if (!part.unit.is_empty())
MUST(object->create_data_property_or_throw(vm.names.unit, PrimitiveString::create(vm, part.unit)));
// e. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), obj).
MUST(result->create_data_property_or_throw(n, object));
// e. Increment n by 1.
// f. Increment n by 1.
}
// 8. Return result.
@ -115,35 +118,54 @@ JS_DEFINE_NATIVE_FUNCTION(DurationFormatPrototype::resolved_options)
// 3. Let options be OrdinaryObjectCreate(%Object.prototype%).
auto options = Object::create(realm, realm.intrinsics().object_prototype());
// 4. For each row of Table 2, except the header row, in table order, do
// a. Let p be the Property value of the current row.
// b. Let v be the value of df's internal slot whose name is the Internal Slot value of the current row.
// c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v).
MUST(options->create_data_property_or_throw(vm.names.locale, PrimitiveString::create(vm, duration_format->locale())));
MUST(options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, duration_format->style_string())));
MUST(options->create_data_property_or_throw(vm.names.years, PrimitiveString::create(vm, duration_format->years_style_string())));
MUST(options->create_data_property_or_throw(vm.names.yearsDisplay, PrimitiveString::create(vm, duration_format->years_display_string())));
MUST(options->create_data_property_or_throw(vm.names.months, PrimitiveString::create(vm, duration_format->months_style_string())));
MUST(options->create_data_property_or_throw(vm.names.monthsDisplay, PrimitiveString::create(vm, duration_format->months_display_string())));
MUST(options->create_data_property_or_throw(vm.names.weeks, PrimitiveString::create(vm, duration_format->weeks_style_string())));
MUST(options->create_data_property_or_throw(vm.names.weeksDisplay, PrimitiveString::create(vm, duration_format->weeks_display_string())));
MUST(options->create_data_property_or_throw(vm.names.days, PrimitiveString::create(vm, duration_format->days_style_string())));
MUST(options->create_data_property_or_throw(vm.names.daysDisplay, PrimitiveString::create(vm, duration_format->days_display_string())));
MUST(options->create_data_property_or_throw(vm.names.hours, PrimitiveString::create(vm, duration_format->hours_style_string())));
MUST(options->create_data_property_or_throw(vm.names.hoursDisplay, PrimitiveString::create(vm, duration_format->hours_display_string())));
MUST(options->create_data_property_or_throw(vm.names.minutes, PrimitiveString::create(vm, duration_format->minutes_style_string())));
MUST(options->create_data_property_or_throw(vm.names.minutesDisplay, PrimitiveString::create(vm, duration_format->minutes_display_string())));
MUST(options->create_data_property_or_throw(vm.names.seconds, PrimitiveString::create(vm, duration_format->seconds_style_string())));
MUST(options->create_data_property_or_throw(vm.names.secondsDisplay, PrimitiveString::create(vm, duration_format->seconds_display_string())));
MUST(options->create_data_property_or_throw(vm.names.milliseconds, PrimitiveString::create(vm, duration_format->milliseconds_style_string())));
MUST(options->create_data_property_or_throw(vm.names.millisecondsDisplay, PrimitiveString::create(vm, duration_format->milliseconds_display_string())));
MUST(options->create_data_property_or_throw(vm.names.microseconds, PrimitiveString::create(vm, duration_format->microseconds_style_string())));
MUST(options->create_data_property_or_throw(vm.names.microsecondsDisplay, PrimitiveString::create(vm, duration_format->microseconds_display_string())));
MUST(options->create_data_property_or_throw(vm.names.nanoseconds, PrimitiveString::create(vm, duration_format->nanoseconds_style_string())));
MUST(options->create_data_property_or_throw(vm.names.nanosecondsDisplay, PrimitiveString::create(vm, duration_format->nanoseconds_display_string())));
MUST(options->create_data_property_or_throw(vm.names.fractionalDigits, duration_format->has_fractional_digits() ? Value(duration_format->fractional_digits()) : js_undefined()));
MUST(options->create_data_property_or_throw(vm.names.numberingSystem, PrimitiveString::create(vm, duration_format->numbering_system())));
// 4. For each row of Table 4, except the header row, in table order, do
auto create_option = [&](PropertyKey const& property, StringView value) {
// a. Let p be the Property value of the current row.
// b. Let v be the value of df's internal slot whose name is the Internal Slot value of the current row.
// c. If p is "fractionalDigits", then
// i. If v is not undefined, set v to 𝔽(v).
// d. Else,
// i. Assert: v is not undefined.
// e. If v is "fractional", then
if (value == "fractional"sv) {
// i. Assert: The Internal Slot value of the current row is [[MillisecondsStyle]], [[MicrosecondsStyle]], or [[NanosecondsStyle]].
// ii. Set v to "numeric".
value = "numeric"sv;
}
// f. If v is not undefined, then
// i. Perform ! CreateDataPropertyOrThrow(options, p, v).
MUST(options->create_data_property_or_throw(property, PrimitiveString::create(vm, value)));
};
create_option(vm.names.locale, duration_format->locale());
create_option(vm.names.numberingSystem, duration_format->numbering_system());
create_option(vm.names.style, duration_format->style_string());
create_option(vm.names.years, duration_format->years_style_string());
create_option(vm.names.yearsDisplay, duration_format->years_display_string());
create_option(vm.names.months, duration_format->months_style_string());
create_option(vm.names.monthsDisplay, duration_format->months_display_string());
create_option(vm.names.weeks, duration_format->weeks_style_string());
create_option(vm.names.weeksDisplay, duration_format->weeks_display_string());
create_option(vm.names.days, duration_format->days_style_string());
create_option(vm.names.daysDisplay, duration_format->days_display_string());
create_option(vm.names.hours, duration_format->hours_style_string());
create_option(vm.names.hoursDisplay, duration_format->hours_display_string());
create_option(vm.names.minutes, duration_format->minutes_style_string());
create_option(vm.names.minutesDisplay, duration_format->minutes_display_string());
create_option(vm.names.seconds, duration_format->seconds_style_string());
create_option(vm.names.secondsDisplay, duration_format->seconds_display_string());
create_option(vm.names.milliseconds, duration_format->milliseconds_style_string());
create_option(vm.names.millisecondsDisplay, duration_format->milliseconds_display_string());
create_option(vm.names.microseconds, duration_format->microseconds_style_string());
create_option(vm.names.microsecondsDisplay, duration_format->microseconds_display_string());
create_option(vm.names.nanoseconds, duration_format->nanoseconds_style_string());
create_option(vm.names.nanosecondsDisplay, duration_format->nanoseconds_display_string());
if (duration_format->has_fractional_digits())
MUST(options->create_data_property_or_throw(vm.names.fractionalDigits, Value(duration_format->fractional_digits())));
// 5. Return options.
return options;

View file

@ -32,7 +32,7 @@ describe("correct behavior", () => {
"1y 2m 3w 3d 4h 5m 6s 7ms 8μs 9ns"
);
expect(new Intl.DurationFormat("en", { style: "digital" }).format(duration)).toBe(
"1 yr, 2 mths, 3 wks, 3 days, 4:05:06"
"1 yr, 2 mths, 3 wks, 3 days, 4:05:06.007008009"
);
expect(
new Intl.DurationFormat("en", {
@ -52,7 +52,7 @@ describe("correct behavior", () => {
"1 J, 2 M, 3 W, 3 T, 4 Std., 5 Min., 6 Sek., 7 ms, 8 μs und 9 ns"
);
expect(new Intl.DurationFormat("de", { style: "digital" }).format(duration)).toBe(
"1 J, 2 Mon., 3 Wo., 3 Tg. und 4:05:06"
"1 J, 2 Mon., 3 Wo., 3 Tg. und 4:05:06,007008009"
);
expect(
new Intl.DurationFormat("de", {
@ -88,6 +88,28 @@ describe("correct behavior", () => {
expect(de.format(duration1)).toBe("1 J, 2 Mon., 3 Wo., 3 Tg. und 0:00:00");
expect(de.format(duration2)).toBe("1 J, 2 Mon., 3 Wo., 3 Tg. und 4:05:06");
});
test("locale with a time separator string other than ':'", () => {
const duration = {
hours: 1,
minutes: 2,
seconds: 3,
};
const da = new Intl.DurationFormat("da", { style: "digital" });
expect(da.format(duration)).toBe("1.02.03");
});
test("locale with preferred 2-digit hours", () => {
const duration = {
hours: 1,
minutes: 2,
seconds: 3,
};
const lt = new Intl.DurationFormat("lt", { style: "digital" });
expect(lt.format(duration)).toBe("01:02:03");
});
});
describe("errors", () => {

View file

@ -17,121 +17,225 @@ describe("correct behavior", () => {
nanoseconds: 9,
};
expect(new Intl.DurationFormat().formatToParts(duration)).toEqual([
{ type: "element", value: "1 yr" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "yr", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 mths" },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "mths", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 wks" },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "wks", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 days" },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "days", unit: "day" },
{ type: "literal", value: ", " },
{ type: "element", value: "4 hr" },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "hr", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "element", value: "5 min" },
{ type: "integer", value: "5", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "min", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "element", value: "6 sec" },
{ type: "integer", value: "6", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "sec", unit: "second" },
{ type: "literal", value: ", " },
{ type: "element", value: "7 ms" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "8 μs" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "9 ns" },
{ type: "integer", value: "9", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "ns", unit: "nanosecond" },
]);
expect(new Intl.DurationFormat("en").formatToParts(duration)).toEqual([
{ type: "element", value: "1 yr" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "yr", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 mths" },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "mths", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 wks" },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "wks", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 days" },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "days", unit: "day" },
{ type: "literal", value: ", " },
{ type: "element", value: "4 hr" },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "hr", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "element", value: "5 min" },
{ type: "integer", value: "5", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "min", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "element", value: "6 sec" },
{ type: "integer", value: "6", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "sec", unit: "second" },
{ type: "literal", value: ", " },
{ type: "element", value: "7 ms" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "8 μs" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "9 ns" },
{ type: "integer", value: "9", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "ns", unit: "nanosecond" },
]);
expect(new Intl.DurationFormat("en", { style: "long" }).formatToParts(duration)).toEqual([
{ type: "element", value: "1 year" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "year", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 months" },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "months", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 weeks" },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "weeks", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 days" },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "days", unit: "day" },
{ type: "literal", value: ", " },
{ type: "element", value: "4 hours" },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "hours", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "element", value: "5 minutes" },
{ type: "integer", value: "5", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "minutes", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "element", value: "6 seconds" },
{ type: "integer", value: "6", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "seconds", unit: "second" },
{ type: "literal", value: ", " },
{ type: "element", value: "7 milliseconds" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "milliseconds", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "8 microseconds" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "microseconds", unit: "microsecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "9 nanoseconds" },
{ type: "integer", value: "9", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "nanoseconds", unit: "nanosecond" },
]);
expect(new Intl.DurationFormat("en", { style: "short" }).formatToParts(duration)).toEqual([
{ type: "element", value: "1 yr" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "yr", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 mths" },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "mths", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 wks" },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "wks", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 days" },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "days", unit: "day" },
{ type: "literal", value: ", " },
{ type: "element", value: "4 hr" },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "hr", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "element", value: "5 min" },
{ type: "integer", value: "5", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "min", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "element", value: "6 sec" },
{ type: "integer", value: "6", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "sec", unit: "second" },
{ type: "literal", value: ", " },
{ type: "element", value: "7 ms" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "8 μs" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "9 ns" },
{ type: "integer", value: "9", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "ns", unit: "nanosecond" },
]);
expect(new Intl.DurationFormat("en", { style: "narrow" }).formatToParts(duration)).toEqual([
{ type: "element", value: "1y" },
{ type: "integer", value: "1", unit: "year" },
{ type: "unit", value: "y", unit: "year" },
{ type: "literal", value: " " },
{ type: "element", value: "2m" },
{ type: "integer", value: "2", unit: "month" },
{ type: "unit", value: "m", unit: "month" },
{ type: "literal", value: " " },
{ type: "element", value: "3w" },
{ type: "integer", value: "3", unit: "week" },
{ type: "unit", value: "w", unit: "week" },
{ type: "literal", value: " " },
{ type: "element", value: "3d" },
{ type: "integer", value: "3", unit: "day" },
{ type: "unit", value: "d", unit: "day" },
{ type: "literal", value: " " },
{ type: "element", value: "4h" },
{ type: "integer", value: "4", unit: "hour" },
{ type: "unit", value: "h", unit: "hour" },
{ type: "literal", value: " " },
{ type: "element", value: "5m" },
{ type: "integer", value: "5", unit: "minute" },
{ type: "unit", value: "m", unit: "minute" },
{ type: "literal", value: " " },
{ type: "element", value: "6s" },
{ type: "integer", value: "6", unit: "second" },
{ type: "unit", value: "s", unit: "second" },
{ type: "literal", value: " " },
{ type: "element", value: "7ms" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: " " },
{ type: "element", value: "8μs" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
{ type: "literal", value: " " },
{ type: "element", value: "9ns" },
{ type: "integer", value: "9", unit: "nanosecond" },
{ type: "unit", value: "ns", unit: "nanosecond" },
]);
expect(new Intl.DurationFormat("en", { style: "digital" }).formatToParts(duration)).toEqual(
[
{ type: "element", value: "1 yr" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "yr", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 mths" },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "mths", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 wks" },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "wks", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 days" },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "days", unit: "day" },
{ type: "literal", value: ", " },
{ type: "element", value: "4:05:06" },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: ":" },
{ type: "integer", value: "05", unit: "minute" },
{ type: "literal", value: ":" },
{ type: "integer", value: "06", unit: "second" },
{ type: "decimal", value: ".", unit: "second" },
{ type: "fraction", value: "007008009", unit: "second" },
]
);
expect(
@ -141,99 +245,184 @@ describe("correct behavior", () => {
fractionalDigits: 3,
}).formatToParts(duration)
).toEqual([
{ type: "element", value: "1y" },
{ type: "integer", value: "1", unit: "year" },
{ type: "unit", value: "y", unit: "year" },
{ type: "literal", value: " " },
{ type: "element", value: "2m" },
{ type: "integer", value: "2", unit: "month" },
{ type: "unit", value: "m", unit: "month" },
{ type: "literal", value: " " },
{ type: "element", value: "3w" },
{ type: "integer", value: "3", unit: "week" },
{ type: "unit", value: "w", unit: "week" },
{ type: "literal", value: " " },
{ type: "element", value: "3d" },
{ type: "integer", value: "3", unit: "day" },
{ type: "unit", value: "d", unit: "day" },
{ type: "literal", value: " " },
{ type: "element", value: "4h" },
{ type: "integer", value: "4", unit: "hour" },
{ type: "unit", value: "h", unit: "hour" },
{ type: "literal", value: " " },
{ type: "element", value: "5m" },
{ type: "integer", value: "5", unit: "minute" },
{ type: "unit", value: "m", unit: "minute" },
{ type: "literal", value: " " },
{ type: "element", value: "6s" },
{ type: "integer", value: "6", unit: "second" },
{ type: "unit", value: "s", unit: "second" },
{ type: "literal", value: " " },
{ type: "element", value: "7ms" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: " " },
{ type: "element", value: "8.009μs" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "decimal", value: ".", unit: "microsecond" },
{ type: "fraction", value: "009", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
]);
expect(new Intl.DurationFormat("de", { style: "long" }).formatToParts(duration)).toEqual([
{ type: "element", value: "1 Jahr" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "Jahr", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 Monate" },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "Monate", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 Wochen" },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "Wochen", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 Tage" },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "Tage", unit: "day" },
{ type: "literal", value: ", " },
{ type: "element", value: "4 Stunden" },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "Stunden", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "element", value: "5 Minuten" },
{ type: "integer", value: "5", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "Minuten", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "element", value: "6 Sekunden" },
{ type: "integer", value: "6", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "Sekunden", unit: "second" },
{ type: "literal", value: ", " },
{ type: "element", value: "7 Millisekunden" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "Millisekunden", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "8 Mikrosekunden" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "Mikrosekunden", unit: "microsecond" },
{ type: "literal", value: " und " },
{ type: "element", value: "9 Nanosekunden" },
{ type: "integer", value: "9", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "Nanosekunden", unit: "nanosecond" },
]);
expect(new Intl.DurationFormat("de", { style: "short" }).formatToParts(duration)).toEqual([
{ type: "element", value: "1 J" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "J", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 Mon." },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "Mon.", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 Wo." },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "Wo.", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 Tg." },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "Tg.", unit: "day" },
{ type: "literal", value: ", " },
{ type: "element", value: "4 Std." },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "Std.", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "element", value: "5 Min." },
{ type: "integer", value: "5", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "Min.", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "element", value: "6 Sek." },
{ type: "integer", value: "6", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "Sek.", unit: "second" },
{ type: "literal", value: ", " },
{ type: "element", value: "7 ms" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "8 μs" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
{ type: "literal", value: " und " },
{ type: "element", value: "9 ns" },
{ type: "integer", value: "9", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "ns", unit: "nanosecond" },
]);
expect(new Intl.DurationFormat("de", { style: "narrow" }).formatToParts(duration)).toEqual([
{ type: "element", value: "1 J" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "J", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 M" },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "M", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 W" },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "W", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 T" },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "T", unit: "day" },
{ type: "literal", value: ", " },
{ type: "element", value: "4 Std." },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "Std.", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "element", value: "5 Min." },
{ type: "integer", value: "5", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "Min.", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "element", value: "6 Sek." },
{ type: "integer", value: "6", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "Sek.", unit: "second" },
{ type: "literal", value: ", " },
{ type: "element", value: "7 ms" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: ", " },
{ type: "element", value: "8 μs" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
{ type: "literal", value: " und " },
{ type: "element", value: "9 ns" },
{ type: "integer", value: "9", unit: "nanosecond" },
{ type: "literal", value: " ", unit: "nanosecond" },
{ type: "unit", value: "ns", unit: "nanosecond" },
]);
expect(new Intl.DurationFormat("de", { style: "digital" }).formatToParts(duration)).toEqual(
[
{ type: "element", value: "1 J" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "J", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 Mon." },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "Mon.", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 Wo." },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "Wo.", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 Tg." },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "Tg.", unit: "day" },
{ type: "literal", value: " und " },
{ type: "element", value: "4:05:06" },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: ":" },
{ type: "integer", value: "05", unit: "minute" },
{ type: "literal", value: ":" },
{ type: "integer", value: "06", unit: "second" },
{ type: "decimal", value: ",", unit: "second" },
{ type: "fraction", value: "007008009", unit: "second" },
]
);
expect(
@ -243,23 +432,43 @@ describe("correct behavior", () => {
fractionalDigits: 3,
}).formatToParts(duration)
).toEqual([
{ type: "element", value: "1 J" },
{ type: "integer", value: "1", unit: "year" },
{ type: "literal", value: " ", unit: "year" },
{ type: "unit", value: "J", unit: "year" },
{ type: "literal", value: ", " },
{ type: "element", value: "2 M" },
{ type: "integer", value: "2", unit: "month" },
{ type: "literal", value: " ", unit: "month" },
{ type: "unit", value: "M", unit: "month" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 W" },
{ type: "integer", value: "3", unit: "week" },
{ type: "literal", value: " ", unit: "week" },
{ type: "unit", value: "W", unit: "week" },
{ type: "literal", value: ", " },
{ type: "element", value: "3 T" },
{ type: "integer", value: "3", unit: "day" },
{ type: "literal", value: " ", unit: "day" },
{ type: "unit", value: "T", unit: "day" },
{ type: "literal", value: ", " },
{ type: "element", value: "4 Std." },
{ type: "integer", value: "4", unit: "hour" },
{ type: "literal", value: " ", unit: "hour" },
{ type: "unit", value: "Std.", unit: "hour" },
{ type: "literal", value: ", " },
{ type: "element", value: "5 Min." },
{ type: "integer", value: "5", unit: "minute" },
{ type: "literal", value: " ", unit: "minute" },
{ type: "unit", value: "Min.", unit: "minute" },
{ type: "literal", value: ", " },
{ type: "element", value: "6 Sek." },
{ type: "integer", value: "6", unit: "second" },
{ type: "literal", value: " ", unit: "second" },
{ type: "unit", value: "Sek.", unit: "second" },
{ type: "literal", value: ", " },
{ type: "element", value: "7 ms" },
{ type: "integer", value: "7", unit: "millisecond" },
{ type: "literal", value: " ", unit: "millisecond" },
{ type: "unit", value: "ms", unit: "millisecond" },
{ type: "literal", value: " und " },
{ type: "element", value: "8,009 μs" },
{ type: "integer", value: "8", unit: "microsecond" },
{ type: "decimal", value: ",", unit: "microsecond" },
{ type: "fraction", value: "009", unit: "microsecond" },
{ type: "literal", value: " ", unit: "microsecond" },
{ type: "unit", value: "μs", unit: "microsecond" },
]);
});
});

View file

@ -284,7 +284,7 @@ describe("correct behavior", () => {
test("fractionalDigits", () => {
const en1 = new Intl.DurationFormat("en");
expect(en1.resolvedOptions().fractionalDigits).toBe(0);
expect(en1.resolvedOptions().fractionalDigits).toBeUndefined();
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(fractionalDigits => {
const en2 = new Intl.DurationFormat("en", { fractionalDigits: fractionalDigits });