1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 07:07:34 +00:00

LibLocale+LibJS+ClockSettings: Make date time format APIs infallible

These APIs only perform small allocations, and are only used by LibJS
and the time zone settings widget. Callers which could only have failed
from these APIs are also made to be infallible here.
This commit is contained in:
Timothy Flynn 2023-08-22 16:41:36 -04:00 committed by Andreas Kling
parent 0914e86691
commit 7536648498
9 changed files with 183 additions and 191 deletions

View file

@ -73,8 +73,8 @@ ThrowCompletionOr<Optional<::Locale::CalendarPattern>> date_time_style_format(VM
::Locale::CalendarPattern time_format {};
::Locale::CalendarPattern date_format {};
auto get_pattern = [&](auto type, auto style) -> ThrowCompletionOr<Optional<::Locale::CalendarPattern>> {
auto formats = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_format(data_locale, date_time_format.calendar(), type));
auto get_pattern = [&](auto type, auto style) -> Optional<::Locale::CalendarPattern> {
auto formats = ::Locale::get_calendar_format(data_locale, date_time_format.calendar(), type);
if (formats.has_value()) {
switch (style) {
@ -89,14 +89,14 @@ ThrowCompletionOr<Optional<::Locale::CalendarPattern>> date_time_style_format(VM
}
}
return OptionalNone {};
return {};
};
// 1. If timeStyle is not undefined, then
if (date_time_format.has_time_style()) {
// a. Assert: timeStyle is one of "full", "long", "medium", or "short".
// b. Let timeFormat be styles.[[TimeFormat]].[[<timeStyle>]].
auto pattern = MUST_OR_THROW_OOM(get_pattern(::Locale::CalendarFormatType::Time, date_time_format.time_style()));
auto pattern = get_pattern(::Locale::CalendarFormatType::Time, date_time_format.time_style());
if (!pattern.has_value())
return OptionalNone {};
@ -107,7 +107,7 @@ ThrowCompletionOr<Optional<::Locale::CalendarPattern>> date_time_style_format(VM
if (date_time_format.has_date_style()) {
// a. Assert: dateStyle is one of "full", "long", "medium", or "short".
// b. Let dateFormat be styles.[[DateFormat]].[[<dateStyle>]].
auto pattern = MUST_OR_THROW_OOM(get_pattern(::Locale::CalendarFormatType::Date, date_time_format.date_style()));
auto pattern = get_pattern(::Locale::CalendarFormatType::Date, date_time_format.date_style());
if (!pattern.has_value())
return OptionalNone {};
@ -131,7 +131,7 @@ ThrowCompletionOr<Optional<::Locale::CalendarPattern>> date_time_style_format(VM
});
// d. Let connector be styles.[[DateTimeFormat]].[[<dateStyle>]].
auto connector = MUST_OR_THROW_OOM(get_pattern(::Locale::CalendarFormatType::DateTime, date_time_format.date_style()));
auto connector = get_pattern(::Locale::CalendarFormatType::DateTime, date_time_format.date_style());
if (!connector.has_value())
return OptionalNone {};
@ -155,7 +155,7 @@ ThrowCompletionOr<Optional<::Locale::CalendarPattern>> date_time_style_format(VM
// NOTE: Our implementation of steps h-j differ from the spec. LibUnicode does not attach range patterns to the
// format pattern; rather, lookups for range patterns are performed separately based on the format pattern's
// skeleton. So we form a new skeleton here and defer the range pattern lookups.
format.skeleton = TRY_OR_THROW_OOM(vm, ::Locale::combine_skeletons(date_format.skeleton, time_format.skeleton));
format.skeleton = ::Locale::combine_skeletons(date_format.skeleton, time_format.skeleton);
// k. Return format.
return format;
@ -424,7 +424,7 @@ static Optional<StyleAndValue> find_calendar_field(StringView name, ::Locale::Ca
return {};
}
static ThrowCompletionOr<Optional<StringView>> resolve_day_period(VM& vm, StringView locale, StringView calendar, ::Locale::CalendarPatternStyle style, ReadonlySpan<PatternPartition> pattern_parts, LocalTime local_time)
static Optional<StringView> resolve_day_period(StringView locale, StringView calendar, ::Locale::CalendarPatternStyle style, ReadonlySpan<PatternPartition> pattern_parts, LocalTime local_time)
{
// Use the "noon" day period if the locale has it, but only if the time is either exactly 12:00.00 or would be displayed as such.
if (local_time.hour == 12) {
@ -439,13 +439,13 @@ static ThrowCompletionOr<Optional<StringView>> resolve_day_period(VM& vm, String
});
if (it == pattern_parts.end()) {
auto noon_symbol = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_day_period_symbol(locale, calendar, style, ::Locale::DayPeriod::Noon));
auto noon_symbol = ::Locale::get_calendar_day_period_symbol(locale, calendar, style, ::Locale::DayPeriod::Noon);
if (noon_symbol.has_value())
return *noon_symbol;
}
}
return TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_day_period_symbol_for_hour(locale, calendar, style, local_time.hour));
return ::Locale::get_calendar_day_period_symbol_for_hour(locale, calendar, style, local_time.hour);
}
// 11.5.5 FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions ), https://tc39.es/ecma402/#sec-formatdatetimepattern
@ -552,7 +552,7 @@ ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(VM& vm, Dat
auto style = date_time_format.day_period();
// ii. Let fv be a String value representing the day period of tm in the form given by f; the String value depends upon the implementation and the effective locale of dateTimeFormat.
auto symbol = MUST_OR_THROW_OOM(resolve_day_period(vm, data_locale, date_time_format.calendar(), style, pattern_parts, local_time));
auto symbol = resolve_day_period(data_locale, date_time_format.calendar(), style, pattern_parts, local_time);
if (symbol.has_value())
formatted_value = TRY_OR_THROW_OOM(vm, String::from_utf8(*symbol));
@ -571,7 +571,7 @@ ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(VM& vm, Dat
// iii. Let fv be a String value representing v in the form given by f; the String value depends upon the implementation and the effective locale of dateTimeFormat.
// The String value may also depend on the value of the [[InDST]] field of tm if f is "short", "long", "shortOffset", or "longOffset".
// If the implementation does not have a localized representation of f, then use the String value of v itself.
auto formatted_value = TRY_OR_THROW_OOM(vm, ::Locale::format_time_zone(data_locale, value, style, local_time.time_since_epoch()));
auto formatted_value = ::Locale::format_time_zone(data_locale, value, style, local_time.time_since_epoch());
// iv. Append a new Record { [[Type]]: p, [[Value]]: fv } as the last element of the list result.
TRY_OR_THROW_OOM(vm, result.try_append({ "timeZoneName"sv, move(formatted_value) }));
@ -652,11 +652,11 @@ ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(VM& vm, Dat
Optional<StringView> symbol;
if (part == "era"sv)
symbol = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_era_symbol(data_locale, date_time_format.calendar(), style, static_cast<::Locale::Era>(value)));
symbol = ::Locale::get_calendar_era_symbol(data_locale, date_time_format.calendar(), style, static_cast<::Locale::Era>(value));
else if (part == "month"sv)
symbol = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_month_symbol(data_locale, date_time_format.calendar(), style, static_cast<::Locale::Month>(value - 1)));
symbol = ::Locale::get_calendar_month_symbol(data_locale, date_time_format.calendar(), style, static_cast<::Locale::Month>(value - 1));
else if (part == "weekday"sv)
symbol = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_weekday_symbol(data_locale, date_time_format.calendar(), style, static_cast<::Locale::Weekday>(value)));
symbol = ::Locale::get_calendar_weekday_symbol(data_locale, date_time_format.calendar(), style, static_cast<::Locale::Weekday>(value));
if (symbol.has_value())
formatted_value = TRY_OR_THROW_OOM(vm, String::from_utf8(*symbol));
@ -684,13 +684,13 @@ ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(VM& vm, Dat
// ii. If v is greater than 11, then
if (value > 11) {
// 1. Let fv be an implementation and locale dependent String value representing "post meridiem".
auto symbol = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_day_period_symbol(data_locale, date_time_format.calendar(), ::Locale::CalendarPatternStyle::Short, ::Locale::DayPeriod::PM));
auto symbol = ::Locale::get_calendar_day_period_symbol(data_locale, date_time_format.calendar(), ::Locale::CalendarPatternStyle::Short, ::Locale::DayPeriod::PM);
formatted_value = TRY_OR_THROW_OOM(vm, String::from_utf8(symbol.value_or("PM"sv)));
}
// iii. Else,
else {
// 1. Let fv be an implementation and locale dependent String value representing "ante meridiem".
auto symbol = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_day_period_symbol(data_locale, date_time_format.calendar(), ::Locale::CalendarPatternStyle::Short, ::Locale::DayPeriod::AM));
auto symbol = ::Locale::get_calendar_day_period_symbol(data_locale, date_time_format.calendar(), ::Locale::CalendarPatternStyle::Short, ::Locale::DayPeriod::AM);
formatted_value = TRY_OR_THROW_OOM(vm, String::from_utf8(symbol.value_or("AM"sv)));
}
@ -806,30 +806,29 @@ ThrowCompletionOr<Array*> format_date_time_to_parts(VM& vm, DateTimeFormat& date
}
template<typename Callback>
ThrowCompletionOr<void> for_each_range_pattern_field(LocalTime const& time1, LocalTime const& time2, Callback&& callback)
void for_each_range_pattern_field(LocalTime const& time1, LocalTime const& time2, Callback&& callback)
{
// Table 4: Range pattern fields, https://tc39.es/ecma402/#table-datetimeformat-rangepatternfields
if (TRY(callback(static_cast<u8>(time1.era), static_cast<u8>(time2.era), ::Locale::CalendarRangePattern::Field::Era)) == IterationDecision::Break)
return {};
if (TRY(callback(time1.year, time2.year, ::Locale::CalendarRangePattern::Field::Year)) == IterationDecision::Break)
return {};
if (TRY(callback(time1.month, time2.month, ::Locale::CalendarRangePattern::Field::Month)) == IterationDecision::Break)
return {};
if (TRY(callback(time1.day, time2.day, ::Locale::CalendarRangePattern::Field::Day)) == IterationDecision::Break)
return {};
if (TRY(callback(time1.hour, time2.hour, ::Locale::CalendarRangePattern::Field::AmPm)) == IterationDecision::Break)
return {};
if (TRY(callback(time1.hour, time2.hour, ::Locale::CalendarRangePattern::Field::DayPeriod)) == IterationDecision::Break)
return {};
if (TRY(callback(time1.hour, time2.hour, ::Locale::CalendarRangePattern::Field::Hour)) == IterationDecision::Break)
return {};
if (TRY(callback(time1.minute, time2.minute, ::Locale::CalendarRangePattern::Field::Minute)) == IterationDecision::Break)
return {};
if (TRY(callback(time1.second, time2.second, ::Locale::CalendarRangePattern::Field::Second)) == IterationDecision::Break)
return {};
if (TRY(callback(time1.millisecond, time2.millisecond, ::Locale::CalendarRangePattern::Field::FractionalSecondDigits)) == IterationDecision::Break)
return {};
return {};
if (callback(static_cast<u8>(time1.era), static_cast<u8>(time2.era), ::Locale::CalendarRangePattern::Field::Era) == IterationDecision::Break)
return;
if (callback(time1.year, time2.year, ::Locale::CalendarRangePattern::Field::Year) == IterationDecision::Break)
return;
if (callback(time1.month, time2.month, ::Locale::CalendarRangePattern::Field::Month) == IterationDecision::Break)
return;
if (callback(time1.day, time2.day, ::Locale::CalendarRangePattern::Field::Day) == IterationDecision::Break)
return;
if (callback(time1.hour, time2.hour, ::Locale::CalendarRangePattern::Field::AmPm) == IterationDecision::Break)
return;
if (callback(time1.hour, time2.hour, ::Locale::CalendarRangePattern::Field::DayPeriod) == IterationDecision::Break)
return;
if (callback(time1.hour, time2.hour, ::Locale::CalendarRangePattern::Field::Hour) == IterationDecision::Break)
return;
if (callback(time1.minute, time2.minute, ::Locale::CalendarRangePattern::Field::Minute) == IterationDecision::Break)
return;
if (callback(time1.second, time2.second, ::Locale::CalendarRangePattern::Field::Second) == IterationDecision::Break)
return;
if (callback(time1.millisecond, time2.millisecond, ::Locale::CalendarRangePattern::Field::FractionalSecondDigits) == IterationDecision::Break)
return;
}
template<typename Callback>
@ -879,7 +878,7 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
bool pattern_contains_larger_date_field = false;
// 11. While dateFieldsPracticallyEqual is true and patternContainsLargerDateField is false, repeat for each row of Table 4 in order, except the header row:
TRY(for_each_range_pattern_field(start_local_time, end_local_time, [&](auto start_value, auto end_value, auto field_name) -> ThrowCompletionOr<IterationDecision> {
for_each_range_pattern_field(start_local_time, end_local_time, [&](auto start_value, auto end_value, auto field_name) {
// a. Let fieldName be the name given in the Range Pattern Field column of the row.
// b. If rangePatterns has a field [[<fieldName>]], let rp be rangePatterns.[[<fieldName>]]; else let rp be undefined.
@ -918,10 +917,10 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
// iii. Else if fieldName is equal to [[DayPeriod]], then
case ::Locale::CalendarRangePattern::Field::DayPeriod: {
// 1. Let v1 be a String value representing the day period of tm1; the String value depends upon the implementation and the effective locale of dateTimeFormat.
auto start_period = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_day_period_symbol_for_hour(date_time_format.data_locale(), date_time_format.calendar(), ::Locale::CalendarPatternStyle::Short, start_value));
auto start_period = ::Locale::get_calendar_day_period_symbol_for_hour(date_time_format.data_locale(), date_time_format.calendar(), ::Locale::CalendarPatternStyle::Short, start_value);
// 2. Let v2 be a String value representing the day period of tm2; the String value depends upon the implementation and the effective locale of dateTimeFormat.
auto end_period = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_day_period_symbol_for_hour(date_time_format.data_locale(), date_time_format.calendar(), ::Locale::CalendarPatternStyle::Short, end_value));
auto end_period = ::Locale::get_calendar_day_period_symbol_for_hour(date_time_format.data_locale(), date_time_format.calendar(), ::Locale::CalendarPatternStyle::Short, end_value);
// 3. If v1 is not equal to v2, then
if (start_period != end_period) {
@ -981,7 +980,7 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
if (date_fields_practically_equal && !pattern_contains_larger_date_field)
return IterationDecision::Continue;
return IterationDecision::Break;
}));
});
// 12. If dateFieldsPracticallyEqual is true, then
if (date_fields_practically_equal) {
@ -1011,7 +1010,7 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
// 14. If rangePattern is undefined, then
if (!range_pattern.has_value()) {
// a. Let rangePattern be rangePatterns.[[Default]].
range_pattern = TRY_OR_THROW_OOM(vm, ::Locale::get_calendar_default_range_format(date_time_format.data_locale(), date_time_format.calendar()));
range_pattern = ::Locale::get_calendar_default_range_format(date_time_format.data_locale(), date_time_format.calendar());
// Non-standard, range_pattern will be empty if Unicode data generation is disabled.
if (!range_pattern.has_value())