diff --git a/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp b/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp index 47bf85b275..264e523bb7 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp @@ -582,6 +582,38 @@ Optional get_time_zone_offset(TimeZone time_zone, AK::Time time) return dst_offset; } +Optional> get_named_time_zone_offsets(TimeZone time_zone, AK::Time time) +{ + auto const& time_zone_offset = find_time_zone_offset(time_zone, time); + Array named_offsets; + + auto format_name = [](auto format, auto offset) -> String { + if (offset == 0) + return s_string_list[format].replace("{}"sv, ""sv); + return String::formatted(s_string_list[format], s_string_list[offset]); + }; + + auto set_named_offset = [&](auto& named_offset, auto dst_offset, auto in_dst, auto format, auto offset) { + named_offset.seconds = time_zone_offset.offset + dst_offset; + named_offset.in_dst = in_dst; + named_offset.name = format_name(format, offset); + }; + + if (time_zone_offset.dst_rule != -1) { + auto offsets = find_dst_offsets(time_zone_offset, time); + auto in_dst = offsets[1]->offset == 0 ? InDST::No : InDST::Yes; + + set_named_offset(named_offsets[0], offsets[0]->offset, InDST::No, time_zone_offset.standard_format, offsets[0]->format); + set_named_offset(named_offsets[1], offsets[1]->offset, in_dst, time_zone_offset.daylight_format, offsets[1]->format); + } else { + auto in_dst = time_zone_offset.dst_offset == 0 ? InDST::No : InDST::Yes; + set_named_offset(named_offsets[0], time_zone_offset.dst_offset, in_dst, time_zone_offset.standard_format, 0); + set_named_offset(named_offsets[1], time_zone_offset.dst_offset, in_dst, time_zone_offset.daylight_format, 0); + } + + return named_offsets; +} + Span all_time_zones() { static constexpr auto all_time_zones = Array { diff --git a/Tests/LibTimeZone/TestTimeZone.cpp b/Tests/LibTimeZone/TestTimeZone.cpp index d86aa6166f..f164db103b 100644 --- a/Tests/LibTimeZone/TestTimeZone.cpp +++ b/Tests/LibTimeZone/TestTimeZone.cpp @@ -149,6 +149,31 @@ TEST_CASE(get_time_zone_offset_with_dst) test_offset("Europe/Moscow"sv, -1589068800, offset(+1, 3, 00, 00), No); // Monday, August 25, 1919 12:00:00 AM } +TEST_CASE(get_named_time_zone_offsets) +{ + auto test_named_offsets = [](auto time_zone, i64 time, i64 expected_standard_offset, i64 expected_daylight_offset, auto expected_standard_name, auto expected_daylight_name) { + auto actual_offsets = TimeZone::get_named_time_zone_offsets(time_zone, AK::Time::from_seconds(time)); + VERIFY(actual_offsets.has_value()); + + EXPECT_EQ(actual_offsets->at(0).seconds, expected_standard_offset); + EXPECT_EQ(actual_offsets->at(1).seconds, expected_daylight_offset); + EXPECT_EQ(actual_offsets->at(0).name, expected_standard_name); + EXPECT_EQ(actual_offsets->at(1).name, expected_daylight_name); + }; + + test_named_offsets("America/New_York"sv, 1642558528, offset(-1, 5, 00, 00), offset(-1, 4, 00, 00), "EST"sv, "EDT"sv); // Wednesday, January 19, 2022 2:15:28 AM + test_named_offsets("UTC"sv, 1642558528, offset(+1, 0, 00, 00), offset(+1, 0, 00, 00), "UTC"sv, "UTC"sv); // Wednesday, January 19, 2022 2:15:28 AM + test_named_offsets("GMT"sv, 1642558528, offset(+1, 0, 00, 00), offset(+1, 0, 00, 00), "GMT"sv, "GMT"sv); // Wednesday, January 19, 2022 2:15:28 AM + + // Phoenix does not observe DST. + test_named_offsets("America/Phoenix"sv, 1642558528, offset(-1, 7, 00, 00), offset(-1, 7, 00, 00), "MST"sv, "MST"sv); // Wednesday, January 19, 2022 2:15:28 AM + + // Moscow's observed DST changed several times in 1919. + test_named_offsets("Europe/Moscow"sv, -1609459200, offset(+1, 2, 31, 19), offset(+1, 3, 31, 19), "MSK"sv, "MSD"sv); // Wednesday, January 1, 1919 12:00:00 AM + test_named_offsets("Europe/Moscow"sv, -1596412800, offset(+1, 2, 31, 19), offset(+1, 4, 31, 19), "MSK"sv, "MDST"sv); // Sunday, June 1, 1919 12:00:00 AM + test_named_offsets("Europe/Moscow"sv, -1589068800, offset(+1, 3, 00, 00), offset(+1, 4, 00, 00), "MSK"sv, "MSD"sv); // Monday, August 25, 1919 12:00:00 AM +} + #else TEST_CASE(time_zone_from_string) diff --git a/Userland/Libraries/LibTimeZone/TimeZone.cpp b/Userland/Libraries/LibTimeZone/TimeZone.cpp index 8629ca7380..af9b739b1e 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.cpp +++ b/Userland/Libraries/LibTimeZone/TimeZone.cpp @@ -4,7 +4,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include #include @@ -157,4 +156,25 @@ Optional get_time_zone_offset(StringView time_zone, AK::Time time) return {}; } +Optional> __attribute__((weak)) get_named_time_zone_offsets([[maybe_unused]] TimeZone time_zone, AK::Time) +{ +#if !ENABLE_TIME_ZONE_DATA + VERIFY(time_zone == TimeZone::UTC); + + NamedOffset utc_offset {}; + utc_offset.name = "UTC"sv; + + return Array { utc_offset, utc_offset }; +#else + return {}; +#endif +} + +Optional> get_named_time_zone_offsets(StringView time_zone, AK::Time time) +{ + if (auto maybe_time_zone = time_zone_from_string(time_zone); maybe_time_zone.has_value()) + return get_named_time_zone_offsets(*maybe_time_zone, time); + return {}; +} + } diff --git a/Userland/Libraries/LibTimeZone/TimeZone.h b/Userland/Libraries/LibTimeZone/TimeZone.h index fb4d58177a..710461f86c 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.h +++ b/Userland/Libraries/LibTimeZone/TimeZone.h @@ -6,8 +6,10 @@ #pragma once +#include #include #include +#include #include #include #include @@ -25,6 +27,10 @@ struct Offset { InDST in_dst { InDST::No }; }; +struct NamedOffset : public Offset { + String name; +}; + StringView current_time_zone(); ErrorOr change_time_zone(StringView time_zone); Span all_time_zones(); @@ -39,4 +45,7 @@ StringView daylight_savings_rule_to_string(DaylightSavingsRule daylight_savings_ Optional get_time_zone_offset(TimeZone time_zone, AK::Time time); Optional get_time_zone_offset(StringView time_zone, AK::Time time); +Optional> get_named_time_zone_offsets(TimeZone time_zone, AK::Time time); +Optional> get_named_time_zone_offsets(StringView time_zone, AK::Time time); + }