LibCore: Keep ICU and libc time zones in sync

Add Core::TimeZone as the single entry point for changing the
current time zone. This updates both ICU and the process TZ/tzset
state so libc-backed helpers such as AK::UnixDateTime::to_string()
agree with JS/ICU time zone state. Previously only the ICU timezone
would be updated, which could result in inconsistent results being
returned.
This commit is contained in:
Shannon Booth 2026-05-16 13:01:03 +02:00 committed by Tim Flynn
parent 8a85146e35
commit 74b76e21b9
9 changed files with 78 additions and 7 deletions

View file

@ -25,6 +25,7 @@ set(SOURCES
SystemServerTakeover.cpp
ThreadEventQueue.cpp
Timer.cpp
TimeZone.cpp
Version.cpp
)

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2026, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/Environment.h>
#include <LibCore/TimeZone.h>
#include <LibUnicode/TimeZone.h>
#include <time.h>
namespace Core::TimeZone {
ErrorOr<void> set_current_time_zone(StringView time_zone)
{
TRY(Unicode::set_current_time_zone(time_zone));
TRY(Core::Environment::set("TZ"sv, time_zone, Core::Environment::Overwrite::Yes));
tzset();
return {};
}
String current_time_zone()
{
return Unicode::current_time_zone();
}
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2026, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Error.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibCore/Export.h>
namespace Core::TimeZone {
CORE_API ErrorOr<void> set_current_time_zone(StringView);
CORE_API String current_time_zone();
}

View file

@ -6,6 +6,7 @@
*/
#include <AK/JsonObject.h>
#include <LibCore/TimeZone.h>
#include <LibGfx/Cursor.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
@ -13,7 +14,6 @@
#include <LibJS/Runtime/Reference.h>
#include <LibJS/Runtime/VM.h>
#include <LibURL/Parser.h>
#include <LibUnicode/TimeZone.h>
#include <LibWeb/ARIA/AriaData.h>
#include <LibWeb/ARIA/StateAndProperties.h>
#include <LibWeb/Bindings/Internals.h>
@ -207,9 +207,9 @@ WebIDL::ExceptionOr<void> Internals::mark_as_garbage(StringView variable_name)
WebIDL::ExceptionOr<String> Internals::set_time_zone(StringView time_zone)
{
auto current_time_zone = Unicode::current_time_zone();
auto current_time_zone = Core::TimeZone::current_time_zone();
if (auto result = Unicode::set_current_time_zone(time_zone); result.is_error())
if (auto result = Core::TimeZone::set_current_time_zone(time_zone); result.is_error())
return vm().throw_completion<JS::InternalError>(MUST(String::formatted("Could not set time zone: {}", result.error())));
JS::clear_system_time_zone_cache();

View file

@ -11,6 +11,7 @@
#include <LibCore/Process.h>
#include <LibCore/Resource.h>
#include <LibCore/System.h>
#include <LibCore/TimeZone.h>
#include <LibCrypto/OpenSSLForward.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/PathFontProvider.h>
@ -183,7 +184,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
}
if (!default_time_zone.is_empty()) {
if (auto result = Unicode::set_current_time_zone(default_time_zone); result.is_error())
if (auto result = Core::TimeZone::set_current_time_zone(default_time_zone); result.is_error())
dbgln("Failed to set default time zone: {}", result.error());
}

View file

@ -7,13 +7,13 @@
#include <AK/Enumerate.h>
#include <AK/StringView.h>
#include <LibCore/TimeZone.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/FinalizationRegistry.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibTest/JavaScriptTestRunner.h>
#include <LibUnicode/TimeZone.h>
TEST_ROOT("Tests/LibJS/Runtime");
@ -142,10 +142,10 @@ TESTJS_GLOBAL_FUNCTION(detach_array_buffer, detachArrayBuffer)
TESTJS_GLOBAL_FUNCTION(set_time_zone, setTimeZone)
{
auto current_time_zone = JS::PrimitiveString::create(vm, Unicode::current_time_zone());
auto current_time_zone = JS::PrimitiveString::create(vm, Core::TimeZone::current_time_zone());
auto time_zone = TRY(vm.argument(0).to_string(vm));
if (auto result = Unicode::set_current_time_zone(time_zone); result.is_error())
if (auto result = Core::TimeZone::set_current_time_zone(time_zone); result.is_error())
return vm.throw_completion<JS::InternalError>(MUST(String::formatted("Could not set time zone: {}", result.error())));
JS::clear_system_time_zone_cache();

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass lastModified should return the last modified date and time

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>document.lastModified</title>
<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
<link rel=help href="https://html.spec.whatwg.org/multipage/#resource-metadata-management">
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
test(function(){
var date = new Date("Thu, 01 Jan 1970 01:23:45 GMT");
var result = ('0' + (date.getMonth()+1)).slice(-2) + '/' + ('0' + date.getDate()).slice(-2) + '/' + date.getFullYear() + " " + [date.getHours(),date.getMinutes(),date.getSeconds()].map(function(n){return ("0" + n).slice(-2);}).join(":");
assert_equals(document.lastModified, result);
}, "lastModified should return the last modified date and time");
</script>

View file

@ -0,0 +1 @@
Last-Modified: Thu, 01 Jan 1970 01:23:45 GMT