diff options
author | Mark de Wever <koraq@xs4all.nl> | 2025-02-18 19:28:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-18 19:28:16 +0100 |
commit | f796747a17941681c8f806cab4e10570b97b5089 (patch) | |
tree | f21149054c6a67e90bd77ed78bc1b716dabbb912 /libcxx/src | |
parent | 1f6165e1844a69a726b92cf8821a645b0fff5303 (diff) | |
download | llvm-f796747a17941681c8f806cab4e10570b97b5089.zip llvm-f796747a17941681c8f806cab4e10570b97b5089.tar.gz llvm-f796747a17941681c8f806cab4e10570b97b5089.tar.bz2 |
[libc++][TZDB] Improves system time zone detection. (#127339)
On some (Linux) systems /etc/localtime is not a symlink to the time
zone, but contains a copy of the binary time zone file. In these case
there usually is a file named /etc/timezone which contains the text for
the current time zone name.
Instead of throwing when /etc/localtime does not exist or is not a
symlink use this fallback.
Fixes: #105634
---------
Co-authored-by: Louis Dionne <ldionne.2@gmail.com>
Diffstat (limited to 'libcxx/src')
-rw-r--r-- | libcxx/src/experimental/tzdb.cpp | 67 |
1 files changed, 49 insertions, 18 deletions
diff --git a/libcxx/src/experimental/tzdb.cpp b/libcxx/src/experimental/tzdb.cpp index f38f495..1f18226 100644 --- a/libcxx/src/experimental/tzdb.cpp +++ b/libcxx/src/experimental/tzdb.cpp @@ -708,6 +708,39 @@ void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) { std::__throw_runtime_error("unknown time zone"); } #else // ifdef _WIN32 + +[[nodiscard]] static string __current_zone_environment() { + if (const char* __tz = std::getenv("TZ")) + return __tz; + + return {}; +} + +[[nodiscard]] static string __current_zone_etc_localtime() { + filesystem::path __path = "/etc/localtime"; + if (!filesystem::exists(__path) || !filesystem::is_symlink(__path)) + return {}; + + filesystem::path __tz = filesystem::read_symlink(__path); + // The path may be a relative path, in that case convert it to an absolute + // path based on the proper initial directory. + if (__tz.is_relative()) + __tz = filesystem::canonical("/etc" / __tz); + + return filesystem::relative(__tz, "/usr/share/zoneinfo/"); +} + +[[nodiscard]] static string __current_zone_etc_timezone() { + filesystem::path __path = "/etc/timezone"; + if (!filesystem::exists(__path)) + return {}; + + ifstream __f(__path); + string __name; + std::getline(__f, __name); + return __name; +} + [[nodiscard]] static const time_zone* __current_zone_posix(const tzdb& tzdb) { // On POSIX systems there are several ways to configure the time zone. // In order of priority they are: @@ -726,30 +759,28 @@ void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) { // // - The time zone name is the target of the symlink /etc/localtime // relative to /usr/share/zoneinfo/ + // + // - The file /etc/timezone. This text file contains the name of the time + // zone. + // + // On Linux systems it seems /etc/timezone is deprecated and being phased + // out. This file is used when /etc/localtime does not exist, or when it exists but is not a symlink. For more information and links see + // https://github.com/llvm/llvm-project/issues/105634 - // The algorithm is like this: - // - If the environment variable TZ is set and points to a valid - // record use this value. - // - Else use the name based on the `/etc/localtime` symlink. + string __name = chrono::__current_zone_environment(); - if (const char* __tz = getenv("TZ")) - if (const time_zone* __result = tzdb.__locate_zone(__tz)) + // Ignore invalid names in the environment. + if (!__name.empty()) + if (const time_zone* __result = tzdb.__locate_zone(__name)) return __result; - filesystem::path __path = "/etc/localtime"; - if (!filesystem::exists(__path)) - std::__throw_runtime_error("tzdb: the symlink '/etc/localtime' does not exist"); - - if (!filesystem::is_symlink(__path)) - std::__throw_runtime_error("tzdb: the path '/etc/localtime' is not a symlink"); + __name = chrono::__current_zone_etc_localtime(); + if (__name.empty()) + __name = chrono::__current_zone_etc_timezone(); - filesystem::path __tz = filesystem::read_symlink(__path); - // The path may be a relative path, in that case convert it to an absolute - // path based on the proper initial directory. - if (__tz.is_relative()) - __tz = filesystem::canonical("/etc" / __tz); + if (__name.empty()) + std::__throw_runtime_error("tzdb: unable to determine the name of the current time zone"); - string __name = filesystem::relative(__tz, "/usr/share/zoneinfo/"); if (const time_zone* __result = tzdb.__locate_zone(__name)) return __result; |