mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 05:38:11 +00:00
LibUnicode: Implement TR-35's localized GMT offset formatting
This adds an API to use LibTimeZone to convert a time zone such as "America/New_York" to a GMT offset string like "GMT-5" (short form) or "GMT-05:00" (long form).
This commit is contained in:
parent
6409900a5b
commit
8d35563f28
5 changed files with 238 additions and 20 deletions
|
@ -6,8 +6,11 @@
|
|||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibTimeZone/TimeZone.h>
|
||||
#include <LibUnicode/DateTimeFormat.h>
|
||||
#include <LibUnicode/Locale.h>
|
||||
#include <LibUnicode/NumberFormat.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace Unicode {
|
||||
|
||||
|
@ -191,6 +194,83 @@ Optional<StringView> __attribute__((weak)) get_calendar_month_symbol(StringView,
|
|||
Optional<StringView> __attribute__((weak)) get_calendar_weekday_symbol(StringView, StringView, CalendarPatternStyle, Weekday) { return {}; }
|
||||
Optional<StringView> __attribute__((weak)) get_calendar_day_period_symbol(StringView, StringView, CalendarPatternStyle, DayPeriod) { return {}; }
|
||||
Optional<StringView> __attribute__((weak)) get_calendar_day_period_symbol_for_hour(StringView, StringView, CalendarPatternStyle, u8) { return {}; }
|
||||
|
||||
Optional<StringView> __attribute__((weak)) get_time_zone_name(StringView, StringView, CalendarPatternStyle) { return {}; }
|
||||
Optional<TimeZoneFormat> __attribute__((weak)) get_time_zone_format(StringView) { return {}; }
|
||||
|
||||
static Optional<String> format_time_zone_offset(StringView locale, StringView time_zone, CalendarPatternStyle style, AK::Time time)
|
||||
{
|
||||
auto formats = get_time_zone_format(locale);
|
||||
if (!formats.has_value())
|
||||
return {};
|
||||
|
||||
auto number_system = get_default_number_system(locale);
|
||||
if (!number_system.has_value())
|
||||
return {};
|
||||
|
||||
auto offset_seconds = TimeZone::get_time_zone_offset(time_zone, time);
|
||||
if (!offset_seconds.has_value())
|
||||
return {};
|
||||
if (*offset_seconds == 0)
|
||||
return formats->gmt_zero_format;
|
||||
|
||||
auto sign = *offset_seconds > 0 ? formats->symbol_ahead_sign : formats->symbol_behind_sign;
|
||||
auto separator = *offset_seconds > 0 ? formats->symbol_ahead_separator : formats->symbol_behind_separator;
|
||||
*offset_seconds = llabs(*offset_seconds);
|
||||
|
||||
auto offset_hours = *offset_seconds / 3'600;
|
||||
*offset_seconds %= 3'600;
|
||||
|
||||
auto offset_minutes = *offset_seconds / 60;
|
||||
*offset_seconds %= 60;
|
||||
|
||||
StringBuilder builder;
|
||||
builder.append(sign);
|
||||
|
||||
switch (style) {
|
||||
// The long format always uses 2-digit hours field and minutes field, with optional 2-digit seconds field.
|
||||
case CalendarPatternStyle::LongOffset:
|
||||
builder.appendff("{:02}{}{:02}", offset_hours, separator, offset_minutes);
|
||||
if (*offset_seconds > 0)
|
||||
builder.appendff("{}{:02}", separator, *offset_seconds);
|
||||
break;
|
||||
|
||||
// The short format is intended for the shortest representation and uses hour fields without leading zero, with optional 2-digit minutes and seconds fields.
|
||||
case CalendarPatternStyle::ShortOffset:
|
||||
builder.appendff("{}", offset_hours);
|
||||
if (offset_minutes > 0) {
|
||||
builder.appendff("{}{:02}", separator, offset_minutes);
|
||||
if (*offset_seconds > 0)
|
||||
builder.appendff("{}{:02}", separator, *offset_seconds);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// The digits used for hours, minutes and seconds fields in this format are the locale's default decimal digits.
|
||||
auto offset = replace_digits_for_number_system(*number_system, builder.build());
|
||||
return formats->gmt_format.replace("{0}"sv, offset);
|
||||
}
|
||||
|
||||
// https://unicode.org/reports/tr35/tr35-dates.html#Time_Zone_Format_Terminology
|
||||
String format_time_zone(StringView locale, StringView time_zone, CalendarPatternStyle style, AK::Time time)
|
||||
{
|
||||
switch (style) {
|
||||
case CalendarPatternStyle::Short:
|
||||
case CalendarPatternStyle::Long:
|
||||
case CalendarPatternStyle::ShortGeneric:
|
||||
case CalendarPatternStyle::LongGeneric:
|
||||
return get_time_zone_name(locale, time_zone, style).value_or(time_zone);
|
||||
|
||||
case CalendarPatternStyle::ShortOffset:
|
||||
case CalendarPatternStyle::LongOffset:
|
||||
return format_time_zone_offset(locale, time_zone, style, time).value_or(time_zone);
|
||||
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue