mirror of
https://github.com/RGBCube/serenity
synced 2025-06-01 16:28:11 +00:00
LibTimeZone: Parse and generate time zone abbreviation format strings
For example, today, America/New_York has the format string "E%sT" and uses US DST rules. Those rules indicate the %s should be replaced by a "D" in daylight time and "S" in standard time.
This commit is contained in:
parent
40147c48be
commit
ef0155932b
1 changed files with 45 additions and 6 deletions
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using StringIndexType = u8;
|
||||||
|
constexpr auto s_string_index_type = "u8"sv;
|
||||||
|
|
||||||
struct DateTime {
|
struct DateTime {
|
||||||
u16 year { 0 };
|
u16 year { 0 };
|
||||||
Optional<u8> month;
|
Optional<u8> month;
|
||||||
|
@ -37,6 +40,9 @@ struct TimeZoneOffset {
|
||||||
Optional<String> dst_rule;
|
Optional<String> dst_rule;
|
||||||
Optional<i32> dst_rule_index;
|
Optional<i32> dst_rule_index;
|
||||||
i64 dst_offset { 0 };
|
i64 dst_offset { 0 };
|
||||||
|
|
||||||
|
StringIndexType standard_format { 0 };
|
||||||
|
StringIndexType daylight_format { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DaylightSavingsOffset {
|
struct DaylightSavingsOffset {
|
||||||
|
@ -44,9 +50,13 @@ struct DaylightSavingsOffset {
|
||||||
u16 year_from { 0 };
|
u16 year_from { 0 };
|
||||||
u16 year_to { 0 };
|
u16 year_to { 0 };
|
||||||
DateTime in_effect;
|
DateTime in_effect;
|
||||||
|
|
||||||
|
StringIndexType format { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TimeZoneData {
|
struct TimeZoneData {
|
||||||
|
UniqueStringStorage<StringIndexType> unique_strings;
|
||||||
|
|
||||||
HashMap<String, Vector<TimeZoneOffset>> time_zones;
|
HashMap<String, Vector<TimeZoneOffset>> time_zones;
|
||||||
Vector<String> time_zone_names;
|
Vector<String> time_zone_names;
|
||||||
Vector<Alias> time_zone_aliases;
|
Vector<Alias> time_zone_aliases;
|
||||||
|
@ -80,12 +90,14 @@ struct AK::Formatter<TimeZoneOffset> : Formatter<FormatString> {
|
||||||
ErrorOr<void> format(FormatBuilder& builder, TimeZoneOffset const& time_zone_offset)
|
ErrorOr<void> format(FormatBuilder& builder, TimeZoneOffset const& time_zone_offset)
|
||||||
{
|
{
|
||||||
return Formatter<FormatString>::format(builder,
|
return Formatter<FormatString>::format(builder,
|
||||||
"{{ {}, {}, {}, {}, {} }}",
|
"{{ {}, {}, {}, {}, {}, {}, {} }}",
|
||||||
time_zone_offset.offset,
|
time_zone_offset.offset,
|
||||||
time_zone_offset.until.value_or({}),
|
time_zone_offset.until.value_or({}),
|
||||||
time_zone_offset.until.has_value(),
|
time_zone_offset.until.has_value(),
|
||||||
time_zone_offset.dst_rule_index.value_or(-1),
|
time_zone_offset.dst_rule_index.value_or(-1),
|
||||||
time_zone_offset.dst_offset);
|
time_zone_offset.dst_offset,
|
||||||
|
time_zone_offset.standard_format,
|
||||||
|
time_zone_offset.daylight_format);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,11 +106,12 @@ struct AK::Formatter<DaylightSavingsOffset> : Formatter<FormatString> {
|
||||||
ErrorOr<void> format(FormatBuilder& builder, DaylightSavingsOffset const& dst_offset)
|
ErrorOr<void> format(FormatBuilder& builder, DaylightSavingsOffset const& dst_offset)
|
||||||
{
|
{
|
||||||
return Formatter<FormatString>::format(builder,
|
return Formatter<FormatString>::format(builder,
|
||||||
"{{ {}, {}, {}, {} }}",
|
"{{ {}, {}, {}, {}, {} }}",
|
||||||
dst_offset.offset,
|
dst_offset.offset,
|
||||||
dst_offset.year_from,
|
dst_offset.year_from,
|
||||||
dst_offset.year_to,
|
dst_offset.year_to,
|
||||||
dst_offset.in_effect);
|
dst_offset.in_effect,
|
||||||
|
dst_offset.format);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -172,6 +185,19 @@ static void parse_dst_rule(StringView segment, TimeZoneOffset& time_zone)
|
||||||
time_zone.dst_rule = segment;
|
time_zone.dst_rule = segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_format(StringView format, TimeZoneData& time_zone_data, TimeZoneOffset& time_zone)
|
||||||
|
{
|
||||||
|
auto formats = format.replace("%s"sv, "{}"sv).split('/');
|
||||||
|
VERIFY(formats.size() <= 2);
|
||||||
|
|
||||||
|
time_zone.standard_format = time_zone_data.unique_strings.ensure(formats[0]);
|
||||||
|
|
||||||
|
if (formats.size() == 2)
|
||||||
|
time_zone.daylight_format = time_zone_data.unique_strings.ensure(formats[1]);
|
||||||
|
else
|
||||||
|
time_zone.daylight_format = time_zone.standard_format;
|
||||||
|
}
|
||||||
|
|
||||||
static Vector<TimeZoneOffset>& parse_zone(StringView zone_line, TimeZoneData& time_zone_data)
|
static Vector<TimeZoneOffset>& parse_zone(StringView zone_line, TimeZoneData& time_zone_data)
|
||||||
{
|
{
|
||||||
auto segments = zone_line.split_view_if([](char ch) { return (ch == '\t') || (ch == ' '); });
|
auto segments = zone_line.split_view_if([](char ch) { return (ch == '\t') || (ch == ' '); });
|
||||||
|
@ -183,6 +209,7 @@ static Vector<TimeZoneOffset>& parse_zone(StringView zone_line, TimeZoneData& ti
|
||||||
TimeZoneOffset time_zone {};
|
TimeZoneOffset time_zone {};
|
||||||
time_zone.offset = parse_time_offset(segments[2]);
|
time_zone.offset = parse_time_offset(segments[2]);
|
||||||
parse_dst_rule(segments[3], time_zone);
|
parse_dst_rule(segments[3], time_zone);
|
||||||
|
parse_format(segments[4], time_zone_data, time_zone);
|
||||||
|
|
||||||
if (segments.size() > 5)
|
if (segments.size() > 5)
|
||||||
time_zone.until = parse_date_time(segments.span().slice(5));
|
time_zone.until = parse_date_time(segments.span().slice(5));
|
||||||
|
@ -196,7 +223,7 @@ static Vector<TimeZoneOffset>& parse_zone(StringView zone_line, TimeZoneData& ti
|
||||||
return time_zones;
|
return time_zones;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_zone_continuation(StringView zone_line, Vector<TimeZoneOffset>& time_zones)
|
static void parse_zone_continuation(StringView zone_line, TimeZoneData& time_zone_data, Vector<TimeZoneOffset>& time_zones)
|
||||||
{
|
{
|
||||||
auto segments = zone_line.split_view_if([](char ch) { return (ch == '\t') || (ch == ' '); });
|
auto segments = zone_line.split_view_if([](char ch) { return (ch == '\t') || (ch == ' '); });
|
||||||
|
|
||||||
|
@ -204,6 +231,7 @@ static void parse_zone_continuation(StringView zone_line, Vector<TimeZoneOffset>
|
||||||
TimeZoneOffset time_zone {};
|
TimeZoneOffset time_zone {};
|
||||||
time_zone.offset = parse_time_offset(segments[0]);
|
time_zone.offset = parse_time_offset(segments[0]);
|
||||||
parse_dst_rule(segments[1], time_zone);
|
parse_dst_rule(segments[1], time_zone);
|
||||||
|
parse_format(segments[2], time_zone_data, time_zone);
|
||||||
|
|
||||||
if (segments.size() > 3)
|
if (segments.size() > 3)
|
||||||
time_zone.until = parse_date_time(segments.span().slice(3));
|
time_zone.until = parse_date_time(segments.span().slice(3));
|
||||||
|
@ -245,6 +273,9 @@ static void parse_rule(StringView rule_line, TimeZoneData& time_zone_data)
|
||||||
auto in_effect = Array { "0"sv, segments[5], segments[6], segments[7] };
|
auto in_effect = Array { "0"sv, segments[5], segments[6], segments[7] };
|
||||||
dst_offset.in_effect = parse_date_time(in_effect).release_value();
|
dst_offset.in_effect = parse_date_time(in_effect).release_value();
|
||||||
|
|
||||||
|
if (segments[9] != "-"sv)
|
||||||
|
dst_offset.format = time_zone_data.unique_strings.ensure(segments[9]);
|
||||||
|
|
||||||
auto& dst_offsets = time_zone_data.dst_offsets.ensure(name);
|
auto& dst_offsets = time_zone_data.dst_offsets.ensure(name);
|
||||||
dst_offsets.append(move(dst_offset));
|
dst_offsets.append(move(dst_offset));
|
||||||
|
|
||||||
|
@ -267,7 +298,7 @@ static ErrorOr<void> parse_time_zones(StringView time_zone_path, TimeZoneData& t
|
||||||
last_parsed_zone = &parse_zone(line, time_zone_data);
|
last_parsed_zone = &parse_zone(line, time_zone_data);
|
||||||
} else if (line.starts_with('\t')) {
|
} else if (line.starts_with('\t')) {
|
||||||
VERIFY(last_parsed_zone != nullptr);
|
VERIFY(last_parsed_zone != nullptr);
|
||||||
parse_zone_continuation(line, *last_parsed_zone);
|
parse_zone_continuation(line, time_zone_data, *last_parsed_zone);
|
||||||
} else {
|
} else {
|
||||||
last_parsed_zone = nullptr;
|
last_parsed_zone = nullptr;
|
||||||
|
|
||||||
|
@ -346,6 +377,7 @@ static void generate_time_zone_data_implementation(Core::File& file, TimeZoneDat
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
SourceGenerator generator { builder };
|
SourceGenerator generator { builder };
|
||||||
|
generator.set("string_index_type"sv, s_string_index_type);
|
||||||
|
|
||||||
set_dst_rule_indices(time_zone_data);
|
set_dst_rule_indices(time_zone_data);
|
||||||
|
|
||||||
|
@ -389,6 +421,9 @@ struct TimeZoneOffset {
|
||||||
|
|
||||||
i32 dst_rule { -1 };
|
i32 dst_rule { -1 };
|
||||||
i64 dst_offset { 0 };
|
i64 dst_offset { 0 };
|
||||||
|
|
||||||
|
@string_index_type@ standard_format { 0 };
|
||||||
|
@string_index_type@ daylight_format { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DaylightSavingsOffset {
|
struct DaylightSavingsOffset {
|
||||||
|
@ -396,9 +431,13 @@ struct DaylightSavingsOffset {
|
||||||
u16 year_from { 0 };
|
u16 year_from { 0 };
|
||||||
u16 year_to { 0 };
|
u16 year_to { 0 };
|
||||||
DateTime in_effect {};
|
DateTime in_effect {};
|
||||||
|
|
||||||
|
@string_index_type@ format { 0 };
|
||||||
};
|
};
|
||||||
)~~~");
|
)~~~");
|
||||||
|
|
||||||
|
time_zone_data.unique_strings.generate(generator);
|
||||||
|
|
||||||
auto append_offsets = [&](auto const& name, auto type, auto const& offsets) {
|
auto append_offsets = [&](auto const& name, auto type, auto const& offsets) {
|
||||||
generator.set("name", name);
|
generator.set("name", name);
|
||||||
generator.set("type", type);
|
generator.set("type", type);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue