From 8ded0c65dcdbdd17271dc6bef9d2aab191675fd4 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sun, 1 Jun 2025 08:22:19 -0400 Subject: [PATCH] LibJS+LibUnicode: Change time zones in a way that works on Windows On Windows, ICU does not look at the TZ environment variable at all. So to support changing time zones in test-js, let's set ICU's default time zone directly. Note that we no longer deal with "null" time zone strings. We just cache whatever ICU thinks is the current time zone before attempting to change it, for which we never have a null result. Co-authored-by: Andrew Kaster --- Libraries/LibUnicode/TimeZone.cpp | 18 +++++++++++++++--- Libraries/LibUnicode/TimeZone.h | 4 +++- Tests/LibJS/test-js.cpp | 21 ++++----------------- Tests/LibUnicode/TestTimeZone.cpp | 24 ++++++++++++------------ 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/Libraries/LibUnicode/TimeZone.cpp b/Libraries/LibUnicode/TimeZone.cpp index d1bc1fff42a..acd43715c8d 100644 --- a/Libraries/LibUnicode/TimeZone.cpp +++ b/Libraries/LibUnicode/TimeZone.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Tim Flynn + * Copyright (c) 2024-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -24,8 +24,8 @@ String current_time_zone() UErrorCode status = U_ZERO_ERROR; - auto time_zone = adopt_own_if_nonnull(icu::TimeZone::detectHostTimeZone()); - if (!time_zone) + auto time_zone = adopt_own_if_nonnull(icu::TimeZone::createDefault()); + if (!time_zone || *time_zone == icu::TimeZone::getUnknown()) return "UTC"_string; icu::UnicodeString time_zone_id; @@ -46,6 +46,18 @@ void clear_system_time_zone_cache() cached_system_time_zone.clear(); } +ErrorOr set_current_time_zone(StringView time_zone) +{ + auto time_zone_data = TimeZoneData::for_time_zone(time_zone); + if (!time_zone_data.has_value()) + return Error::from_string_literal("Unable to find the provided time zone"); + + icu::TimeZone::setDefault(time_zone_data->time_zone()); + clear_system_time_zone_cache(); + + return {}; +} + // https://github.com/unicode-org/icu/blob/main/icu4c/source/tools/tzcode/icuzones static constexpr bool is_legacy_non_iana_time_zone(StringView time_zone) { diff --git a/Libraries/LibUnicode/TimeZone.h b/Libraries/LibUnicode/TimeZone.h index 518589d1fd7..66f1601ff15 100644 --- a/Libraries/LibUnicode/TimeZone.h +++ b/Libraries/LibUnicode/TimeZone.h @@ -1,11 +1,12 @@ /* - * Copyright (c) 2024, Tim Flynn + * Copyright (c) 2024-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include #include #include @@ -24,6 +25,7 @@ struct TimeZoneOffset { }; String current_time_zone(); +ErrorOr set_current_time_zone(StringView); void clear_system_time_zone_cache(); Vector const& available_time_zones(); Vector available_time_zones_in_region(StringView region); diff --git a/Tests/LibJS/test-js.cpp b/Tests/LibJS/test-js.cpp index 1ada18db2f8..eeeb2b88393 100644 --- a/Tests/LibJS/test-js.cpp +++ b/Tests/LibJS/test-js.cpp @@ -6,14 +6,11 @@ */ #include -#include #include #include #include #include #include -#include -#include TEST_ROOT("Libraries/LibJS/Tests"); @@ -111,23 +108,13 @@ TESTJS_GLOBAL_FUNCTION(detach_array_buffer, detachArrayBuffer) TESTJS_GLOBAL_FUNCTION(set_time_zone, setTimeZone) { - auto current_time_zone = JS::js_null(); + auto current_time_zone = JS::PrimitiveString::create(vm, Unicode::current_time_zone()); + auto time_zone = TRY(vm.argument(0).to_string(vm)); - if (auto time_zone = Core::Environment::get("TZ"sv); time_zone.has_value()) - current_time_zone = JS::PrimitiveString::create(vm, *time_zone); - - if (auto time_zone = vm.argument(0); time_zone.is_null()) { - if (auto result = Core::Environment::unset("TZ"sv); result.is_error()) - return vm.throw_completion(MUST(String::formatted("Could not unset time zone: {}", result.error()))); - } else { - if (auto result = Core::Environment::set("TZ"sv, TRY(time_zone.to_string(vm)), Core::Environment::Overwrite::Yes); result.is_error()) - return vm.throw_completion(MUST(String::formatted("Could not set time zone: {}", result.error()))); - } + if (auto result = Unicode::set_current_time_zone(time_zone); result.is_error()) + return vm.throw_completion(MUST(String::formatted("Could not set time zone: {}", result.error()))); JS::clear_system_time_zone_cache(); - Unicode::clear_system_time_zone_cache(); - tzset(); - return current_time_zone; } diff --git a/Tests/LibUnicode/TestTimeZone.cpp b/Tests/LibUnicode/TestTimeZone.cpp index 208a0b90137..c79a200124f 100644 --- a/Tests/LibUnicode/TestTimeZone.cpp +++ b/Tests/LibUnicode/TestTimeZone.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Tim Flynn + * Copyright (c) 2024-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -7,29 +7,25 @@ #include #include -#include #include class TimeZoneGuard { public: explicit TimeZoneGuard(StringView time_zone) - : m_time_zone(Core::Environment::get("TZ"sv)) + : m_time_zone(Unicode::current_time_zone()) { - MUST(Core::Environment::set("TZ"sv, time_zone, Core::Environment::Overwrite::Yes)); - Unicode::clear_system_time_zone_cache(); + (void)Unicode::set_current_time_zone(time_zone); } ~TimeZoneGuard() { - if (m_time_zone.has_value()) - MUST(Core::Environment::set("TZ"sv, *m_time_zone, Core::Environment::Overwrite::Yes)); - else - MUST(Core::Environment::unset("TZ"sv)); - Unicode::clear_system_time_zone_cache(); + MUST(Unicode::set_current_time_zone(m_time_zone)); } + String const& time_zone() const { return m_time_zone; } + private: - Optional m_time_zone; + String m_time_zone; }; TEST_CASE(current_time_zone) @@ -38,9 +34,13 @@ TEST_CASE(current_time_zone) TimeZoneGuard guard { "America/New_York"sv }; EXPECT_EQ(Unicode::current_time_zone(), "America/New_York"sv); } + { + TimeZoneGuard guard { "America/Los_Angeles"sv }; + EXPECT_EQ(Unicode::current_time_zone(), "America/Los_Angeles"sv); + } { TimeZoneGuard guard { "ladybird"sv }; - EXPECT_EQ(Unicode::current_time_zone(), "UTC"sv); + EXPECT_EQ(Unicode::current_time_zone(), guard.time_zone()); } }