From 05de9b82b871c3967e7c739c2d21db0621248ded Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Mon, 10 Jan 2022 08:02:01 -0500 Subject: [PATCH] LibJS: Include hour-cycle in DateTimeFormat options This is a normative change to the Intl spec: https://github.com/tc39/ecma402/commit/20e5c26 Note that this doesn't actually affect us. Its purpose is to provide the hour-cycle to BestFitFormatMatcher. This AO is implementation defined, and ours just invokes BasicFormatMatcher, which doesn't use this field. We could now have LibUnicode generate this field and use it to find a better format pattern, though. --- .../LibJS/Runtime/Intl/DateTimeFormat.cpp | 179 +++++++++--------- 1 file changed, 87 insertions(+), 92 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp index 99538d894bc..846c45cfa5a 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp @@ -179,7 +179,7 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo // 27. Set dateTimeFormat.[[TimeZone]] to timeZone. date_time_format.set_time_zone(move(time_zone)); - // 28. Let opt be a new Record. + // 28. Let formatOptions be a new Record. Unicode::CalendarPattern format_options {}; // 29. For each row of Table 4, except the header row, in table order, do @@ -193,7 +193,7 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo // i. Let value be ? GetNumberOption(options, "fractionalSecondDigits", 1, 3, undefined). auto value = TRY(get_number_option(global_object, *options, property, 1, 3, {})); - // d. Set opt.[[]] to value. + // d. Set formatOptions.[[]] to value. if (value.has_value()) option = static_cast(value.value()); } @@ -202,7 +202,7 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo // i. Let value be ? GetOption(options, prop, "string", « the strings given in the Values column of the row », undefined). auto value = TRY(get_option(global_object, *options, property, Value::Type::String, defaults, Empty {})); - // d. Set opt.[[]] to value. + // d. Set formatOptions.[[]] to value. if (!value.is_undefined()) option = Unicode::calendar_pattern_style_from_string(value.as_string().string()); } @@ -212,31 +212,81 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo // 30. Let dataLocaleData be localeData.[[]]. - // 31. Let matcher be ? GetOption(options, "formatMatcher", "string", « "basic", "best fit" », "best fit"). + // 31. Let hcDefault be dataLocaleData.[[hourCycle]]. + auto default_hour_cycle = Unicode::get_default_regional_hour_cycle(data_locale); + + // Non-standard, default_hour_cycle will be empty if Unicode data generation is disabled. + if (!default_hour_cycle.has_value()) + return &date_time_format; + + // 32. Let hc be dateTimeFormat.[[HourCycle]]. + // 33. If hc is null, then + // a. Set hc to hcDefault. + auto hour_cycle_value = date_time_format.has_hour_cycle() ? date_time_format.hour_cycle() : *default_hour_cycle; + + // 34. If hour12 is true, then + if (hour12.is_boolean() && hour12.as_bool()) { + // a. If hcDefault is "h11" or "h23", then + if ((default_hour_cycle == Unicode::HourCycle::H11) || (default_hour_cycle == Unicode::HourCycle::H23)) { + // i. Set hc to "h11". + hour_cycle_value = Unicode::HourCycle::H11; + } + // b. Else, + else { + // i. Set hc to "h12". + hour_cycle_value = Unicode::HourCycle::H12; + } + } + // 35. Else if hour12 is false, then + else if (hour12.is_boolean() && !hour12.as_bool()) { + // a. If hcDefault is "h11" or "h23", then + if ((default_hour_cycle == Unicode::HourCycle::H11) || (default_hour_cycle == Unicode::HourCycle::H23)) { + // i. Set hc to "h23". + hour_cycle_value = Unicode::HourCycle::H23; + } + // b. Else, + else { + // i. Set hc to "h24". + hour_cycle_value = Unicode::HourCycle::H24; + } + } + // 36. Else, + else { + // a. Assert: hour12 is undefined. + VERIFY(hour12.is_undefined()); + } + + // 37. Set dateTimeFormat.[[HourCycle]] to hc. + date_time_format.set_hour_cycle(hour_cycle_value); + + // 38. Set formatOptions.[[hourCycle]] to hc. + format_options.hour_cycle = hour_cycle_value; + + // 39. Let matcher be ? GetOption(options, "formatMatcher", "string", « "basic", "best fit" », "best fit"). matcher = TRY(get_option(global_object, *options, vm.names.formatMatcher, Value::Type::String, AK::Array { "basic"sv, "best fit"sv }, "best fit"sv)); - // 32. Let dateStyle be ? GetOption(options, "dateStyle", "string", « "full", "long", "medium", "short" », undefined). + // 40. Let dateStyle be ? GetOption(options, "dateStyle", "string", « "full", "long", "medium", "short" », undefined). auto date_style = TRY(get_option(global_object, *options, vm.names.dateStyle, Value::Type::String, AK::Array { "full"sv, "long"sv, "medium"sv, "short"sv }, Empty {})); - // 33. Set dateTimeFormat.[[DateStyle]] to dateStyle. + // 41. Set dateTimeFormat.[[DateStyle]] to dateStyle. if (!date_style.is_undefined()) date_time_format.set_date_style(date_style.as_string().string()); - // 34. Let timeStyle be ? GetOption(options, "timeStyle", "string", « "full", "long", "medium", "short" », undefined). + // 42. Let timeStyle be ? GetOption(options, "timeStyle", "string", « "full", "long", "medium", "short" », undefined). auto time_style = TRY(get_option(global_object, *options, vm.names.timeStyle, Value::Type::String, AK::Array { "full"sv, "long"sv, "medium"sv, "short"sv }, Empty {})); - // 35. Set dateTimeFormat.[[TimeStyle]] to timeStyle. + // 43. Set dateTimeFormat.[[TimeStyle]] to timeStyle. if (!time_style.is_undefined()) date_time_format.set_time_style(time_style.as_string().string()); Optional best_format {}; - // 36. If dateStyle is not undefined or timeStyle is not undefined, then + // 44. If dateStyle is not undefined or timeStyle is not undefined, then if (date_time_format.has_date_style() || date_time_format.has_time_style()) { // a. For each row in Table 4, except the header row, do TRY(for_each_calendar_field(global_object, format_options, [&](auto const& option, auto const& property, auto const&) -> ThrowCompletionOr { // i. Let prop be the name given in the Property column of the row. - // ii. Let p be opt.[[]]. + // ii. Let p be formatOptions.[[]]. // iii. If p is not undefined, then if (option.has_value()) { // 1. Throw a TypeError exception. @@ -250,28 +300,24 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo // c. Let bestFormat be DateTimeStyleFormat(dateStyle, timeStyle, styles). best_format = date_time_style_format(data_locale, date_time_format); } - // 37. Else, + // 45. Else, else { // a. Let formats be dataLocaleData.[[formats]].[[]]. auto formats = Unicode::get_calendar_available_formats(data_locale, date_time_format.calendar()); // b. If matcher is "basic", then if (matcher.as_string().string() == "basic"sv) { - // i. Let bestFormat be BasicFormatMatcher(opt, formats). + // i. Let bestFormat be BasicFormatMatcher(formatOptions, formats). best_format = basic_format_matcher(format_options, move(formats)); } // c. Else, else { - // i. Let bestFormat be BestFitFormatMatcher(opt, formats). + // i. Let bestFormat be BestFitFormatMatcher(formatOptions, formats). best_format = best_fit_format_matcher(format_options, move(formats)); } } - // Non-standard, best_format will be empty if Unicode data generation is disabled. - if (!best_format.has_value()) - return &date_time_format; - - // 38. For each row in Table 4, except the header row, in table order, do + // 46. For each row in Table 4, except the header row, in table order, do date_time_format.for_each_calendar_field_zipped_with(*best_format, [&](auto& date_time_format_field, auto const& best_format_field, auto) { // a. Let prop be the name given in the Property column of the row. // b. If bestFormat has a field [[]], then @@ -285,93 +331,42 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo String pattern; Vector range_patterns; - // 39. If dateTimeFormat.[[Hour]] is undefined, then + // 47. If dateTimeFormat.[[Hour]] is undefined, then if (!date_time_format.has_hour()) { // a. Set dateTimeFormat.[[HourCycle]] to undefined. date_time_format.clear_hour_cycle(); + } - // b. Let pattern be bestFormat.[[pattern]]. + // 48. If dateTimeformat.[[HourCycle]] is "h11" or "h12", then + if ((hour_cycle_value == Unicode::HourCycle::H11) || (hour_cycle_value == Unicode::HourCycle::H12)) { + // a. Let pattern be bestFormat.[[pattern12]]. + if (best_format->pattern12.has_value()) { + pattern = best_format->pattern12.release_value(); + } else { + // Non-standard, LibUnicode only provides [[pattern12]] when [[pattern]] has a day + // period. Other implementations provide [[pattern12]] as a copy of [[pattern]]. + pattern = move(best_format->pattern); + } + + // b. Let rangePatterns be bestFormat.[[rangePatterns12]]. + range_patterns = Unicode::get_calendar_range12_formats(data_locale, date_time_format.calendar(), best_format->skeleton); + } + // 49. Else, + else { + // a. Let pattern be bestFormat.[[pattern]]. pattern = move(best_format->pattern); - // c. Let rangePatterns be bestFormat.[[rangePatterns]]. + // b. Let rangePatterns be bestFormat.[[rangePatterns]]. range_patterns = Unicode::get_calendar_range_formats(data_locale, date_time_format.calendar(), best_format->skeleton); } - // 40. Else, - else { - // a. Let hcDefault be dataLocaleData.[[hourCycle]]. - auto default_hour_cycle = Unicode::get_default_regional_hour_cycle(data_locale); - VERIFY(default_hour_cycle.has_value()); - // b. Let hc be dateTimeFormat.[[HourCycle]]. - // c. If hc is null, then - // i. Set hc to hcDefault. - auto hour_cycle = date_time_format.has_hour_cycle() ? date_time_format.hour_cycle() : *default_hour_cycle; - - // d. If hour12 is not undefined, then - if (!hour12.is_undefined()) { - // i. If hour12 is true, then - if (hour12.as_bool()) { - // 1. If hcDefault is "h11" or "h23", then - if ((default_hour_cycle == Unicode::HourCycle::H11) || (default_hour_cycle == Unicode::HourCycle::H23)) { - // a. Set hc to "h11". - hour_cycle = Unicode::HourCycle::H11; - } - // 2. Else, - else { - // a. Set hc to "h12". - hour_cycle = Unicode::HourCycle::H12; - } - } - // ii. Else, - else { - // 1. Assert: hour12 is false. - // 2. If hcDefault is "h11" or "h23", then - if ((default_hour_cycle == Unicode::HourCycle::H11) || (default_hour_cycle == Unicode::HourCycle::H23)) { - // a. Set hc to "h23". - hour_cycle = Unicode::HourCycle::H23; - } - // 3. Else, - else { - // a. Set hc to "h24". - hour_cycle = Unicode::HourCycle::H24; - } - } - } - - // e. Set dateTimeFormat.[[HourCycle]] to hc. - date_time_format.set_hour_cycle(hour_cycle); - - // f. If dateTimeformat.[[HourCycle]] is "h11" or "h12", then - if ((hour_cycle == Unicode::HourCycle::H11) || (hour_cycle == Unicode::HourCycle::H12)) { - // i. Let pattern be bestFormat.[[pattern12]]. - if (best_format->pattern12.has_value()) { - pattern = best_format->pattern12.release_value(); - } else { - // Non-standard, LibUnicode only provides [[pattern12]] when [[pattern]] has a day - // period. Other implementations provide [[pattern12]] as a copy of [[pattern]]. - pattern = move(best_format->pattern); - } - - // ii. Let rangePatterns be bestFormat.[[rangePatterns12]]. - range_patterns = Unicode::get_calendar_range12_formats(data_locale, date_time_format.calendar(), best_format->skeleton); - } - // g. Else, - else { - // i. Let pattern be bestFormat.[[pattern]]. - pattern = move(best_format->pattern); - - // ii. Let rangePatterns be bestFormat.[[rangePatterns]]. - range_patterns = Unicode::get_calendar_range_formats(data_locale, date_time_format.calendar(), best_format->skeleton); - } - } - - // 41. Set dateTimeFormat.[[Pattern]] to pattern. + // 50. Set dateTimeFormat.[[Pattern]] to pattern. date_time_format.set_pattern(move(pattern)); - // 42. Set dateTimeFormat.[[RangePatterns]] to rangePatterns. + // 51. Set dateTimeFormat.[[RangePatterns]] to rangePatterns. date_time_format.set_range_patterns(move(range_patterns)); - // 43. Return dateTimeFormat. + // 52. Return dateTimeFormat. return &date_time_format; }