Skip to content

Commit f796747

Browse files
mordanteldionne
andauthored
[libc++][TZDB] Improves system time zone detection. (llvm#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: llvm#105634 --------- Co-authored-by: Louis Dionne <ldionne.2@gmail.com>
1 parent 1f6165e commit f796747

File tree

1 file changed

+49
-18
lines changed

1 file changed

+49
-18
lines changed

libcxx/src/experimental/tzdb.cpp

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,39 @@ void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) {
708708
std::__throw_runtime_error("unknown time zone");
709709
}
710710
#else // ifdef _WIN32
711+
712+
[[nodiscard]] static string __current_zone_environment() {
713+
if (const char* __tz = std::getenv("TZ"))
714+
return __tz;
715+
716+
return {};
717+
}
718+
719+
[[nodiscard]] static string __current_zone_etc_localtime() {
720+
filesystem::path __path = "/etc/localtime";
721+
if (!filesystem::exists(__path) || !filesystem::is_symlink(__path))
722+
return {};
723+
724+
filesystem::path __tz = filesystem::read_symlink(__path);
725+
// The path may be a relative path, in that case convert it to an absolute
726+
// path based on the proper initial directory.
727+
if (__tz.is_relative())
728+
__tz = filesystem::canonical("/etc" / __tz);
729+
730+
return filesystem::relative(__tz, "/usr/share/zoneinfo/");
731+
}
732+
733+
[[nodiscard]] static string __current_zone_etc_timezone() {
734+
filesystem::path __path = "/etc/timezone";
735+
if (!filesystem::exists(__path))
736+
return {};
737+
738+
ifstream __f(__path);
739+
string __name;
740+
std::getline(__f, __name);
741+
return __name;
742+
}
743+
711744
[[nodiscard]] static const time_zone* __current_zone_posix(const tzdb& tzdb) {
712745
// On POSIX systems there are several ways to configure the time zone.
713746
// In order of priority they are:
@@ -726,30 +759,28 @@ void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) {
726759
//
727760
// - The time zone name is the target of the symlink /etc/localtime
728761
// relative to /usr/share/zoneinfo/
762+
//
763+
// - The file /etc/timezone. This text file contains the name of the time
764+
// zone.
765+
//
766+
// On Linux systems it seems /etc/timezone is deprecated and being phased
767+
// 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
768+
// https://github.com/llvm/llvm-project/issues/105634
729769

730-
// The algorithm is like this:
731-
// - If the environment variable TZ is set and points to a valid
732-
// record use this value.
733-
// - Else use the name based on the `/etc/localtime` symlink.
770+
string __name = chrono::__current_zone_environment();
734771

735-
if (const char* __tz = getenv("TZ"))
736-
if (const time_zone* __result = tzdb.__locate_zone(__tz))
772+
// Ignore invalid names in the environment.
773+
if (!__name.empty())
774+
if (const time_zone* __result = tzdb.__locate_zone(__name))
737775
return __result;
738776

739-
filesystem::path __path = "/etc/localtime";
740-
if (!filesystem::exists(__path))
741-
std::__throw_runtime_error("tzdb: the symlink '/etc/localtime' does not exist");
742-
743-
if (!filesystem::is_symlink(__path))
744-
std::__throw_runtime_error("tzdb: the path '/etc/localtime' is not a symlink");
777+
__name = chrono::__current_zone_etc_localtime();
778+
if (__name.empty())
779+
__name = chrono::__current_zone_etc_timezone();
745780

746-
filesystem::path __tz = filesystem::read_symlink(__path);
747-
// The path may be a relative path, in that case convert it to an absolute
748-
// path based on the proper initial directory.
749-
if (__tz.is_relative())
750-
__tz = filesystem::canonical("/etc" / __tz);
781+
if (__name.empty())
782+
std::__throw_runtime_error("tzdb: unable to determine the name of the current time zone");
751783

752-
string __name = filesystem::relative(__tz, "/usr/share/zoneinfo/");
753784
if (const time_zone* __result = tzdb.__locate_zone(__name))
754785
return __result;
755786

0 commit comments

Comments
 (0)