1 //===----------------------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
17 // Contains a parser for the IANA time zone data files.
19 // These files can be found at https://data.iana.org/time-zones/ and are in the
20 // public domain. Information regarding the input can be found at
21 // https://data.iana.org/time-zones/tz-how-to.html and
22 // https://man7.org/linux/man-pages/man8/zic.8.html.
24 // As indicated at https://howardhinnant.github.io/date/tz.html#Installation
25 // For Windows another file seems to be required
26 // https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml
27 // This file seems to contain the mapping of Windows time zone name to IANA
30 // However this article mentions another way to do the mapping on Windows
31 // https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255
32 // This requires Windows 10 Version 1903, which was released in May of 2019
33 // and considered end of life in December 2020
34 // https://learn.microsoft.com/en-us/lifecycle/announcements/windows-10-1903-end-of-servicing
36 // TODO TZDB Implement the Windows mapping in tzdb::current_zone
38 _LIBCPP_BEGIN_NAMESPACE_STD
42 // This function is weak so it can be overriden in the tests. The
43 // declaration is in the test header test/support/test_tzdb.h
44 _LIBCPP_WEAK string_view
__libcpp_tzdb_directory() {
45 #if defined(__linux__)
46 return "/usr/share/zoneinfo/";
48 # error "unknown path to the IANA Time Zone Database"
52 [[nodiscard
]] static bool __is_whitespace(int __c
) { return __c
== ' ' || __c
== '\t'; }
54 static void __skip_optional_whitespace(istream
& __input
) {
55 while (chrono::__is_whitespace(__input
.peek()))
59 static void __skip_mandatory_whitespace(istream
& __input
) {
60 if (!chrono::__is_whitespace(__input
.get()))
61 std::__throw_runtime_error("corrupt tzdb: expected whitespace");
63 chrono::__skip_optional_whitespace(__input
);
66 static void __matches(istream
& __input
, char __expected
) {
67 if (std::tolower(__input
.get()) != __expected
)
68 std::__throw_runtime_error((string("corrupt tzdb: expected character '") + __expected
+ '\'').c_str());
71 static void __matches(istream
& __input
, string_view __expected
) {
72 for (auto __c
: __expected
)
73 if (std::tolower(__input
.get()) != __c
)
74 std::__throw_runtime_error((string("corrupt tzdb: expected string '") + string(__expected
) + '\'').c_str());
77 [[nodiscard
]] static string
__parse_string(istream
& __input
) {
80 int __c
= __input
.get();
87 case istream::traits_type::eof():
89 std::__throw_runtime_error("corrupt tzdb: expected a string");
94 __result
.push_back(__c
);
99 static string
__parse_version(istream
& __input
) {
100 // The first line in tzdata.zi contains
102 // The parser expects this pattern
103 // #\s*version\s*\(.*)
104 // This part is not documented.
105 chrono::__matches(__input
, '#');
106 chrono::__skip_optional_whitespace(__input
);
107 chrono::__matches(__input
, "version");
108 chrono::__skip_mandatory_whitespace(__input
);
109 return chrono::__parse_string(__input
);
112 static tzdb
__make_tzdb() {
115 filesystem::path __root
= chrono::__libcpp_tzdb_directory();
116 ifstream __tzdata
{__root
/ "tzdata.zi"};
118 __result
.version
= chrono::__parse_version(__tzdata
);
122 //===----------------------------------------------------------------------===//
124 //===----------------------------------------------------------------------===//
126 _LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list
& get_tzdb_list() {
127 static tzdb_list __result
{chrono::__make_tzdb()};
131 _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI
const tzdb
& reload_tzdb() {
132 if (chrono::remote_version() == chrono::get_tzdb().version
)
133 return chrono::get_tzdb();
135 return chrono::get_tzdb_list().__emplace_front(chrono::__make_tzdb());
138 _LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string
remote_version() {
139 filesystem::path __root
= chrono::__libcpp_tzdb_directory();
140 ifstream __tzdata
{__root
/ "tzdata.zi"};
141 return chrono::__parse_version(__tzdata
);
144 } // namespace chrono
146 _LIBCPP_END_NAMESPACE_STD