diff options
-rw-r--r-- | Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp | 23 | ||||
-rw-r--r-- | Tests/LibTimeZone/TestTimeZone.cpp | 79 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Date.cpp | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibTimeZone/TimeZone.cpp | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibTimeZone/TimeZone.h | 14 | ||||
-rw-r--r-- | Userland/Libraries/LibUnicode/DateTimeFormat.cpp | 32 |
7 files changed, 90 insertions, 73 deletions
diff --git a/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp b/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp index 64e05485d3..b83d5312b1 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp @@ -450,7 +450,7 @@ static constexpr Array<@type@, @size@> @name@ { { append_string_conversions("DaylightSavingsRule"sv, "daylight_savings_rule"sv, time_zone_data.dst_offset_names); generator.append(R"~~~( -static i64 get_dst_offset(TimeZoneOffset const& time_zone_offset, AK::Time time) +static Offset get_dst_offset(TimeZoneOffset const& time_zone_offset, AK::Time time) { auto const& dst_rules = s_dst_offsets[time_zone_offset.dst_rule]; @@ -487,17 +487,17 @@ static i64 get_dst_offset(TimeZoneOffset const& time_zone_offset, AK::Time time) } if (!standard_offset || !daylight_offset) - return 0; + return {}; auto standard_time_in_effect = time_in_effect_for_rule(*standard_offset); auto daylight_time_in_effect = time_in_effect_for_rule(*daylight_offset); if ((time < daylight_time_in_effect) || (time >= standard_time_in_effect)) - return standard_offset->offset; - return daylight_offset->offset; + return { standard_offset->offset, InDST::No }; + return { daylight_offset->offset, InDST::Yes }; } -Optional<i64> get_time_zone_offset(TimeZone time_zone, AK::Time time) +Optional<Offset> get_time_zone_offset(TimeZone time_zone, AK::Time time) { auto const& time_zone_offsets = s_time_zone_offsets[to_underlying(time_zone)]; @@ -512,13 +512,16 @@ Optional<i64> get_time_zone_offset(TimeZone time_zone, AK::Time time) VERIFY(index < time_zone_offsets.size()); auto const& time_zone_offset = time_zone_offsets[index]; - i64 dst_offset = 0; - if (time_zone_offset.dst_rule != -1) + Offset dst_offset {}; + if (time_zone_offset.dst_rule != -1) { dst_offset = get_dst_offset(time_zone_offset, time); - else - dst_offset = time_zone_offset.dst_offset; + } else { + auto in_dst = time_zone_offset.dst_offset == 0 ? InDST::No : InDST::Yes; + dst_offset = { time_zone_offset.dst_offset, in_dst }; + } - return time_zone_offset.offset + dst_offset; + dst_offset.seconds += time_zone_offset.offset; + return dst_offset; } } diff --git a/Tests/LibTimeZone/TestTimeZone.cpp b/Tests/LibTimeZone/TestTimeZone.cpp index 19cfa5fc42..d86aa6166f 100644 --- a/Tests/LibTimeZone/TestTimeZone.cpp +++ b/Tests/LibTimeZone/TestTimeZone.cpp @@ -10,6 +10,8 @@ #include <AK/Time.h> #include <LibTimeZone/TimeZone.h> +using enum TimeZone::InDST; + #if ENABLE_TIME_ZONE_DATA # include <LibTimeZone/TimeZoneData.h> @@ -90,60 +92,61 @@ static i64 offset(i64 sign, i64 hours, i64 minutes, i64 seconds) return sign * ((hours * 3600) + (minutes * 60) + seconds); } -static void test_offset(StringView time_zone, i64 time, i64 expected_offset) +static void test_offset(StringView time_zone, i64 time, i64 expected_offset, TimeZone::InDST expected_in_dst) { auto actual_offset = TimeZone::get_time_zone_offset(time_zone, AK::Time::from_seconds(time)); VERIFY(actual_offset.has_value()); - EXPECT_EQ(*actual_offset, expected_offset); + EXPECT_EQ(actual_offset->seconds, expected_offset); + EXPECT_EQ(actual_offset->in_dst, expected_in_dst); } TEST_CASE(get_time_zone_offset) { - test_offset("America/Chicago"sv, -2717668237, offset(-1, 5, 50, 36)); // Sunday, November 18, 1883 12:09:23 PM - test_offset("America/Chicago"sv, -2717668236, offset(-1, 6, 00, 00)); // Sunday, November 18, 1883 12:09:24 PM - test_offset("America/Chicago"sv, -1067810460, offset(-1, 6, 00, 00)); // Sunday, March 1, 1936 1:59:00 AM - test_offset("America/Chicago"sv, -1067810400, offset(-1, 5, 00, 00)); // Sunday, March 1, 1936 2:00:00 AM - test_offset("America/Chicago"sv, -1045432860, offset(-1, 5, 00, 00)); // Sunday, November 15, 1936 1:59:00 AM - test_offset("America/Chicago"sv, -1045432800, offset(-1, 6, 00, 00)); // Sunday, November 15, 1936 2:00:00 AM - - test_offset("Europe/London"sv, -3852662401, offset(-1, 0, 01, 15)); // Tuesday, November 30, 1847 11:59:59 PM - test_offset("Europe/London"sv, -3852662400, offset(+1, 0, 00, 00)); // Wednesday, December 1, 1847 12:00:00 AM - test_offset("Europe/London"sv, -37238401, offset(+1, 0, 00, 00)); // Saturday, October 26, 1968 11:59:59 PM - test_offset("Europe/London"sv, -37238400, offset(+1, 1, 00, 00)); // Sunday, October 27, 1968 12:00:00 AM - test_offset("Europe/London"sv, 57722399, offset(+1, 1, 00, 00)); // Sunday, October 31, 1971 1:59:59 AM - test_offset("Europe/London"sv, 57722400, offset(+1, 0, 00, 00)); // Sunday, October 31, 1971 2:00:00 AM - - test_offset("UTC"sv, -1641846268, offset(+1, 0, 00, 00)); - test_offset("UTC"sv, 0, offset(+1, 0, 00, 00)); - test_offset("UTC"sv, 1641846268, offset(+1, 0, 00, 00)); - - test_offset("Etc/GMT+4"sv, -1641846268, offset(-1, 4, 00, 00)); - test_offset("Etc/GMT+5"sv, 0, offset(-1, 5, 00, 00)); - test_offset("Etc/GMT+6"sv, 1641846268, offset(-1, 6, 00, 00)); - - test_offset("Etc/GMT-12"sv, -1641846268, offset(+1, 12, 00, 00)); - test_offset("Etc/GMT-13"sv, 0, offset(+1, 13, 00, 00)); - test_offset("Etc/GMT-14"sv, 1641846268, offset(+1, 14, 00, 00)); + test_offset("America/Chicago"sv, -2717668237, offset(-1, 5, 50, 36), No); // Sunday, November 18, 1883 12:09:23 PM + test_offset("America/Chicago"sv, -2717668236, offset(-1, 6, 00, 00), No); // Sunday, November 18, 1883 12:09:24 PM + test_offset("America/Chicago"sv, -1067810460, offset(-1, 6, 00, 00), No); // Sunday, March 1, 1936 1:59:00 AM + test_offset("America/Chicago"sv, -1067810400, offset(-1, 5, 00, 00), No); // Sunday, March 1, 1936 2:00:00 AM + test_offset("America/Chicago"sv, -1045432860, offset(-1, 5, 00, 00), No); // Sunday, November 15, 1936 1:59:00 AM + test_offset("America/Chicago"sv, -1045432800, offset(-1, 6, 00, 00), No); // Sunday, November 15, 1936 2:00:00 AM + + test_offset("Europe/London"sv, -3852662401, offset(-1, 0, 01, 15), No); // Tuesday, November 30, 1847 11:59:59 PM + test_offset("Europe/London"sv, -3852662400, offset(+1, 0, 00, 00), No); // Wednesday, December 1, 1847 12:00:00 AM + test_offset("Europe/London"sv, -37238401, offset(+1, 0, 00, 00), No); // Saturday, October 26, 1968 11:59:59 PM + test_offset("Europe/London"sv, -37238400, offset(+1, 1, 00, 00), No); // Sunday, October 27, 1968 12:00:00 AM + test_offset("Europe/London"sv, 57722399, offset(+1, 1, 00, 00), No); // Sunday, October 31, 1971 1:59:59 AM + test_offset("Europe/London"sv, 57722400, offset(+1, 0, 00, 00), No); // Sunday, October 31, 1971 2:00:00 AM + + test_offset("UTC"sv, -1641846268, offset(+1, 0, 00, 00), No); + test_offset("UTC"sv, 0, offset(+1, 0, 00, 00), No); + test_offset("UTC"sv, 1641846268, offset(+1, 0, 00, 00), No); + + test_offset("Etc/GMT+4"sv, -1641846268, offset(-1, 4, 00, 00), No); + test_offset("Etc/GMT+5"sv, 0, offset(-1, 5, 00, 00), No); + test_offset("Etc/GMT+6"sv, 1641846268, offset(-1, 6, 00, 00), No); + + test_offset("Etc/GMT-12"sv, -1641846268, offset(+1, 12, 00, 00), No); + test_offset("Etc/GMT-13"sv, 0, offset(+1, 13, 00, 00), No); + test_offset("Etc/GMT-14"sv, 1641846268, offset(+1, 14, 00, 00), No); EXPECT(!TimeZone::get_time_zone_offset("I don't exist"sv, {}).has_value()); } TEST_CASE(get_time_zone_offset_with_dst) { - test_offset("America/New_York"sv, 1642558528, offset(-1, 5, 00, 00)); // Wednesday, January 19, 2022 2:15:28 AM - test_offset("America/New_York"sv, 1663553728, offset(-1, 4, 00, 00)); // Monday, September 19, 2022 2:15:28 AM - test_offset("America/New_York"sv, 1671453238, offset(-1, 5, 00, 00)); // Monday, December 19, 2022 12:33:58 PM + test_offset("America/New_York"sv, 1642558528, offset(-1, 5, 00, 00), No); // Wednesday, January 19, 2022 2:15:28 AM + test_offset("America/New_York"sv, 1663553728, offset(-1, 4, 00, 00), Yes); // Monday, September 19, 2022 2:15:28 AM + test_offset("America/New_York"sv, 1671453238, offset(-1, 5, 00, 00), No); // Monday, December 19, 2022 12:33:58 PM // Phoenix does not observe DST. - test_offset("America/Phoenix"sv, 1642558528, offset(-1, 7, 00, 00)); // Wednesday, January 19, 2022 2:15:28 AM - test_offset("America/Phoenix"sv, 1663553728, offset(-1, 7, 00, 00)); // Monday, September 19, 2022 2:15:28 AM - test_offset("America/Phoenix"sv, 1671453238, offset(-1, 7, 00, 00)); // Monday, December 19, 2022 12:33:58 PM + test_offset("America/Phoenix"sv, 1642558528, offset(-1, 7, 00, 00), No); // Wednesday, January 19, 2022 2:15:28 AM + test_offset("America/Phoenix"sv, 1663553728, offset(-1, 7, 00, 00), No); // Monday, September 19, 2022 2:15:28 AM + test_offset("America/Phoenix"sv, 1671453238, offset(-1, 7, 00, 00), No); // Monday, December 19, 2022 12:33:58 PM // Moscow's observed DST changed several times in 1919. - test_offset("Europe/Moscow"sv, -1609459200, offset(+1, 2, 31, 19)); // Wednesday, January 1, 1919 12:00:00 AM - test_offset("Europe/Moscow"sv, -1596412800, offset(+1, 4, 31, 19)); // Sunday, June 1, 1919 12:00:00 AM - test_offset("Europe/Moscow"sv, -1592611200, offset(+1, 4, 00, 00)); // Tuesday, July 15, 1919 12:00:00 AM - test_offset("Europe/Moscow"sv, -1589068800, offset(+1, 3, 00, 00)); // Monday, August 25, 1919 12:00:00 AM + test_offset("Europe/Moscow"sv, -1609459200, offset(+1, 2, 31, 19), No); // Wednesday, January 1, 1919 12:00:00 AM + test_offset("Europe/Moscow"sv, -1596412800, offset(+1, 4, 31, 19), Yes); // Sunday, June 1, 1919 12:00:00 AM + test_offset("Europe/Moscow"sv, -1592611200, offset(+1, 4, 00, 00), Yes); // Tuesday, July 15, 1919 12:00:00 AM + test_offset("Europe/Moscow"sv, -1589068800, offset(+1, 3, 00, 00), No); // Monday, August 25, 1919 12:00:00 AM } #else @@ -159,7 +162,7 @@ TEST_CASE(time_zone_from_string) TEST_CASE(get_time_zone_offset) { - EXPECT_EQ(TimeZone::get_time_zone_offset("UTC", AK::Time::from_seconds(123456)), 0); + EXPECT_EQ(TimeZone::get_time_zone_offset("UTC", AK::Time::from_seconds(123456)), { 0, No }); EXPECT(!TimeZone::get_time_zone_offset("Europe/Paris"sv, {}).has_value()); EXPECT(!TimeZone::get_time_zone_offset("Etc/UTC"sv, {}).has_value()); diff --git a/Userland/Libraries/LibJS/Runtime/Date.cpp b/Userland/Libraries/LibJS/Runtime/Date.cpp index 77c251350b..486aa00a43 100644 --- a/Userland/Libraries/LibJS/Runtime/Date.cpp +++ b/Userland/Libraries/LibJS/Runtime/Date.cpp @@ -286,7 +286,8 @@ double local_tza(double time, [[maybe_unused]] bool is_utc, Optional<StringView> auto time_since_epoch = Value(time).is_finite_number() ? AK::Time::from_milliseconds(time) : AK::Time::max(); auto maybe_offset = TimeZone::get_time_zone_offset(time_zone, time_since_epoch); - return maybe_offset.value_or(0) * 1000; + + return maybe_offset.has_value() ? static_cast<double>(maybe_offset->seconds) * 1000 : 0; } // 21.4.1.8 LocalTime ( t ), https://tc39.es/ecma262/#sec-localtime diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp index 6d604ae84e..8754e7f906 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp @@ -175,10 +175,10 @@ i64 get_iana_time_zone_offset_nanoseconds(BigInt const& epoch_nanoseconds, Strin else time = Time::from_seconds(*seconds.to_base(10).to_int<i64>()); - auto offset_seconds = ::TimeZone::get_time_zone_offset(*time_zone, time); - VERIFY(offset_seconds.has_value()); + auto offset = ::TimeZone::get_time_zone_offset(*time_zone, time); + VERIFY(offset.has_value()); - return *offset_seconds * 1'000'000'000; + return offset->seconds * 1'000'000'000; } // 11.6.5 GetIANATimeZoneNextTransition ( epochNanoseconds, timeZoneIdentifier ), https://tc39.es/proposal-temporal/#sec-temporal-getianatimezonenexttransition diff --git a/Userland/Libraries/LibTimeZone/TimeZone.cpp b/Userland/Libraries/LibTimeZone/TimeZone.cpp index fd67552da7..c898ed365a 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.cpp +++ b/Userland/Libraries/LibTimeZone/TimeZone.cpp @@ -66,17 +66,17 @@ Optional<StringView> canonicalize_time_zone(StringView time_zone) Optional<DaylightSavingsRule> __attribute__((weak)) daylight_savings_rule_from_string(StringView) { return {}; } StringView __attribute__((weak)) daylight_savings_rule_to_string(DaylightSavingsRule) { return {}; } -Optional<i64> __attribute__((weak)) get_time_zone_offset([[maybe_unused]] TimeZone time_zone, AK::Time) +Optional<Offset> __attribute__((weak)) get_time_zone_offset([[maybe_unused]] TimeZone time_zone, AK::Time) { #if !ENABLE_TIME_ZONE_DATA VERIFY(time_zone == TimeZone::UTC); - return 0; + return Offset {}; #else return {}; #endif } -Optional<i64> get_time_zone_offset(StringView time_zone, AK::Time time) +Optional<Offset> get_time_zone_offset(StringView time_zone, AK::Time time) { if (auto maybe_time_zone = time_zone_from_string(time_zone); maybe_time_zone.has_value()) return get_time_zone_offset(*maybe_time_zone, time); diff --git a/Userland/Libraries/LibTimeZone/TimeZone.h b/Userland/Libraries/LibTimeZone/TimeZone.h index 93912695f8..6a1e7b2a81 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.h +++ b/Userland/Libraries/LibTimeZone/TimeZone.h @@ -14,6 +14,16 @@ namespace TimeZone { +enum class InDST { + No, + Yes, +}; + +struct Offset { + i64 seconds { 0 }; + InDST in_dst { InDST::No }; +}; + StringView current_time_zone(); Optional<TimeZone> time_zone_from_string(StringView time_zone); @@ -23,7 +33,7 @@ Optional<StringView> canonicalize_time_zone(StringView time_zone); Optional<DaylightSavingsRule> daylight_savings_rule_from_string(StringView daylight_savings_rule); StringView daylight_savings_rule_to_string(DaylightSavingsRule daylight_savings_rule); -Optional<i64> get_time_zone_offset(TimeZone time_zone, AK::Time time); -Optional<i64> get_time_zone_offset(StringView time_zone, AK::Time time); +Optional<Offset> get_time_zone_offset(TimeZone time_zone, AK::Time time); +Optional<Offset> get_time_zone_offset(StringView time_zone, AK::Time time); } diff --git a/Userland/Libraries/LibUnicode/DateTimeFormat.cpp b/Userland/Libraries/LibUnicode/DateTimeFormat.cpp index 1229760695..9c635bb98d 100644 --- a/Userland/Libraries/LibUnicode/DateTimeFormat.cpp +++ b/Userland/Libraries/LibUnicode/DateTimeFormat.cpp @@ -208,21 +208,21 @@ static Optional<String> format_time_zone_offset(StringView locale, StringView ti if (!number_system.has_value()) return {}; - auto offset_seconds = TimeZone::get_time_zone_offset(time_zone, time); - if (!offset_seconds.has_value()) + auto offset = TimeZone::get_time_zone_offset(time_zone, time); + if (!offset.has_value()) return {}; - if (*offset_seconds == 0) + 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 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_hours = offset->seconds / 3'600; + offset->seconds %= 3'600; - auto offset_minutes = *offset_seconds / 60; - *offset_seconds %= 60; + auto offset_minutes = offset->seconds / 60; + offset->seconds %= 60; StringBuilder builder; builder.append(sign); @@ -231,8 +231,8 @@ static Optional<String> format_time_zone_offset(StringView locale, StringView ti // 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); + 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. @@ -240,8 +240,8 @@ static Optional<String> format_time_zone_offset(StringView locale, StringView ti builder.appendff("{}", offset_hours); if (offset_minutes > 0) { builder.appendff("{}{:02}", separator, offset_minutes); - if (*offset_seconds > 0) - builder.appendff("{}{:02}", separator, *offset_seconds); + if (offset->seconds > 0) + builder.appendff("{}{:02}", separator, offset->seconds); } break; @@ -250,8 +250,8 @@ static Optional<String> format_time_zone_offset(StringView locale, StringView ti } // 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); + auto result = replace_digits_for_number_system(*number_system, builder.build()); + return formats->gmt_format.replace("{0}"sv, result); } // https://unicode.org/reports/tr35/tr35-dates.html#Time_Zone_Format_Terminology |