LibJS: Work around inconsistency in CLDR Temporal data sources

This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/94a7487
https://github.com/tc39/proposal-temporal/commit/64a2395
This commit is contained in:
Timothy Flynn 2026-02-14 11:15:35 -05:00 committed by Shannon Booth
parent 46c864fdd3
commit 21079748ff
5 changed files with 56 additions and 33 deletions

View file

@ -667,7 +667,7 @@ ErrorOr<void> print_intl_date_time_format(JS::PrintContext& print_context, JS::I
TRY(print_value(print_context, JS::PrimitiveString::create(date_time_format.vm(), date_time_format.time_style_string()), seen_objects));
}
auto result = JS::Intl::for_each_calendar_field(date_time_format.vm(), date_time_format.date_time_format(), [&](auto& option, auto const& property, auto const&) -> JS::ThrowCompletionOr<void> {
auto result = JS::Intl::for_each_calendar_field(date_time_format.vm(), date_time_format.date_time_format(), [&](auto, auto& option, auto const& property, auto const&) -> JS::ThrowCompletionOr<void> {
using ValueType = typename RemoveReference<decltype(option)>::ValueType;
if (!option.has_value())

View file

@ -429,23 +429,44 @@ Optional<Unicode::CalendarPattern> get_date_time_format(Unicode::CalendarPattern
}
// 15.6.2 AdjustDateTimeStyleFormat ( formats, baseFormat, matcher, allowedOptions ), https://tc39.es/proposal-temporal/#sec-adjustdatetimestyleformat
Unicode::CalendarPattern adjust_date_time_style_format(Unicode::CalendarPattern const& base_format, ReadonlySpan<Unicode::CalendarPattern::Field> allowed_options)
Unicode::CalendarPattern adjust_date_time_style_format(VM& vm, Unicode::CalendarPattern& base_format, ReadonlySpan<Unicode::CalendarPattern::Field> allowed_options)
{
// 1. Let formatOptions be a new Record.
// 1. Let anyConflictingFields be false.
auto any_conflicted_fields = false;
// 2. For each row of Table 16, except the header row, in table order, do
MUST(for_each_calendar_field(vm, base_format, [&](auto option_type, auto& option, auto const&, auto const&) -> ThrowCompletionOr<void> {
// a. Let prop be the name given in the "Property" column of the current row.
// b. If baseFormat has a [[<prop>]] field and allowedOptions does not contain prop, set anyConflictingFields to true.
if (option.has_value() && !allowed_options.contains_slow(option_type))
any_conflicted_fields = true;
return {};
}));
// 3. If anyConflictingFields is false, return baseFormat.
if (!any_conflicted_fields)
return base_format;
// 4. NOTE: The above steps prevent the operation from returning an altered format when baseFormat would be sufficient.
// This should be unnecessary, but exists because the ECMA-402 specification does not guarantee that a format
// returned from DateTimeStyleFormat can also be returned from BasicFormatMatcher or BestFitFormatMatcher.
// 5. Let formatOptions be a new Record.
Unicode::CalendarPattern format_options;
// 2. For each field name fieldName of allowedOptions, do
// 6. For each property name prop of allowedOptions, do
base_format.for_each_calendar_field_zipped_with(format_options, allowed_options, [&](auto const& base_option, auto& format_option) {
// a. Set the field of formatOptions whose name is fieldName to the value of the field of baseFormat whose name is fieldName.
format_option = base_option;
// a. If baseFormat has a [[<prop>]] field, set formatOptions.[[<prop>]] to baseFormat.[[<prop>]].
if (base_option.has_value())
format_option = base_option;
return IterationDecision::Continue;
});
// 3. If matcher is "basic", then
// 7. If matcher is "basic", then
// a. Let bestFormat be BasicFormatMatcher(formatOptions, formats).
// 4. Else,
// 8. Else,
// a. Let bestFormat be BestFitFormatMatcher(formatOptions, formats).
// 5. Return bestFormat.
// 9. Return bestFormat.
return format_options;
}

View file

@ -147,7 +147,7 @@ ThrowCompletionOr<Utf16String> format_date_time_range(VM&, DateTimeFormat&, Form
ThrowCompletionOr<GC::Ref<Array>> format_date_time_range_to_parts(VM&, DateTimeFormat&, FormattableDateTime const& start, FormattableDateTime const& end);
Optional<Unicode::CalendarPattern> get_date_time_format(Unicode::CalendarPattern const& options, OptionRequired, OptionDefaults, OptionInherit);
Unicode::CalendarPattern adjust_date_time_style_format(Unicode::CalendarPattern const& base_format, ReadonlySpan<Unicode::CalendarPattern::Field> allowed_options);
Unicode::CalendarPattern adjust_date_time_style_format(VM&, Unicode::CalendarPattern& base_format, ReadonlySpan<Unicode::CalendarPattern::Field> allowed_options);
ThrowCompletionOr<FormattableDateTime> to_date_time_formattable(VM&, Value);
bool is_temporal_object(FormattableDateTime const&);
bool same_temporal_type(FormattableDateTime const&, FormattableDateTime const&);
@ -168,18 +168,20 @@ ThrowCompletionOr<void> for_each_calendar_field(VM& vm, Unicode::CalendarPattern
constexpr auto two_digit_numeric_narrow_short_long = AK::Array { "2-digit"sv, "numeric"sv, "narrow"sv, "short"sv, "long"sv };
constexpr auto time_zone = AK::Array { "short"sv, "long"sv, "shortOffset"sv, "longOffset"sv, "shortGeneric"sv, "longGeneric"sv };
using enum Unicode::CalendarPattern::Field;
// Table 16: Components of date and time formats, https://tc39.es/ecma402/#table-datetimeformat-components
TRY(callback(pattern.weekday, vm.names.weekday, narrow_short_long));
TRY(callback(pattern.era, vm.names.era, narrow_short_long));
TRY(callback(pattern.year, vm.names.year, two_digit_numeric));
TRY(callback(pattern.month, vm.names.month, two_digit_numeric_narrow_short_long));
TRY(callback(pattern.day, vm.names.day, two_digit_numeric));
TRY(callback(pattern.day_period, vm.names.dayPeriod, narrow_short_long));
TRY(callback(pattern.hour, vm.names.hour, two_digit_numeric));
TRY(callback(pattern.minute, vm.names.minute, two_digit_numeric));
TRY(callback(pattern.second, vm.names.second, two_digit_numeric));
TRY(callback(pattern.fractional_second_digits, vm.names.fractionalSecondDigits, Empty {}));
TRY(callback(pattern.time_zone_name, vm.names.timeZoneName, time_zone));
TRY(callback(Weekday, pattern.weekday, vm.names.weekday, narrow_short_long));
TRY(callback(Era, pattern.era, vm.names.era, narrow_short_long));
TRY(callback(Year, pattern.year, vm.names.year, two_digit_numeric));
TRY(callback(Month, pattern.month, vm.names.month, two_digit_numeric_narrow_short_long));
TRY(callback(Day, pattern.day, vm.names.day, two_digit_numeric));
TRY(callback(DayPeriod, pattern.day_period, vm.names.dayPeriod, narrow_short_long));
TRY(callback(Hour, pattern.hour, vm.names.hour, two_digit_numeric));
TRY(callback(Minute, pattern.minute, vm.names.minute, two_digit_numeric));
TRY(callback(Second, pattern.second, vm.names.second, two_digit_numeric));
TRY(callback(FractionalSecondDigits, pattern.fractional_second_digits, vm.names.fractionalSecondDigits, Empty {}));
TRY(callback(TimeZoneName, pattern.time_zone_name, vm.names.timeZoneName, time_zone));
return {};
}

View file

@ -233,7 +233,7 @@ ThrowCompletionOr<GC::Ref<DateTimeFormat>> create_date_time_format(VM& vm, Funct
PropertyKey const* explicit_format_component = nullptr;
// 25. For each row of Table 16, except the header row, in table order, do
TRY(for_each_calendar_field(vm, format_options, [&](auto& option, PropertyKey const& property, auto const& values) -> ThrowCompletionOr<void> {
TRY(for_each_calendar_field(vm, format_options, [&](auto, auto& option, PropertyKey const& property, auto const& values) -> ThrowCompletionOr<void> {
using ValueType = typename RemoveReference<decltype(option)>::ValueType;
// a. Let prop be the name given in the Property column of the current row.
@ -327,16 +327,16 @@ ThrowCompletionOr<GC::Ref<DateTimeFormat>> create_date_time_format(VM& vm, Funct
// f. If dateStyle is not undefined, then
if (!date_style.is_undefined()) {
// i. Set dateTimeFormat.[[TemporalPlainDateFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[weekday]], [[era]], [[year]], [[month]], [[day]] »).
auto temporal_plain_date_format = adjust_date_time_style_format(best_format, { { Weekday, Era, Year, Month, Day } });
// i. Set dateTimeFormat.[[TemporalPlainDateFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « "weekday", "era", "year", "month", "day" »).
auto temporal_plain_date_format = adjust_date_time_style_format(vm, best_format, { { Weekday, Era, Year, Month, Day } });
date_time_format->set_temporal_plain_date_format(move(temporal_plain_date_format));
// ii. Set dateTimeFormat.[[TemporalPlainYearMonthFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[era]], [[year]], [[month]] »).
auto temporal_plain_year_month_format = adjust_date_time_style_format(best_format, { { Era, Year, Month } });
// ii. Set dateTimeFormat.[[TemporalPlainYearMonthFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « "era", "year", "month" »).
auto temporal_plain_year_month_format = adjust_date_time_style_format(vm, best_format, { { Era, Year, Month } });
date_time_format->set_temporal_plain_year_month_format(move(temporal_plain_year_month_format));
// iii. Set dateTimeFormat.[[TemporalPlainMonthDayFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[month]], [[day]] »).
auto temporal_plain_month_day_format = adjust_date_time_style_format(best_format, { { Month, Day } });
// iii. Set dateTimeFormat.[[TemporalPlainMonthDayFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « "month", "day" »).
auto temporal_plain_month_day_format = adjust_date_time_style_format(vm, best_format, { { Month, Day } });
date_time_format->set_temporal_plain_month_day_format(move(temporal_plain_month_day_format));
}
// g. Else,
@ -348,8 +348,8 @@ ThrowCompletionOr<GC::Ref<DateTimeFormat>> create_date_time_format(VM& vm, Funct
// h. If timeStyle is not undefined, then
if (!time_style.is_undefined()) {
// i. Set dateTimeFormat.[[TemporalPlainTimeFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[dayPeriod]], [[hour]], [[minute]], [[second]], [[fractionalSecondDigits]] »).
auto temporal_plain_time_format = adjust_date_time_style_format(best_format, { { DayPeriod, Hour, Minute, Second, FractionalSecondDigits } });
// i. Set dateTimeFormat.[[TemporalPlainTimeFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits" »).
auto temporal_plain_time_format = adjust_date_time_style_format(vm, best_format, { { DayPeriod, Hour, Minute, Second, FractionalSecondDigits } });
date_time_format->set_temporal_plain_time_format(move(temporal_plain_time_format));
}
// i. Else,
@ -357,8 +357,8 @@ ThrowCompletionOr<GC::Ref<DateTimeFormat>> create_date_time_format(VM& vm, Funct
// i. Set dateTimeFormat.[[TemporalPlainTimeFormat]] to null.
}
// j. Set dateTimeFormat.[[TemporalPlainDateTimeFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[weekday]], [[era]], [[year]], [[month]], [[day]], [[dayPeriod]], [[hour]], [[minute]], [[second]], [[fractionalSecondDigits]] »).
auto temporal_plain_date_time_format = adjust_date_time_style_format(best_format, { { Weekday, Era, Year, Month, Day, DayPeriod, Hour, Minute, Second, FractionalSecondDigits } });
// j. Set dateTimeFormat.[[TemporalPlainDateTimeFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « "weekday", "era", "year", "month", "day", "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits" »).
auto temporal_plain_date_time_format = adjust_date_time_style_format(vm, best_format, { { Weekday, Era, Year, Month, Day, DayPeriod, Hour, Minute, Second, FractionalSecondDigits } });
date_time_format->set_temporal_plain_date_time_format(move(temporal_plain_date_time_format));
// k. Set dateTimeFormat.[[TemporalInstantFormat]] to bestFormat.

View file

@ -94,7 +94,7 @@ JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::resolved_options)
}
if (!date_time_format->has_date_style() && !date_time_format->has_time_style()) {
MUST(for_each_calendar_field(vm, date_time_format->date_time_format(), [&](auto& option, auto const& property, auto const&) -> ThrowCompletionOr<void> {
MUST(for_each_calendar_field(vm, date_time_format->date_time_format(), [&](auto, auto& option, auto const& property, auto const&) -> ThrowCompletionOr<void> {
using ValueType = typename RemoveReference<decltype(option)>::ValueType;
if (!option.has_value())