[libcxx-commits] [libcxx] [libc++][chrono] Loads tzdata.zi in tzdb. (PR #74928)
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Feb 4 07:09:03 PST 2024
https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/74928
>From edb9daf76efb7191e99bc33cf633f086b783da45 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Fri, 23 Sep 2022 18:33:20 +0200
Subject: [PATCH 1/2] [libc++][chrono] Loads tzdata.zi in tzdb.
This implements the loading of the tzdata.zi file and store its contents
in the tzdb struct.
This adds all required members except:
- the leap seconds,
- the locate_zone, and
- current_zone.
The class time_zone is incomplete and only contains the parts needed
for storing the parsed data.
The class time_zone_link is fully implemented including its non-member
functions.
Implements parts of:
- P0355 Extending <chrono> to Calendars and Time Zones
- P1614 The Mothership has Landed
Implements:
- P1982 Rename link to time_zone_link
---
libcxx/docs/Status/Cxx20Papers.csv | 2 +-
libcxx/docs/Status/SpaceshipProjects.csv | 4 +-
libcxx/include/CMakeLists.txt | 3 +
libcxx/include/__chrono/time_zone.h | 75 +++
libcxx/include/__chrono/time_zone_link.h | 72 +++
libcxx/include/__chrono/time_zone_types.h | 112 ++++
libcxx/include/__chrono/tzdb.h | 9 +
libcxx/include/chrono | 40 +-
libcxx/include/module.modulemap.in | 12 +
libcxx/modules/std/chrono.inc | 4 +
libcxx/src/tz.cpp | 483 +++++++++++++++
...rono.nodiscard_extensions.compile.pass.cpp | 16 +
.../chrono.nodiscard_extensions.verify.cpp | 19 +
.../time.zone/time.zone.db/links.pass.cpp | 102 ++++
.../time.zone/time.zone.db/rules.pass.cpp | 565 ++++++++++++++++++
.../time.zone/time.zone.db/zones.pass.cpp | 371 ++++++++++++
.../test/libcxx/transitive_includes/cxx03.csv | 2 +
.../test/libcxx/transitive_includes/cxx11.csv | 2 +
.../test/libcxx/transitive_includes/cxx14.csv | 2 +
.../test/libcxx/transitive_includes/cxx17.csv | 2 +
.../test/libcxx/transitive_includes/cxx20.csv | 2 +
.../test/libcxx/transitive_includes/cxx23.csv | 2 +
.../test/libcxx/transitive_includes/cxx26.csv | 2 +
.../time.zone.db.access/get_tzdb.pass.cpp | 14 +-
.../time.zone.db.tzdb/tzdb.members.pass.cpp | 9 +-
.../time.zone.link.members/name.pass.cpp | 35 ++
.../time.zone.link.members/target.pass.cpp | 34 ++
.../comparison.pass.cpp | 36 ++
.../time.zone.link/types.compile.pass.cpp | 30 +
.../time.zone.members/name.pass.cpp | 35 ++
.../time.zone.nonmembers/comparison.pass.cpp | 36 ++
.../time.zone.timezone/types.compile.pass.cpp | 30 +
32 files changed, 2148 insertions(+), 14 deletions(-)
create mode 100644 libcxx/include/__chrono/time_zone.h
create mode 100644 libcxx/include/__chrono/time_zone_link.h
create mode 100644 libcxx/include/__chrono/time_zone_types.h
create mode 100644 libcxx/test/libcxx/time/time.zone/time.zone.db/links.pass.cpp
create mode 100644 libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp
create mode 100644 libcxx/test/libcxx/time/time.zone/time.zone.db/zones.pass.cpp
create mode 100644 libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/name.pass.cpp
create mode 100644 libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/target.pass.cpp
create mode 100644 libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.nonmembers/comparison.pass.cpp
create mode 100644 libcxx/test/std/time/time.zone/time.zone.link/types.compile.pass.cpp
create mode 100644 libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/name.pass.cpp
create mode 100644 libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.nonmembers/comparison.pass.cpp
create mode 100644 libcxx/test/std/time/time.zone/time.zone.timezone/types.compile.pass.cpp
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index d73088687975c..314b73d6704e1 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -180,7 +180,7 @@
"`P1973R1 <https://wg21.link/P1973R1>`__","LWG","Rename ""_default_init"" Functions, Rev1","Prague","|Complete|","16.0"
"`P1976R2 <https://wg21.link/P1976R2>`__","LWG","Fixed-size span construction from dynamic range","Prague","|Complete|","11.0","|ranges|"
"`P1981R0 <https://wg21.link/P1981R0>`__","LWG","Rename leap to leap_second","Prague","* *",""
-"`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","* *",""
+"`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","|Complete|","18.0","|chrono|"
"`P1983R0 <https://wg21.link/P1983R0>`__","LWG","Wording for GB301, US296, US292, US291, and US283","Prague","|Complete|","15.0","|ranges|"
"`P1994R1 <https://wg21.link/P1994R1>`__","LWG","elements_view needs its own sentinel","Prague","|Complete|","16.0","|ranges|"
"`P2002R1 <https://wg21.link/P2002R1>`__","CWG","Defaulted comparison specification cleanups","Prague","* *",""
diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv
index aaa9278a50c1e..c8221078e9a8d 100644
--- a/libcxx/docs/Status/SpaceshipProjects.csv
+++ b/libcxx/docs/Status/SpaceshipProjects.csv
@@ -171,10 +171,10 @@ Section,Description,Dependencies,Assignee,Complete
| `month_weekday_last <https://reviews.llvm.org/D152699>`_
| `year_month_weekday <https://reviews.llvm.org/D152699>`_
| `year_month_weekday_last <https://reviews.llvm.org/D152699>`_",None,Hristo Hristov,|Complete|
-`[time.zone.nonmembers] <https://wg21.link/time.zone.nonmembers>`_,"`chrono::time_zone`",A ``<chrono>`` implementation,Mark de Wever,|In Progress|
+`[time.zone.nonmembers] <https://wg21.link/time.zone.nonmembers>`_,"`chrono::time_zone`",A ``<chrono>`` implementation,Mark de Wever,|Complete|
`[time.zone.zonedtime.nonmembers] <https://wg21.link/time.zone.zonedtime.nonmembers>`_,"`chrono::zoned_time`",A ``<chrono>`` implementation,Mark de Wever,|In Progress|
`[time.zone.leap.nonmembers] <https://wg21.link/time.zone.leap.nonmembers>`_,"`chrono::time_leap_seconds`",A ``<chrono>`` implementation,Mark de Wever,|In Progress|
-`[time.zone.link.nonmembers] <https://wg21.link/time.zone.link.nonmembers>`_,"`chrono::time_zone_link`",A ``<chrono>`` implementation,Mark de Wever,|In Progress|
+`[time.zone.link.nonmembers] <https://wg21.link/time.zone.link.nonmembers>`_,"`chrono::time_zone_link`",A ``<chrono>`` implementation,Mark de Wever,|Complete|
- `5.13 Clause 28: Localization library <https://wg21.link/p1614r2#clause-28-localization-library>`_,,,,
"| `[locale] <https://wg21.link/locale>`_
| `[locale.operators] <https://wg21.link/locale.operators>`_",| remove ops `locale <https://reviews.llvm.org/D152654>`_,None,Hristo Hristov,|Complete|
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 6ded426640f0d..73863699715a9 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -290,6 +290,9 @@ set(files
__chrono/steady_clock.h
__chrono/system_clock.h
__chrono/time_point.h
+ __chrono/time_zone.h
+ __chrono/time_zone_link.h
+ __chrono/time_zone_types.h
__chrono/tzdb.h
__chrono/tzdb_list.h
__chrono/weekday.h
diff --git a/libcxx/include/__chrono/time_zone.h b/libcxx/include/__chrono/time_zone.h
new file mode 100644
index 0000000000000..b54c58485aef3
--- /dev/null
+++ b/libcxx/include/__chrono/time_zone.h
@@ -0,0 +1,75 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+
+#ifndef _LIBCPP___CHRONO_TIME_ZONE_H
+#define _LIBCPP___CHRONO_TIME_ZONE_H
+
+#include <version>
+// Enable the contents of the header only when libc++ was built with experimental features enabled.
+#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+# include <__chrono/time_zone_types.h>
+# include <__compare/strong_order.h>
+# include <__config>
+# include <string>
+# include <string_view>
+# include <vector>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
+ !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+namespace chrono {
+
+class _LIBCPP_AVAILABILITY_TZDB time_zone {
+public:
+ explicit _LIBCPP_HIDE_FROM_ABI time_zone(string&& __name) : __name_(std::move(__name)) {}
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name_; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI vector<__tz::__continuation>& __continuations() { return __continuations_; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const vector<__tz::__continuation>& __continuations() const {
+ return __continuations_;
+ }
+
+private:
+ string __name_;
+ // Note the first line has a name + __continuation, the other lines
+ // are just __continuations. So there is always at least one item in
+ // the vector.
+ vector<__tz::__continuation> __continuations_;
+};
+
+_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline bool
+operator==(const time_zone& __x, const time_zone& __y) noexcept {
+ return __x.name() == __y.name();
+}
+
+_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline strong_ordering
+operator<=>(const time_zone& __x, const time_zone& __y) noexcept {
+ return __x.name() <=> __y.name();
+}
+
+} // namespace chrono
+
+# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM)
+ // && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+#endif // _LIBCPP___CHRONO_TIME_ZONE_H
diff --git a/libcxx/include/__chrono/time_zone_link.h b/libcxx/include/__chrono/time_zone_link.h
new file mode 100644
index 0000000000000..a188298a8af21
--- /dev/null
+++ b/libcxx/include/__chrono/time_zone_link.h
@@ -0,0 +1,72 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+
+#ifndef _LIBCPP___CHRONO_TIME_ZONE_LINK_H
+#define _LIBCPP___CHRONO_TIME_ZONE_LINK_H
+
+#include <version>
+// Enable the contents of the header only when libc++ was built with experimental features enabled.
+#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+# include <__compare/strong_order.h>
+# include <__config>
+# include <string>
+# include <string_view>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
+ !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+namespace chrono {
+
+class _LIBCPP_AVAILABILITY_TZDB time_zone_link {
+public:
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI explicit time_zone_link(string_view __name, string_view __target)
+ : __name_{__name}, __target_{__target} {}
+
+ _LIBCPP_HIDE_FROM_ABI time_zone_link(time_zone_link&&) = default;
+ _LIBCPP_HIDE_FROM_ABI time_zone_link& operator=(time_zone_link&&) = default;
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name_; }
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI string_view target() const noexcept { return __target_; }
+
+private:
+ string __name_;
+ // TODO TZDB instead of the name we can store the pointer to a zone. These
+ // pointers are immutable. This makes it possible to directly return a
+ // pointer in the time_zone in the 'locate_zone' function.
+ string __target_;
+};
+
+_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline bool
+operator==(const time_zone_link& __x, const time_zone_link& __y) noexcept {
+ return __x.name() == __y.name();
+}
+
+_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline strong_ordering
+operator<=>(const time_zone_link& __x, const time_zone_link& __y) noexcept {
+ return __x.name() <=> __y.name();
+}
+
+} // namespace chrono
+
+# endif //_LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+#endif // _LIBCPP___CHRONO_TIME_ZONE_LINK_H
diff --git a/libcxx/include/__chrono/time_zone_types.h b/libcxx/include/__chrono/time_zone_types.h
new file mode 100644
index 0000000000000..e8d7b7ddf8465
--- /dev/null
+++ b/libcxx/include/__chrono/time_zone_types.h
@@ -0,0 +1,112 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+
+#ifndef _LIBCPP___CHRONO_TIME_ZONE_TYPES_H
+#define _LIBCPP___CHRONO_TIME_ZONE_TYPES_H
+
+#include <version>
+// Enable the contents of the header only when libc++ was built with experimental features enabled.
+#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+# include <__chrono/day.h>
+# include <__chrono/duration.h>
+# include <__chrono/month.h>
+# include <__chrono/weekday.h>
+# include <__chrono/year.h>
+# include <__config>
+# include <string>
+# include <variant>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
+ !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+namespace chrono::__tz {
+
+// Sun>=8 first Sunday on or after the eighth
+// Sun<=25 last Sunday on or before the 25th
+struct _LIBCPP_AVAILABILITY_TZDB __constrained_weekday {
+ /* year_month_day operator()(year __year, month __month);*/ // needed but not implemented
+
+ weekday __weekday;
+ enum __comparison_t { __le, __ge } __comparison;
+ day __day;
+};
+
+// The on field has a few alternative presentations
+// 5 the fifth of the month
+// lastSun the last Sunday in the month
+// lastMon the last Monday in the month
+// Sun>=8 first Sunday on or after the eighth
+// Sun<=25 last Sunday on or before the 25th
+using __on = variant<day, weekday_last, __constrained_weekday>;
+
+enum class _LIBCPP_AVAILABILITY_TZDB __clock { __local, __standard, __universal };
+
+struct _LIBCPP_AVAILABILITY_TZDB __at {
+ seconds __time{0};
+ __tz::__clock __clock{__tz::__clock::__local};
+};
+
+struct _LIBCPP_AVAILABILITY_TZDB __save {
+ seconds __time;
+ bool __is_dst;
+};
+
+// The names of the fields match the fields of a Rule.
+struct _LIBCPP_AVAILABILITY_TZDB __rule {
+ year __from;
+ year __to;
+ month __in_month; // __in is a reserved name
+ __tz::__on __on;
+ __tz::__at __at;
+ __tz::__save __save;
+ string __letters;
+};
+
+struct _LIBCPP_AVAILABILITY_TZDB __continuation {
+ seconds __stdoff;
+
+ // The RULES is either a SAVE or a NAME.
+ // The size_t is used as cache. After loading the rules they are
+ // sorted and remain stable, then an index in the vector can be
+ // used.
+ // If this field contains - then standard time always
+ // applies. This is indicated by the monostate.
+ using __rules_t = variant<monostate, __tz::__save, string, size_t>;
+
+ __rules_t __rules;
+
+ string __format;
+ // TODO TZDB until can be contain more than just a year.
+ // Parts of the UNTIL, the optional parts are default initialized
+ // optional<year> __until_;
+ year __year = chrono::year::min();
+ month __in_month{January}; // __in is a reserved name
+ __tz::__on __on{chrono::day{1}};
+ __tz::__at __at{chrono::seconds{0}, __tz::__clock::__local};
+};
+
+} // namespace chrono::__tz
+
+# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM)
+ // && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+#endif // _LIBCPP___CHRONO_TIME_ZONE_TYPES_H
diff --git a/libcxx/include/__chrono/tzdb.h b/libcxx/include/__chrono/tzdb.h
index bd7b05d478e50..fc230c550327d 100644
--- a/libcxx/include/__chrono/tzdb.h
+++ b/libcxx/include/__chrono/tzdb.h
@@ -16,7 +16,13 @@
// Enable the contents of the header only when libc++ was built with experimental features enabled.
#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+# include <__availability>
+# include <__chrono/time_zone.h>
+# include <__chrono/time_zone_link.h>
+# include <__chrono/time_zone_types.h>
+# include <__config>
# include <string>
+# include <vector>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -31,6 +37,9 @@ namespace chrono {
struct _LIBCPP_AVAILABILITY_TZDB tzdb {
string version;
+ vector<pair<string, vector<__tz::__rule>>> __rules;
+ vector<time_zone> zones;
+ vector<time_zone_link> links;
};
} // namespace chrono
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index c80fa78a56ba1..036fbac9d6028 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -686,6 +686,8 @@ constexpr hours make24(const hours& h, bool is_pm) noexcept;
// [time.zone.db], time zone database
struct tzdb { // C++20
string version;
+ vector<time_zone> zones;
+ vector<time_zone_link> links;
};
class tzdb_list { // C++20
@@ -716,15 +718,34 @@ tzdb_list& get_tzdb_list();
const tzdb& reload_tzdb(); // C++20
string remote_version(); // C++20
-// 25.10.5, class time_zone // C++20
+// 25.10.5, class time_zone // C++20
enum class choose {earliest, latest};
-class time_zone;
-bool operator==(const time_zone& x, const time_zone& y) noexcept;
-bool operator!=(const time_zone& x, const time_zone& y) noexcept;
-bool operator<(const time_zone& x, const time_zone& y) noexcept;
-bool operator>(const time_zone& x, const time_zone& y) noexcept;
-bool operator<=(const time_zone& x, const time_zone& y) noexcept;
-bool operator>=(const time_zone& x, const time_zone& y) noexcept;
+class time_zone {
+ time_zone(time_zone&&) = default;
+ time_zone& operator=(time_zone&&) = default;
+
+ // unspecified additional constructors
+
+ string_view name() const noexcept;
+};
+bool operator==(const time_zone& x, const time_zone& y) noexcept; // C++20
+strong_ordering operator<=>(const time_zone& x, const time_zone& y) noexcept; // C++20
+
+// [time.zone.link], class time_zone_link
+class time_zone_link { // C++20
+public:
+ time_zone_link(time_zone_link&&) = default;
+ time_zone_link& operator=(time_zone_link&&) = default;
+
+ // unspecified additional constructors
+
+ string_view name() const noexcept;
+ string_view target() const noexcept;
+};
+
+bool operator==(const time_zone_link& x, const time_zone_link& y); // C++20
+strong_ordering operator<=>(const time_zone_link& x, const time_zone_link& y); // C++20
+
} // chrono
namespace std {
@@ -842,6 +863,9 @@ constexpr chrono::year operator ""y(unsigned lo
#if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
+# include <__chrono/time_zone.h>
+# include <__chrono/time_zone_link.h>
+# include <__chrono/time_zone_types.h>
# include <__chrono/tzdb.h>
# include <__chrono/tzdb_list.h>
#endif
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 9828c48e0587b..42bdac096fbb7 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1155,6 +1155,18 @@ module std_private_chrono_steady_clock [system] {
header "__chrono/steady_clock.h"
export std_private_chrono_time_point
}
+module std_private_chrono_time_zone [system] {
+ header "__chrono/time_zone.h"
+ export *
+}
+module std_private_chrono_time_zone_link [system] {
+ header "__chrono/time_zone_link.h"
+ export *
+}
+module std_private_chrono_time_zone_types [system] {
+ header "__chrono/time_zone_types.h"
+ export *
+}
module std_private_chrono_system_clock [system] {
header "__chrono/system_clock.h"
export std_private_chrono_time_point
diff --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc
index 65dc973936c47..8ebe8d26065dc 100644
--- a/libcxx/modules/std/chrono.inc
+++ b/libcxx/modules/std/chrono.inc
@@ -221,7 +221,9 @@ export namespace std {
// [time.zone.timezone], class time_zone
using std::chrono::choose;
+# endif
using std::chrono::time_zone;
+# if 0
// [time.zone.zonedtraits], class template zoned_traits
using std::chrono::zoned_traits;
@@ -233,10 +235,12 @@ export namespace std {
// [time.zone.leap], leap second support
using std::chrono::leap_second;
+# endif
// [time.zone.link], class time_zone_link
using std::chrono::time_zone_link;
+# if 0
// [time.format], formatting
using std::chrono::local_time_format;
# endif
diff --git a/libcxx/src/tz.cpp b/libcxx/src/tz.cpp
index 4425f0e6b91bd..472cac136dd77 100644
--- a/libcxx/src/tz.cpp
+++ b/libcxx/src/tz.cpp
@@ -8,6 +8,7 @@
// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+#include <algorithm>
#include <chrono>
#include <filesystem>
#include <fstream>
@@ -49,6 +50,10 @@ _LIBCPP_WEAK string_view __libcpp_tzdb_directory() {
#endif
}
+//===----------------------------------------------------------------------===//
+// Details
+//===----------------------------------------------------------------------===//
+
[[nodiscard]] static bool __is_whitespace(int __c) { return __c == ' ' || __c == '\t'; }
static void __skip_optional_whitespace(istream& __input) {
@@ -63,6 +68,26 @@ static void __skip_mandatory_whitespace(istream& __input) {
chrono::__skip_optional_whitespace(__input);
}
+[[nodiscard]] static bool __is_eol(int __c) { return __c == '\n' || __c == std::char_traits<char>::eof(); }
+
+static void __skip_line(istream& __input) {
+ while (!chrono::__is_eol(__input.peek())) {
+ __input.get();
+ }
+ __input.get();
+}
+
+static void __skip(istream& __input, char __suffix) {
+ if (std::tolower(__input.peek()) == __suffix)
+ __input.get();
+}
+
+static void __skip(istream& __input, string_view __suffix) {
+ for (auto __c : __suffix)
+ if (std::tolower(__input.peek()) == __c)
+ __input.get();
+}
+
static void __matches(istream& __input, char __expected) {
if (std::tolower(__input.get()) != __expected)
std::__throw_runtime_error((string("corrupt tzdb: expected character '") + __expected + '\'').c_str());
@@ -96,6 +121,369 @@ static void __matches(istream& __input, string_view __expected) {
}
}
+[[nodiscard]] static int64_t __parse_integral(istream& __input, bool __leading_zero_allowed) {
+ int64_t __result = __input.get();
+ if (__leading_zero_allowed) {
+ if (__result < '0' || __result > '9')
+ std::__throw_runtime_error("corrupt tzdb: expected a digit");
+ } else {
+ if (__result < '1' || __result > '9')
+ std::__throw_runtime_error("corrupt tzdb: expected a non-zero digit");
+ }
+ __result -= '0';
+ while (true) {
+ if (__input.peek() < '0' || __input.peek() > '9')
+ return __result;
+
+ // In order to avoid possible overflows we limit the accepted range.
+ // Most values parsed are expected to be very small:
+ // - 8784 hours in a year
+ // - 31 days in a month
+ // - year no real maximum, these values are expected to be less than
+ // the range of the year type.
+ //
+ // However the leapseconds use a seconds after epoch value. Using an
+ // int would run into an overflow in 2038. By using a 64-bit value
+ // the range is large enough for the bilions of years. Limiting that
+ // range slightly to make the code easier is not an issue.
+ if (__result > (std::numeric_limits<int64_t>::max() / 16))
+ std::__throw_runtime_error("corrupt tzdb: integral too large");
+
+ __result *= 10;
+ __result += __input.get() - '0';
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Calendar
+//===----------------------------------------------------------------------===//
+
+[[nodiscard]] static day __parse_day(istream& __input) {
+ unsigned __result = chrono::__parse_integral(__input, false);
+ if (__result > 31)
+ std::__throw_runtime_error("corrupt tzdb day: value too large");
+ return day{__result};
+}
+
+[[nodiscard]] static weekday __parse_weekday(istream& __input) {
+ // TZDB allows the shortest unique name.
+ switch (std::tolower(__input.get())) {
+ case 'f':
+ chrono::__skip(__input, "riday");
+ return Friday;
+
+ case 'm':
+ chrono::__skip(__input, "onday");
+ return Monday;
+
+ case 's':
+ switch (std::tolower(__input.get())) {
+ case 'a':
+ chrono::__skip(__input, "turday");
+ return Saturday;
+
+ case 'u':
+ chrono::__skip(__input, "unday");
+ return Sunday;
+ }
+ break;
+
+ case 't':
+ switch (std::tolower(__input.get())) {
+ case 'h':
+ chrono::__skip(__input, "ursday");
+ return Thursday;
+
+ case 'u':
+ chrono::__skip(__input, "esday");
+ return Tuesday;
+ }
+ break;
+ case 'w':
+ chrono::__skip(__input, "ednesday");
+ return Wednesday;
+ }
+
+ std::__throw_runtime_error("corrupt tzdb weekday: invalid name");
+}
+
+[[nodiscard]] static month __parse_month(istream& __input) {
+ // TZDB allows the shortest unique name.
+ switch (std::tolower(__input.get())) {
+ case 'a':
+ switch (std::tolower(__input.get())) {
+ case 'p':
+ chrono::__skip(__input, "ril");
+ return April;
+
+ case 'u':
+ chrono::__skip(__input, "gust");
+ return August;
+ }
+ break;
+
+ case 'd':
+ chrono::__skip(__input, "ecember");
+ return December;
+
+ case 'f':
+ chrono::__skip(__input, "ebruary");
+ return February;
+
+ case 'j':
+ switch (std::tolower(__input.get())) {
+ case 'a':
+ chrono::__skip(__input, "nuary");
+ return January;
+
+ case 'u':
+ switch (std::tolower(__input.get())) {
+ case 'n':
+ chrono::__skip(__input, 'e');
+ return June;
+
+ case 'l':
+ chrono::__skip(__input, 'y');
+ return July;
+ }
+ }
+ break;
+
+ case 'm':
+ if (std::tolower(__input.get()) == 'a')
+ switch (std::tolower(__input.get())) {
+ case 'y':
+ return May;
+
+ case 'r':
+ chrono::__skip(__input, "ch");
+ return March;
+ }
+ break;
+
+ case 'n':
+ chrono::__skip(__input, "ovember");
+ return November;
+
+ case 'o':
+ chrono::__skip(__input, "ctober");
+ return October;
+
+ case 's':
+ chrono::__skip(__input, "eptember");
+ return September;
+ }
+ std::__throw_runtime_error("corrupt tzdb month: invalid name");
+}
+
+[[nodiscard]] static year __parse_year_value(istream& __input) {
+ bool __negative = __input.peek() == '-';
+ if (__negative) [[unlikely]]
+ __input.get();
+
+ static_assert(year::min() == -year::max());
+ int __result = std::min<int64_t>(__parse_integral(__input, true), static_cast<int>(year::max()));
+
+ return year{__negative ? -__result : __result};
+}
+
+[[nodiscard]] static year __parse_year(istream& __input) {
+ if (std::tolower(__input.peek()) != 'm') [[likely]]
+ return chrono::__parse_year_value(__input);
+
+ __input.get();
+ switch (std::tolower(__input.peek())) {
+ case 'i':
+ __input.get();
+ chrono::__skip(__input, 'n');
+ [[fallthrough]];
+
+ case ' ':
+ // The m is minimum, even when that is ambigious.
+ return year::min();
+
+ case 'a':
+ __input.get();
+ chrono::__skip(__input, 'x');
+ return year::max();
+ }
+
+ std::__throw_runtime_error("corrupt tzdb year: expected 'min' or 'max'");
+}
+
+//===----------------------------------------------------------------------===//
+// TZDB fields
+//===----------------------------------------------------------------------===//
+
+[[nodiscard]] static year __parse_to(istream& __input, year __only) {
+ if (std::tolower(__input.peek()) != 'o')
+ return chrono::__parse_year(__input);
+
+ __input.get();
+ chrono::__skip(__input, "nly");
+ return __only;
+}
+
+[[nodiscard]] static __tz::__constrained_weekday::__comparison_t __parse_comparison(istream& __input) {
+ switch (__input.get()) {
+ case '>':
+ chrono::__matches(__input, '=');
+ return __tz::__constrained_weekday::__ge;
+
+ case '<':
+ chrono::__matches(__input, '=');
+ return __tz::__constrained_weekday::__le;
+ }
+ std::__throw_runtime_error("corrupt tzdb on: expected '>=' or '<='");
+}
+
+[[nodiscard]] static __tz::__on __parse_on(istream& __input) {
+ if (std::isdigit(__input.peek()))
+ return chrono::__parse_day(__input);
+
+ if (std::tolower(__input.peek()) == 'l') {
+ chrono::__matches(__input, "last");
+ return weekday_last(chrono::__parse_weekday(__input));
+ }
+
+ return __tz::__constrained_weekday{
+ chrono::__parse_weekday(__input), chrono::__parse_comparison(__input), chrono::__parse_day(__input)};
+}
+
+[[nodiscard]] static seconds __parse_duration(istream& __input) {
+ seconds __result{0};
+ int __c = __input.peek();
+ bool __negative = __c == '-';
+ if (__negative) {
+ __input.get();
+ // Negative is either a negative value or a single -.
+ // The latter means 0 and the parsing is complete.
+ if (!std::isdigit(__input.peek()))
+ return __result;
+ }
+
+ __result += hours(__parse_integral(__input, true));
+ if (__input.peek() != ':')
+ return __negative ? -__result : __result;
+
+ __input.get();
+ __result += minutes(__parse_integral(__input, true));
+ if (__input.peek() != ':')
+ return __negative ? -__result : __result;
+
+ __input.get();
+ __result += seconds(__parse_integral(__input, true));
+ if (__input.peek() != '.')
+ return __negative ? -__result : __result;
+
+ __input.get();
+ (void)__parse_integral(__input, true); // Truncate the digits.
+
+ return __negative ? -__result : __result;
+}
+
+[[nodiscard]] static __tz::__clock __parse_clock(istream& __input) {
+ switch (__input.get()) { // case sensitive
+ case 'w':
+ return __tz::__clock::__local;
+ case 's':
+ return __tz::__clock::__standard;
+
+ case 'u':
+ case 'g':
+ case 'z':
+ return __tz::__clock::__universal;
+ }
+
+ __input.unget();
+ return __tz::__clock::__local;
+}
+
+[[nodiscard]] static bool __parse_dst(istream& __input, seconds __offset) {
+ switch (__input.get()) { // case sensitive
+ case 's':
+ return false;
+
+ case 'd':
+ return true;
+ }
+
+ __input.unget();
+ return __offset != 0s;
+}
+
+[[nodiscard]] static __tz::__at __parse_at(istream& __input) {
+ return {__parse_duration(__input), __parse_clock(__input)};
+}
+
+[[nodiscard]] static __tz::__save __parse_save(istream& __input) {
+ seconds __time = chrono::__parse_duration(__input);
+ return {__time, chrono::__parse_dst(__input, __time)};
+}
+
+[[nodiscard]] static string __parse_letters(istream& __input) {
+ string __result = __parse_string(__input);
+ return __result != "-" ? __result : "";
+}
+
+[[nodiscard]] static __tz::__continuation::__rules_t __parse_rules(istream& __input) {
+ int __c = __input.peek();
+ // A single - is not a SAVE but a special case.
+ if (__c == '-') {
+ __input.get();
+ if (chrono::__is_whitespace(__input.peek()))
+ return monostate{};
+ __input.unget();
+ return chrono::__parse_save(__input);
+ }
+
+ if (std::isdigit(__c) || __c == '+')
+ return chrono::__parse_save(__input);
+
+ return chrono::__parse_string(__input);
+}
+
+[[nodiscard]] static __tz::__continuation __parse_continuation(istream& __input) {
+ __tz::__continuation __result;
+
+ // Note STDOFF is specified as
+ // This field has the same format as the AT and SAVE fields of rule lines;
+ // These fields have different suffix letters, these letters seem
+ // not to be used so do not allow any of them.
+
+ __result.__stdoff = chrono::__parse_duration(__input);
+ chrono::__skip_mandatory_whitespace(__input);
+ __result.__rules = chrono::__parse_rules(__input);
+ chrono::__skip_mandatory_whitespace(__input);
+ __result.__format = chrono::__parse_string(__input);
+ chrono::__skip_optional_whitespace(__input);
+
+ if (chrono::__is_eol(__input.peek()))
+ return __result;
+ __result.__year = chrono::__parse_year(__input);
+ chrono::__skip_optional_whitespace(__input);
+
+ if (chrono::__is_eol(__input.peek()))
+ return __result;
+ __result.__in_month = chrono::__parse_month(__input);
+ chrono::__skip_optional_whitespace(__input);
+
+ if (chrono::__is_eol(__input.peek()))
+ return __result;
+ __result.__on = chrono::__parse_on(__input);
+ chrono::__skip_optional_whitespace(__input);
+
+ if (chrono::__is_eol(__input.peek()))
+ return __result;
+ __result.__at = __parse_at(__input);
+
+ return __result;
+}
+
+//===----------------------------------------------------------------------===//
+// Time Zone Database entries
+//===----------------------------------------------------------------------===//
+
static string __parse_version(istream& __input) {
// The first line in tzdata.zi contains
// # version YYYYw
@@ -109,6 +497,96 @@ static string __parse_version(istream& __input) {
return chrono::__parse_string(__input);
}
+static void __parse_rule(tzdb& __tzdb, istream& __input) {
+ chrono::__skip_mandatory_whitespace(__input);
+ string __name = chrono::__parse_string(__input);
+
+ if (__tzdb.__rules.empty() || __tzdb.__rules.back().first != __name)
+ __tzdb.__rules.emplace_back(__name, vector<__tz::__rule>{});
+
+ __tz::__rule& __rule = __tzdb.__rules.back().second.emplace_back();
+
+ chrono::__skip_mandatory_whitespace(__input);
+ __rule.__from = chrono::__parse_year(__input);
+ chrono::__skip_mandatory_whitespace(__input);
+ __rule.__to = chrono::__parse_to(__input, __rule.__from);
+ chrono::__skip_mandatory_whitespace(__input);
+ chrono::__matches(__input, '-');
+ chrono::__skip_mandatory_whitespace(__input);
+ __rule.__in_month = chrono::__parse_month(__input);
+ chrono::__skip_mandatory_whitespace(__input);
+ __rule.__on = chrono::__parse_on(__input);
+ chrono::__skip_mandatory_whitespace(__input);
+ __rule.__at = __parse_at(__input);
+ chrono::__skip_mandatory_whitespace(__input);
+ __rule.__save = __parse_save(__input);
+ chrono::__skip_mandatory_whitespace(__input);
+ __rule.__letters = chrono::__parse_letters(__input);
+ chrono::__skip_line(__input);
+}
+
+static void __parse_zone(tzdb& __tzdb, istream& __input) {
+ chrono::__skip_mandatory_whitespace(__input);
+ vector<__tz::__continuation>& __continuations =
+ __tzdb.zones.emplace_back(chrono::__parse_string(__input)).__continuations();
+ chrono::__skip_mandatory_whitespace(__input);
+
+ do {
+ // The first line must be valid, continuations are optional.
+ __continuations.emplace_back(__parse_continuation(__input));
+ chrono::__skip_line(__input);
+ chrono::__skip_optional_whitespace(__input);
+ } while (std::isdigit(__input.peek()) || __input.peek() == '-');
+}
+
+static void __parse_link(tzdb& __tzdb, istream& __input) {
+ chrono::__skip_mandatory_whitespace(__input);
+ string __target = chrono::__parse_string(__input);
+ chrono::__skip_mandatory_whitespace(__input);
+ string __name = chrono::__parse_string(__input);
+ chrono::__skip_line(__input);
+
+ __tzdb.links.emplace_back(std::move(__name), std::move(__target));
+}
+
+static void __parse_tzdata(tzdb& __db, istream& __input) {
+ while (true) {
+ int __c = std::tolower(__input.get());
+
+ switch (__c) {
+ case istream::traits_type::eof():
+ return;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ break;
+
+ case '#':
+ chrono::__skip_line(__input);
+ break;
+
+ case 'r':
+ chrono::__skip(__input, "ule");
+ chrono::__parse_rule(__db, __input);
+ break;
+
+ case 'z':
+ chrono::__skip(__input, "one");
+ chrono::__parse_zone(__db, __input);
+ break;
+
+ case 'l':
+ chrono::__skip(__input, "link");
+ chrono::__parse_link(__db, __input);
+ break;
+
+ default:
+ std::__throw_runtime_error("corrupt tzdb: unexpected input");
+ }
+ }
+}
+
static tzdb __make_tzdb() {
tzdb __result;
@@ -116,6 +594,11 @@ static tzdb __make_tzdb() {
ifstream __tzdata{__root / "tzdata.zi"};
__result.version = chrono::__parse_version(__tzdata);
+ chrono::__parse_tzdata(__result, __tzdata);
+ std::ranges::sort(__result.__rules, {}, [](const auto& p) { return p.first; });
+ std::ranges::sort(__result.zones);
+ std::ranges::sort(__result.links);
+
return __result;
}
diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp
index 1f30af06fedd8..850734c98206b 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp
@@ -34,4 +34,20 @@ void test() {
std::chrono::get_tzdb_list();
std::chrono::get_tzdb();
std::chrono::remote_version();
+
+ {
+ std::chrono::time_zone tz{"name"};
+ tz.name();
+ operator==(tz, tz);
+ operator<=>(tz, tz);
+ }
+
+ {
+ std::chrono::time_zone_link("name", "target");
+ std::chrono::time_zone_link link("name", "target");
+ link.name();
+ link.target();
+ operator==(link, link);
+ operator<=>(link, link);
+ }
}
diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
index ab68b1fb44f96..f75172d898f84 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
@@ -32,4 +32,23 @@ void test() {
crno::get_tzdb_list(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
crno::get_tzdb(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
crno::remote_version(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+ {
+ crno::time_zone tz{"name"};
+ tz.name(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ operator==(tz, tz); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ operator<=>(tz, tz); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ }
+
+ {
+ // expected-warning at +1 {{ignoring temporary created by a constructor declared with 'nodiscard' attribute}}
+ crno::time_zone_link("name", "target");
+ crno::time_zone_link link("name", "target");
+ link.name(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ link.target(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ operator==(link, link);
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ operator<=>(link, link);
+ }
}
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.db/links.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.db/links.pass.cpp
new file mode 100644
index 0000000000000..92d761d46bcce
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/links.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// Tests the IANA database rules links and operations.
+// This is not part of the public tzdb interface.
+
+#include <chrono>
+
+#include <cassert>
+#include <chrono>
+#include <fstream>
+#include <string>
+#include <string_view>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "test_tzdb.h"
+
+scoped_test_env env;
+[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
+const std::filesystem::path file = env.create_file("zoneinfo/tzdata.zi");
+
+std::string_view std::chrono::__libcpp_tzdb_directory() {
+ static std::string result = dir.string();
+ return result;
+}
+
+void write(std::string_view input) {
+ static int version = 0;
+
+ std::ofstream f{file};
+ f << "# version " << version++ << '\n';
+ f.write(input.data(), input.size());
+}
+
+static const std::chrono::tzdb& parse(std::string_view input) {
+ write(input);
+ return std::chrono::reload_tzdb();
+}
+
+static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) {
+ write(input);
+
+ TEST_VALIDATE_EXCEPTION(
+ std::runtime_error,
+ [&]([[maybe_unused]] const std::runtime_error& e) {
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ TEST_IGNORE_NODISCARD std::chrono::reload_tzdb());
+}
+
+static void test_invalid() {
+ test_exception("L", "corrupt tzdb: expected whitespace");
+
+ test_exception("L ", "corrupt tzdb: expected a string");
+
+ test_exception("L n", "corrupt tzdb: expected whitespace");
+
+ test_exception("L n ", "corrupt tzdb: expected a string");
+}
+
+static void test_link() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+L z d
+l b a
+lInK b b
+)");
+ assert(result.links.size() == 3);
+
+ assert(result.links[0].name() == "a");
+ assert(result.links[0].target() == "b");
+
+ assert(result.links[1].name() == "b");
+ assert(result.links[1].target() == "b");
+
+ assert(result.links[2].name() == "d");
+ assert(result.links[2].target() == "z");
+}
+
+int main(int, const char**) {
+ test_invalid();
+ test_link();
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp
new file mode 100644
index 0000000000000..98fb2b02f6b3f
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp
@@ -0,0 +1,565 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// Tests the IANA database rules parsing and operations.
+// This is not part of the public tzdb interface.
+
+#include <chrono>
+#include <fstream>
+#include <string>
+#include <string_view>
+#include <variant>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "test_tzdb.h"
+
+scoped_test_env env;
+[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
+const std::filesystem::path file = env.create_file("zoneinfo/tzdata.zi");
+
+std::string_view std::chrono::__libcpp_tzdb_directory() {
+ static std::string result = dir.string();
+ return result;
+}
+
+void write(std::string_view input) {
+ static int version = 0;
+
+ std::ofstream f{file};
+ f << "# version " << version++ << '\n';
+ f.write(input.data(), input.size());
+}
+
+static const std::chrono::tzdb& parse(std::string_view input) {
+ write(input);
+ return std::chrono::reload_tzdb();
+}
+
+static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) {
+ write(input);
+
+ TEST_VALIDATE_EXCEPTION(
+ std::runtime_error,
+ [&]([[maybe_unused]] const std::runtime_error& e) {
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ TEST_IGNORE_NODISCARD std::chrono::reload_tzdb());
+}
+
+static void test_invalid() {
+ test_exception("R", "corrupt tzdb: expected whitespace");
+
+ test_exception("R ", "corrupt tzdb: expected a string");
+
+ test_exception("R r", "corrupt tzdb: expected whitespace");
+
+ test_exception("R r x", "corrupt tzdb: expected a digit");
+ test_exception("R r +", "corrupt tzdb: expected a digit");
+ test_exception("R r mx", "corrupt tzdb year: expected 'min' or 'max'");
+
+ test_exception("R r mix", "corrupt tzdb: expected whitespace");
+ test_exception("R r 0", "corrupt tzdb: expected whitespace");
+
+ test_exception("R r 0 x", "corrupt tzdb: expected a digit");
+ test_exception("R r 0 +", "corrupt tzdb: expected a digit");
+ test_exception("R r 0 mx", "corrupt tzdb year: expected 'min' or 'max'");
+
+ test_exception("R r 0 mix", "corrupt tzdb: expected whitespace");
+ test_exception("R r 0 1", "corrupt tzdb: expected whitespace");
+
+ test_exception("R r 0 1 X", "corrupt tzdb: expected character '-'");
+
+ test_exception("R r 0 1 -", "corrupt tzdb: expected whitespace");
+
+ test_exception("R r 0 1 - j", "corrupt tzdb month: invalid name");
+
+ test_exception("R r 0 1 - Ja", "corrupt tzdb: expected whitespace");
+
+ test_exception("R r 0 1 - Ja +", "corrupt tzdb weekday: invalid name");
+ test_exception("R r 0 1 - Ja 32", "corrupt tzdb day: value too large");
+ test_exception("R r 0 1 - Ja l", "corrupt tzdb: expected string 'last'");
+ test_exception("R r 0 1 - Ja last", "corrupt tzdb weekday: invalid name");
+ test_exception("R r 0 1 - Ja lastS", "corrupt tzdb weekday: invalid name");
+ test_exception("R r 0 1 - Ja S", "corrupt tzdb weekday: invalid name");
+ test_exception("R r 0 1 - Ja Su", "corrupt tzdb on: expected '>=' or '<='");
+ test_exception("R r 0 1 - Ja Su>", "corrupt tzdb: expected character '='");
+ test_exception("R r 0 1 - Ja Su<", "corrupt tzdb: expected character '='");
+ test_exception("R r 0 1 - Ja Su>=+", "corrupt tzdb: expected a non-zero digit");
+ test_exception("R r 0 1 - Ja Su>=0", "corrupt tzdb: expected a non-zero digit");
+ test_exception("R r 0 1 - Ja Su>=32", "corrupt tzdb day: value too large");
+
+ test_exception("R r 0 1 - Ja Su>=31", "corrupt tzdb: expected whitespace");
+
+ test_exception("R r 0 1 - Ja Su>=31 ", "corrupt tzdb: expected a digit");
+ test_exception("R r 0 1 - Ja Su>=31 +", "corrupt tzdb: expected a digit");
+
+ test_exception("R r 0 1 - Ja Su>=31 1", "corrupt tzdb: expected whitespace");
+ test_exception("R r 0 1 - Ja Su>=31 1a", "corrupt tzdb: expected whitespace");
+
+ test_exception("R r 0 1 - Ja Su>=31 1w 2", "corrupt tzdb: expected whitespace");
+ test_exception("R r 0 1 - Ja Su>=31 1w 2a", "corrupt tzdb: expected whitespace");
+
+ test_exception("R r 0 1 - Ja Su>=31 1w 2s", "corrupt tzdb: expected whitespace");
+ test_exception("R r 0 1 - Ja Su>=31 1w 2s ", "corrupt tzdb: expected a string");
+}
+
+static void test_name() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+R z 0 1 - Ja Su>=31 1w 2s -
+rULE z 0 1 - Ja Su>=31 1w 2s -
+RuLe z 0 1 - Ja Su>=31 1w 2s -
+R a 0 1 - Ja Su>=31 1w 2s -
+R a 0 1 - Ja Su>=31 1w 2s -
+)");
+
+ assert(result.__rules.size() == 2);
+ assert(result.__rules[0].first == "a");
+ assert(result.__rules[0].second.size() == 2);
+ assert(result.__rules[1].first == "z");
+ assert(result.__rules[1].second.size() == 3);
+}
+
+static void test_from() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+# min abbrviations
+R a M 1 - Ja Su>=31 1w 2s -
+R a mI 1 - Ja Su>=31 1w 2s -
+R a mIN 1 - Ja Su>=31 1w 2s -
+
+# max abbrviations
+R a MA 1 - Ja Su>=31 1w 2s -
+R a mAx 1 - Ja Su>=31 1w 2s -
+
+# 40000 > max
+R a -40000 1 - Ja Su>=31 1w 2s -
+R a -1000 1 - Ja Su>=31 1w 2s -
+R a -100 1 - Ja Su>=31 1w 2s -
+R a 0000 1 - Ja Su>=31 1w 2s -
+R a 100 1 - Ja Su>=31 1w 2s -
+R a 1000 1 - Ja Su>=31 1w 2s -
+# 40000 < min
+R a 40000 1 - Ja Su>=31 1w 2s -
+)");
+
+ assert(result.__rules.size() == 1);
+ assert(result.__rules[0].second.size() == 12);
+
+ assert(result.__rules[0].second[0].__from == std::chrono::year::min());
+ assert(result.__rules[0].second[1].__from == std::chrono::year::min());
+ assert(result.__rules[0].second[2].__from == std::chrono::year::min());
+
+ assert(result.__rules[0].second[3].__from == std::chrono::year::max());
+ assert(result.__rules[0].second[4].__from == std::chrono::year::max());
+
+ assert(result.__rules[0].second[5].__from == std::chrono::year::min());
+ assert(result.__rules[0].second[6].__from == std::chrono::year(-1000));
+ assert(result.__rules[0].second[7].__from == std::chrono::year(-100));
+ assert(result.__rules[0].second[8].__from == std::chrono::year(0));
+ assert(result.__rules[0].second[9].__from == std::chrono::year(100));
+ assert(result.__rules[0].second[10].__from == std::chrono::year(1000));
+ assert(result.__rules[0].second[11].__from == std::chrono::year::max());
+}
+
+static void test_to() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+# min abbrviations
+R a 0 m - Ja Su>=31 1w 2s -
+R a 0 mi - Ja Su>=31 1w 2s -
+R a 0 min - Ja Su>=31 1w 2s -
+
+# max abbrviations
+R a 0 ma - Ja Su>=31 1w 2s -
+R a 0 max - Ja Su>=31 1w 2s -
+
+# 40000 > max
+R a 0 -40000 - Ja Su>=31 1w 2s -
+R a 0 -1000 - Ja Su>=31 1w 2s -
+R a 0 -100 - Ja Su>=31 1w 2s -
+R a 0 0000 - Ja Su>=31 1w 2s -
+R a 0 100 - Ja Su>=31 1w 2s -
+R a 0 1000 - Ja Su>=31 1w 2s -
+# 40000 < min
+R a 0 40000 - Ja Su>=31 1w 2s -
+
+# only abbreviations
+R a m O - Ja Su>=31 1w 2s -
+R a ma oN - Ja Su>=31 1w 2s -
+R a -100 onL - Ja Su>=31 1w 2s -
+R a 100 oNlY - Ja Su>=31 1w 2s -
+)");
+
+ assert(result.__rules.size() == 1);
+ assert(result.__rules[0].second.size() == 16);
+
+ assert(result.__rules[0].second[0].__to == std::chrono::year::min());
+ assert(result.__rules[0].second[1].__to == std::chrono::year::min());
+ assert(result.__rules[0].second[2].__to == std::chrono::year::min());
+
+ assert(result.__rules[0].second[3].__to == std::chrono::year::max());
+ assert(result.__rules[0].second[4].__to == std::chrono::year::max());
+
+ assert(result.__rules[0].second[5].__to == std::chrono::year::min());
+ assert(result.__rules[0].second[6].__to == std::chrono::year(-1000));
+ assert(result.__rules[0].second[7].__to == std::chrono::year(-100));
+ assert(result.__rules[0].second[8].__to == std::chrono::year(0));
+ assert(result.__rules[0].second[9].__to == std::chrono::year(100));
+ assert(result.__rules[0].second[10].__to == std::chrono::year(1000));
+ assert(result.__rules[0].second[11].__to == std::chrono::year::max());
+
+ assert(result.__rules[0].second[12].__to == std::chrono::year::min());
+ assert(result.__rules[0].second[13].__to == std::chrono::year::max());
+ assert(result.__rules[0].second[14].__to == std::chrono::year(-100));
+ assert(result.__rules[0].second[15].__to == std::chrono::year(100));
+}
+
+static void test_in() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+# All tests in alphabetic order to validate shortest unique abbreviation
+
+# Shortest abbreviation valid
+R s 0 1 - ap Su>=31 1w 2s -
+R s 0 1 - au Su>=31 1w 2s -
+R s 0 1 - d Su>=31 1w 2s -
+R s 0 1 - f Su>=31 1w 2s -
+R s 0 1 - ja Su>=31 1w 2s -
+R s 0 1 - jul Su>=31 1w 2s -
+R s 0 1 - jun Su>=31 1w 2s -
+R s 0 1 - May Su>=31 1w 2s -
+R s 0 1 - mar Su>=31 1w 2s -
+R s 0 1 - n Su>=31 1w 2s -
+R s 0 1 - o Su>=31 1w 2s -
+R s 0 1 - s Su>=31 1w 2s -
+
+# 3 letter abbreviation
+R a 0 1 - APR Su>=31 1w 2s -
+R a 0 1 - AUG Su>=31 1w 2s -
+R a 0 1 - DEC Su>=31 1w 2s -
+R a 0 1 - FEB Su>=31 1w 2s -
+R a 0 1 - JAN Su>=31 1w 2s -
+R a 0 1 - JUL Su>=31 1w 2s -
+R a 0 1 - JUN Su>=31 1w 2s -
+R a 0 1 - MAY Su>=31 1w 2s -
+R a 0 1 - MAR Su>=31 1w 2s -
+R a 0 1 - NOV Su>=31 1w 2s -
+R a 0 1 - OCT Su>=31 1w 2s -
+R a 0 1 - SEP Su>=31 1w 2s -
+
+# Full names
+R f 0 1 - ApRiL Su>=31 1w 2s -
+R f 0 1 - AuGuSt Su>=31 1w 2s -
+R f 0 1 - DeCeMber Su>=31 1w 2s -
+R f 0 1 - FeBrUary Su>=31 1w 2s -
+R f 0 1 - JaNuAry Su>=31 1w 2s -
+R f 0 1 - JuLy Su>=31 1w 2s -
+R f 0 1 - JuNe Su>=31 1w 2s -
+R f 0 1 - MaY Su>=31 1w 2s -
+R f 0 1 - MaRch Su>=31 1w 2s -
+R f 0 1 - NoVemBeR Su>=31 1w 2s -
+R f 0 1 - OcTobEr Su>=31 1w 2s -
+R f 0 1 - SePteMbEr Su>=31 1w 2s -
+)");
+
+ assert(result.__rules.size() == 3);
+ for (std::size_t i = 0; i < result.__rules.size(); ++i) {
+ assert(result.__rules[i].second.size() == 12);
+
+ assert(result.__rules[i].second[0].__in_month == std::chrono::April);
+ assert(result.__rules[i].second[1].__in_month == std::chrono::August);
+ assert(result.__rules[i].second[2].__in_month == std::chrono::December);
+ assert(result.__rules[i].second[3].__in_month == std::chrono::February);
+ assert(result.__rules[i].second[4].__in_month == std::chrono::January);
+ assert(result.__rules[i].second[5].__in_month == std::chrono::July);
+ assert(result.__rules[i].second[6].__in_month == std::chrono::June);
+ assert(result.__rules[i].second[7].__in_month == std::chrono::May);
+ assert(result.__rules[i].second[8].__in_month == std::chrono::March);
+ assert(result.__rules[i].second[9].__in_month == std::chrono::November);
+ assert(result.__rules[i].second[10].__in_month == std::chrono::October);
+ assert(result.__rules[i].second[11].__in_month == std::chrono::September);
+ }
+};
+
+static void test_on_day() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+# The parser does not validate the day as valid day of month
+R a 0 1 - Fe 1 1w 2s -
+R a 0 1 - Fe 10 1w 2s -
+R a 0 1 - Fe 20 1w 2s -
+R a 0 1 - Fe 30 1w 2s -
+R a 0 1 - Fe 31 1w 2s -
+)");
+
+ assert(result.__rules.size() == 1);
+ assert(result.__rules[0].second.size() == 5);
+ assert(std::get<std::chrono::day>(result.__rules[0].second[0].__on) == std::chrono::day(1));
+ assert(std::get<std::chrono::day>(result.__rules[0].second[1].__on) == std::chrono::day(10));
+ assert(std::get<std::chrono::day>(result.__rules[0].second[2].__on) == std::chrono::day(20));
+ assert(std::get<std::chrono::day>(result.__rules[0].second[3].__on) == std::chrono::day(30));
+ assert(std::get<std::chrono::day>(result.__rules[0].second[4].__on) == std::chrono::day(31));
+}
+
+static void test_on_last() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+# All tests in alphabetic order to validate shortest unique abbreviation
+
+# Shortest abbreviation valid
+R s 0 1 - Ja lastF 1w 2s -
+R s 0 1 - Ja lastM 1w 2s -
+R s 0 1 - Ja lastSa 1w 2s -
+R s 0 1 - Ja lastSu 1w 2s -
+R s 0 1 - Ja lastTh 1w 2s -
+R s 0 1 - Ja lastTu 1w 2s -
+R s 0 1 - Ja lastW 1w 2s -
+
+# 3 letter abbreviation
+R a 0 1 - Ja lastFri 1w 2s -
+R a 0 1 - Ja lastMon 1w 2s -
+R a 0 1 - Ja lastSat 1w 2s -
+R a 0 1 - Ja lastSun 1w 2s -
+R a 0 1 - Ja lastThu 1w 2s -
+R a 0 1 - Ja lastTue 1w 2s -
+R a 0 1 - Ja lastWed 1w 2s -
+
+# Full names
+R f 0 1 - Ja lastFriday 1w 2s -
+R f 0 1 - Ja lastMonday 1w 2s -
+R f 0 1 - Ja lastSaturday 1w 2s -
+R f 0 1 - Ja lastSunday 1w 2s -
+R f 0 1 - Ja lastThursday 1w 2s -
+R f 0 1 - Ja lastTuesday 1w 2s -
+R f 0 1 - Ja lastWednesday 1w 2s -
+)");
+
+ assert(result.__rules.size() == 3);
+ for (std::size_t i = 0; i < result.__rules.size(); ++i) {
+ assert(result.__rules[i].second.size() == 7);
+
+ assert(std::get<std::chrono::weekday_last>(result.__rules[i].second[0].__on) ==
+ std::chrono::weekday_last(std::chrono::Friday));
+ assert(std::get<std::chrono::weekday_last>(result.__rules[i].second[1].__on) ==
+ std::chrono::weekday_last(std::chrono::Monday));
+ assert(std::get<std::chrono::weekday_last>(result.__rules[i].second[2].__on) ==
+ std::chrono::weekday_last(std::chrono::Saturday));
+ assert(std::get<std::chrono::weekday_last>(result.__rules[i].second[3].__on) ==
+ std::chrono::weekday_last(std::chrono::Sunday));
+ assert(std::get<std::chrono::weekday_last>(result.__rules[i].second[4].__on) ==
+ std::chrono::weekday_last(std::chrono::Thursday));
+ assert(std::get<std::chrono::weekday_last>(result.__rules[i].second[5].__on) ==
+ std::chrono::weekday_last(std::chrono::Tuesday));
+ assert(std::get<std::chrono::weekday_last>(result.__rules[i].second[6].__on) ==
+ std::chrono::weekday_last(std::chrono::Wednesday));
+ }
+}
+
+static void test_on_constrain() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+# Shortest abbreviation valid
+R s 0 1 - Ja F>=1 1w 2s -
+R s 0 1 - Ja M<=1 1w 2s -
+R s 0 1 - Ja Sa>=31 1w 2s -
+R s 0 1 - Ja Su<=31 1w 2s -
+R s 0 1 - Ja Th>=10 1w 2s -
+R s 0 1 - Ja Tu<=20 1w 2s -
+R s 0 1 - Ja W>=30 1w 2s -
+
+# 3 letter abbreviation
+R a 0 1 - Ja Fri>=1 1w 2s -
+R a 0 1 - Ja Mon<=1 1w 2s -
+R a 0 1 - Ja Sat>=31 1w 2s -
+R a 0 1 - Ja Sun<=31 1w 2s -
+R a 0 1 - Ja Thu>=10 1w 2s -
+R a 0 1 - Ja Tue<=20 1w 2s -
+R a 0 1 - Ja Wed>=30 1w 2s -
+
+# Full names
+R f 0 1 - Ja Friday>=1 1w 2s -
+R f 0 1 - Ja Monday<=1 1w 2s -
+R f 0 1 - Ja Saturday>=31 1w 2s -
+R f 0 1 - Ja Sunday<=31 1w 2s -
+R f 0 1 - Ja Thursday>=10 1w 2s -
+R f 0 1 - Ja Tuesday<=20 1w 2s -
+R f 0 1 - Ja Wednesday>=30 1w 2s -
+
+)");
+
+ std::chrono::__tz::__constrained_weekday r;
+ assert(result.__rules.size() == 3);
+ for (std::size_t i = 0; i < result.__rules.size(); ++i) {
+ assert(result.__rules[i].second.size() == 7);
+
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.__rules[i].second[0].__on);
+ assert(r.__weekday == std::chrono::Friday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
+ assert(r.__day == std::chrono::day(1));
+
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.__rules[i].second[1].__on);
+ assert(r.__weekday == std::chrono::Monday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
+ assert(r.__day == std::chrono::day(1));
+
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.__rules[i].second[2].__on);
+ assert(r.__weekday == std::chrono::Saturday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
+ assert(r.__day == std::chrono::day(31));
+
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.__rules[i].second[3].__on);
+ assert(r.__weekday == std::chrono::Sunday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
+ assert(r.__day == std::chrono::day(31));
+
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.__rules[i].second[4].__on);
+ assert(r.__weekday == std::chrono::Thursday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
+ assert(r.__day == std::chrono::day(10));
+
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.__rules[i].second[5].__on);
+ assert(r.__weekday == std::chrono::Tuesday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
+ assert(r.__day == std::chrono::day(20));
+
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.__rules[i].second[6].__on);
+ assert(r.__weekday == std::chrono::Wednesday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
+ assert(r.__day == std::chrono::day(30));
+ }
+}
+
+static void test_on() {
+ test_on_day();
+ test_on_last();
+ test_on_constrain();
+}
+
+static void test_at() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+# Based on the examples in the man page.
+# Note the input is not expected to have fractional seconds, they are truncated.
+R a 0 1 - Ja Su>=31 2w 2s -
+R a 0 1 - Ja Su>=31 2:00s 2s -
+R a 0 1 - Ja Su>=31 01:28:14u 2s -
+R a 0 1 - Ja Su>=31 00:19:32.10g 2s -
+R a 0 1 - Ja Su>=31 12:00z 2s -
+R a 0 1 - Ja Su>=31 15:00 2s -
+R a 0 1 - Ja Su>=31 24:00 2s -
+R a 0 1 - Ja Su>=31 260:00 2s -
+R a 0 1 - Ja Su>=31 -2:30 2s -
+R a 0 1 - Ja Su>=31 - 2s -
+)");
+
+ assert(result.__rules.size() == 1);
+ assert(result.__rules[0].second.size() == 10);
+
+ assert(result.__rules[0].second[0].__at.__time == std::chrono::hours(2));
+ assert(result.__rules[0].second[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.__rules[0].second[1].__at.__time == std::chrono::hours(2));
+ assert(result.__rules[0].second[1].__at.__clock == std::chrono::__tz::__clock::__standard);
+
+ assert(result.__rules[0].second[2].__at.__time ==
+ std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
+ assert(result.__rules[0].second[2].__at.__clock == std::chrono::__tz::__clock::__universal);
+
+ assert(result.__rules[0].second[3].__at.__time == std::chrono::minutes(19) + std::chrono::seconds(32));
+ assert(result.__rules[0].second[3].__at.__clock == std::chrono::__tz::__clock::__universal);
+
+ assert(result.__rules[0].second[4].__at.__time == std::chrono::hours(12));
+ assert(result.__rules[0].second[4].__at.__clock == std::chrono::__tz::__clock::__universal);
+
+ assert(result.__rules[0].second[5].__at.__time == std::chrono::hours(15));
+ assert(result.__rules[0].second[5].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.__rules[0].second[6].__at.__time == std::chrono::hours(24));
+ assert(result.__rules[0].second[6].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.__rules[0].second[7].__at.__time == std::chrono::hours(260));
+ assert(result.__rules[0].second[7].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.__rules[0].second[8].__at.__time == -(std::chrono::hours(2) + std::chrono::minutes(30)));
+ assert(result.__rules[0].second[8].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.__rules[0].second[9].__at.__time == std::chrono::hours(0)); // The man page expresses it in hours
+ assert(result.__rules[0].second[9].__at.__clock == std::chrono::__tz::__clock::__local);
+}
+
+static void test_save() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+R a 0 1 - Ja Su>=31 1w 2d -
+R a 0 1 - Ja Su>=31 1w 2:00s -
+R a 0 1 - Ja Su>=31 1w 0 -
+R a 0 1 - Ja Su>=31 1w 0:00:01 -
+R a 0 1 - Ja Su>=31 1w -0:00:01 -
+)");
+
+ assert(result.__rules.size() == 1);
+ assert(result.__rules[0].second.size() == 5);
+
+ assert(result.__rules[0].second[0].__save.__time == std::chrono::hours(2));
+ assert(result.__rules[0].second[0].__save.__is_dst == true);
+
+ assert(result.__rules[0].second[1].__save.__time == std::chrono::hours(2));
+ assert(result.__rules[0].second[1].__save.__is_dst == false);
+
+ assert(result.__rules[0].second[2].__save.__time == std::chrono::hours(0));
+ assert(result.__rules[0].second[2].__save.__is_dst == false);
+
+ assert(result.__rules[0].second[3].__save.__time == std::chrono::seconds(1));
+ assert(result.__rules[0].second[3].__save.__is_dst == true);
+
+ assert(result.__rules[0].second[4].__save.__time == -std::chrono::seconds(1));
+ assert(result.__rules[0].second[4].__save.__is_dst == true);
+}
+
+static void test_letter() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+R a 0 1 - Ja Su>=31 1w 2s -
+R a 0 1 - Ja Su>=31 1w 2s a
+R a 0 1 - Ja Su>=31 1w 2s abc
+)");
+
+ assert(result.__rules.size() == 1);
+ assert(result.__rules[0].second.size() == 3);
+
+ assert(result.__rules[0].second[0].__letters == "");
+ assert(result.__rules[0].second[1].__letters == "a");
+ assert(result.__rules[0].second[2].__letters == "abc");
+}
+
+int main(int, const char**) {
+ test_invalid();
+ test_name();
+ test_from();
+ test_to();
+ test_in();
+ test_on();
+ test_at();
+ test_save();
+ test_letter();
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.db/zones.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.db/zones.pass.cpp
new file mode 100644
index 0000000000000..c37ccc4528d2d
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/zones.pass.cpp
@@ -0,0 +1,371 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// Tests the IANA database zones parsing and operations.
+// This is not part of the public tzdb interface.
+
+#include <cassert>
+#include <chrono>
+#include <fstream>
+#include <string>
+#include <string_view>
+#include <variant>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "test_tzdb.h"
+
+scoped_test_env env;
+[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
+const std::filesystem::path file = env.create_file("zoneinfo/tzdata.zi");
+
+std::string_view std::chrono::__libcpp_tzdb_directory() {
+ static std::string result = dir.string();
+ return result;
+}
+
+void write(std::string_view input) {
+ static int version = 0;
+
+ std::ofstream f{file};
+ f << "# version " << version++ << '\n';
+ f.write(input.data(), input.size());
+}
+
+static const std::chrono::tzdb& parse(std::string_view input) {
+ write(input);
+ return std::chrono::reload_tzdb();
+}
+
+static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) {
+ write(input);
+
+ TEST_VALIDATE_EXCEPTION(
+ std::runtime_error,
+ [&]([[maybe_unused]] const std::runtime_error& e) {
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ TEST_IGNORE_NODISCARD std::chrono::reload_tzdb());
+}
+
+static void test_invalid() {
+ test_exception("Z", "corrupt tzdb: expected whitespace");
+
+ test_exception("Z ", "corrupt tzdb: expected a string");
+
+ test_exception("Z n", "corrupt tzdb: expected whitespace");
+
+ test_exception("Z n ", "corrupt tzdb: expected a digit");
+ test_exception("Z n x", "corrupt tzdb: expected a digit");
+ test_exception("Z n +", "corrupt tzdb: expected a digit");
+
+ test_exception("Z n 0", "corrupt tzdb: expected whitespace");
+
+ test_exception("Z n 0 ", "corrupt tzdb: expected a string");
+
+ test_exception("Z n 0 r", "corrupt tzdb: expected whitespace");
+
+ test_exception("Z n 0 r ", "corrupt tzdb: expected a string");
+}
+
+static void test_name() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+Z n 0 r f
+)");
+ assert(result.zones.size() == 1);
+ assert(result.zones[0].name() == "n");
+}
+
+static void test_stdoff() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+# Based on the examples in the man page.
+# Note the input is not expected to have fractional seconds, they are truncated.
+Zo na 2 r f
+Zon nb 2:00 r f
+Zone nc 01:28:14 r f
+zONE nd 00:19:32.10 r f
+zoNe ne 12:00 r f
+Z nf 15:00 r f
+Z ng 24:00 r f
+Z nh 260:00 r f
+Z ni -2:30 r f
+Z nj - r f
+)");
+
+ assert(result.zones.size() == 10);
+ for (std::size_t i = 0; i < result.zones.size(); ++i)
+ assert(result.zones[0].__continuations().size() == 1);
+
+ assert(result.zones[0].__continuations()[0].__stdoff == std::chrono::hours(2));
+ assert(result.zones[1].__continuations()[0].__stdoff == std::chrono::hours(2));
+ assert(result.zones[2].__continuations()[0].__stdoff ==
+ std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
+ assert(result.zones[3].__continuations()[0].__stdoff == std::chrono::minutes(19) + std::chrono::seconds(32));
+ assert(result.zones[4].__continuations()[0].__stdoff == std::chrono::hours(12));
+ assert(result.zones[5].__continuations()[0].__stdoff == std::chrono::hours(15));
+ assert(result.zones[6].__continuations()[0].__stdoff == std::chrono::hours(24));
+ assert(result.zones[7].__continuations()[0].__stdoff == std::chrono::hours(260));
+ assert(result.zones[8].__continuations()[0].__stdoff == -(std::chrono::hours(2) + std::chrono::minutes(30)));
+ assert(result.zones[9].__continuations()[0].__stdoff == std::chrono::hours(0)); // The man page expresses it in hours
+}
+
+static void test_rules() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+Z na 0 - f
+Z nb 0 r f
+Z nc 0 2d f
+Z nd 0 2:00s f
+Z ne 0 0 f
+Z nf 0 0:00:01 f
+Z ng 0 -0:00:01 f
+)");
+
+ assert(result.zones.size() == 7);
+ for (std::size_t i = 0; i < result.zones.size(); ++i)
+ assert(result.zones[0].__continuations().size() == 1);
+
+ assert(std::holds_alternative<std::monostate>(result.zones[0].__continuations()[0].__rules));
+ assert(std::get<std::string>(result.zones[1].__continuations()[0].__rules) == "r");
+
+ assert(std::get<std::chrono::__tz::__save>(result.zones[2].__continuations()[0].__rules).__time ==
+ std::chrono::hours(2));
+ assert(std::get<std::chrono::__tz::__save>(result.zones[2].__continuations()[0].__rules).__is_dst == true);
+
+ assert(std::get<std::chrono::__tz::__save>(result.zones[3].__continuations()[0].__rules).__time ==
+ std::chrono::hours(2));
+ assert(std::get<std::chrono::__tz::__save>(result.zones[3].__continuations()[0].__rules).__is_dst == false);
+
+ assert(std::get<std::chrono::__tz::__save>(result.zones[4].__continuations()[0].__rules).__time ==
+ std::chrono::hours(0));
+ assert(std::get<std::chrono::__tz::__save>(result.zones[4].__continuations()[0].__rules).__is_dst == false);
+
+ assert(std::get<std::chrono::__tz::__save>(result.zones[5].__continuations()[0].__rules).__time ==
+ std::chrono::seconds(1));
+ assert(std::get<std::chrono::__tz::__save>(result.zones[5].__continuations()[0].__rules).__is_dst == true);
+
+ assert(std::get<std::chrono::__tz::__save>(result.zones[6].__continuations()[0].__rules).__time ==
+ -std::chrono::seconds(1));
+ assert(std::get<std::chrono::__tz::__save>(result.zones[6].__continuations()[0].__rules).__is_dst == true);
+}
+
+static void test_format() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+Z n 0 r f
+)");
+ assert(result.zones.size() == 1);
+ assert(result.zones[0].__continuations().size() == 1);
+ assert(result.zones[0].__continuations()[0].__format == "f");
+}
+
+static void test_until() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+Z na 0 r f
+Z nb 0 r f 1000
+Z nc 0 r f -1000 N
+Z nd 0 r f ma S 31
+Z ne 0 r f 0 jA LASTw
+Z nf 0 r f -42 jUN m<=1
+Z ng 0 r f 42 jul Su>=12
+Z nh 0 r f 42 JUl 1 2w
+Z ni 0 r f 42 July 1 01:28:14u
+Z nj 0 r f 42 Jul 1 -
+)");
+ assert(result.zones.size() == 10);
+ for (std::size_t i = 0; i < result.zones.size(); ++i)
+ assert(result.zones[0].__continuations().size() == 1);
+
+ std::chrono::__tz::__constrained_weekday r;
+
+ assert(result.zones[0].__continuations()[0].__year == std::chrono::year::min());
+ assert(result.zones[0].__continuations()[0].__in_month == std::chrono::January);
+ assert(std::get<std::chrono::day>(result.zones[0].__continuations()[0].__on) == std::chrono::day(1));
+ assert(result.zones[0].__continuations()[0].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[0].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[1].__continuations()[0].__year == std::chrono::year(1000));
+ assert(result.zones[1].__continuations()[0].__in_month == std::chrono::January);
+ assert(std::get<std::chrono::day>(result.zones[1].__continuations()[0].__on) == std::chrono::day(1));
+ assert(result.zones[1].__continuations()[0].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[1].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[2].__continuations()[0].__year == std::chrono::year(-1000));
+ assert(result.zones[2].__continuations()[0].__in_month == std::chrono::November);
+ assert(std::get<std::chrono::day>(result.zones[2].__continuations()[0].__on) == std::chrono::day(1));
+ assert(result.zones[2].__continuations()[0].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[2].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[3].__continuations()[0].__year == std::chrono::year::max());
+ assert(result.zones[3].__continuations()[0].__in_month == std::chrono::September);
+ assert(std::get<std::chrono::day>(result.zones[3].__continuations()[0].__on) == std::chrono::day(31));
+ assert(result.zones[3].__continuations()[0].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[3].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[4].__continuations()[0].__year == std::chrono::year(0));
+ assert(result.zones[4].__continuations()[0].__in_month == std::chrono::January);
+ assert(std::get<std::chrono::weekday_last>(result.zones[4].__continuations()[0].__on) ==
+ std::chrono::weekday_last{std::chrono::Wednesday});
+ assert(result.zones[4].__continuations()[0].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[4].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[5].__continuations()[0].__year == std::chrono::year(-42));
+ assert(result.zones[5].__continuations()[0].__in_month == std::chrono::June);
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.zones[5].__continuations()[0].__on);
+ assert(r.__weekday == std::chrono::Monday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
+ assert(r.__day == std::chrono::day(1));
+ assert(result.zones[5].__continuations()[0].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[5].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[6].__continuations()[0].__year == std::chrono::year(42));
+ assert(result.zones[6].__continuations()[0].__in_month == std::chrono::July);
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.zones[6].__continuations()[0].__on);
+ assert(r.__weekday == std::chrono::Sunday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
+ assert(r.__day == std::chrono::day(12));
+ assert(result.zones[6].__continuations()[0].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[6].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[7].__continuations()[0].__year == std::chrono::year(42));
+ assert(result.zones[7].__continuations()[0].__in_month == std::chrono::July);
+ assert(std::get<std::chrono::day>(result.zones[7].__continuations()[0].__on) == std::chrono::day(1));
+ assert(result.zones[7].__continuations()[0].__at.__time == std::chrono::hours(2));
+ assert(result.zones[7].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[8].__continuations()[0].__year == std::chrono::year(42));
+ assert(result.zones[8].__continuations()[0].__in_month == std::chrono::July);
+ assert(std::get<std::chrono::day>(result.zones[8].__continuations()[0].__on) == std::chrono::day(1));
+ assert(result.zones[8].__continuations()[0].__at.__time ==
+ std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
+ assert(result.zones[8].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__universal);
+
+ assert(result.zones[9].__continuations()[0].__year == std::chrono::year(42));
+ assert(result.zones[9].__continuations()[0].__in_month == std::chrono::July);
+ assert(std::get<std::chrono::day>(result.zones[9].__continuations()[0].__on) == std::chrono::day(1));
+ assert(result.zones[9].__continuations()[0].__at.__time ==
+ std::chrono::hours(0)); // The man page expresses it in hours
+ assert(result.zones[9].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+}
+
+static void test_continuation() {
+ const std::chrono::tzdb& result = parse(
+ R"(
+Z na 0 r f
+0 r f 1000
+0 r f -1000 N
+0 r f ma S 31
+0 r f 0 Ja lastW
+0 r f -42 Jun M<=1
+0 r f 42 Jul Su>=12
+0 r f 42 Jul 1 2w
+0 r f 42 Jul 1 01:28:14u
+0 r f 42 Jul 1 -
+)");
+
+ assert(result.zones.size() == 1);
+ assert(result.zones[0].__continuations().size() == 10);
+
+ std::chrono::__tz::__constrained_weekday r;
+
+ assert(result.zones[0].__continuations()[0].__year == std::chrono::year::min());
+ assert(result.zones[0].__continuations()[0].__in_month == std::chrono::January);
+ assert(std::get<std::chrono::day>(result.zones[0].__continuations()[0].__on) == std::chrono::day(1));
+ assert(result.zones[0].__continuations()[0].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[0].__continuations()[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[0].__continuations()[1].__year == std::chrono::year(1000));
+ assert(result.zones[0].__continuations()[1].__in_month == std::chrono::January);
+ assert(std::get<std::chrono::day>(result.zones[0].__continuations()[1].__on) == std::chrono::day(1));
+ assert(result.zones[0].__continuations()[1].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[0].__continuations()[1].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[0].__continuations()[2].__year == std::chrono::year(-1000));
+ assert(result.zones[0].__continuations()[2].__in_month == std::chrono::November);
+ assert(std::get<std::chrono::day>(result.zones[0].__continuations()[2].__on) == std::chrono::day(1));
+ assert(result.zones[0].__continuations()[2].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[0].__continuations()[2].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[0].__continuations()[3].__year == std::chrono::year::max());
+ assert(result.zones[0].__continuations()[3].__in_month == std::chrono::September);
+ assert(std::get<std::chrono::day>(result.zones[0].__continuations()[3].__on) == std::chrono::day(31));
+ assert(result.zones[0].__continuations()[3].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[0].__continuations()[3].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[0].__continuations()[4].__year == std::chrono::year(0));
+ assert(result.zones[0].__continuations()[4].__in_month == std::chrono::January);
+ assert(std::get<std::chrono::weekday_last>(result.zones[0].__continuations()[4].__on) ==
+ std::chrono::weekday_last{std::chrono::Wednesday});
+ assert(result.zones[0].__continuations()[4].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[0].__continuations()[4].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[0].__continuations()[5].__year == std::chrono::year(-42));
+ assert(result.zones[0].__continuations()[5].__in_month == std::chrono::June);
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.zones[0].__continuations()[5].__on);
+ assert(r.__weekday == std::chrono::Monday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
+ assert(r.__day == std::chrono::day(1));
+ assert(result.zones[0].__continuations()[5].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[0].__continuations()[5].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[0].__continuations()[6].__year == std::chrono::year(42));
+ assert(result.zones[0].__continuations()[6].__in_month == std::chrono::July);
+ r = std::get<std::chrono::__tz::__constrained_weekday>(result.zones[0].__continuations()[6].__on);
+ assert(r.__weekday == std::chrono::Sunday);
+ assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
+ assert(r.__day == std::chrono::day(12));
+ assert(result.zones[0].__continuations()[6].__at.__time == std::chrono::seconds(0));
+ assert(result.zones[0].__continuations()[6].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[0].__continuations()[7].__year == std::chrono::year(42));
+ assert(result.zones[0].__continuations()[7].__in_month == std::chrono::July);
+ assert(std::get<std::chrono::day>(result.zones[0].__continuations()[7].__on) == std::chrono::day(1));
+ assert(result.zones[0].__continuations()[7].__at.__time == std::chrono::hours(2));
+ assert(result.zones[0].__continuations()[7].__at.__clock == std::chrono::__tz::__clock::__local);
+
+ assert(result.zones[0].__continuations()[8].__year == std::chrono::year(42));
+ assert(result.zones[0].__continuations()[8].__in_month == std::chrono::July);
+ assert(std::get<std::chrono::day>(result.zones[0].__continuations()[8].__on) == std::chrono::day(1));
+ assert(result.zones[0].__continuations()[8].__at.__time ==
+ std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
+ assert(result.zones[0].__continuations()[8].__at.__clock == std::chrono::__tz::__clock::__universal);
+
+ assert(result.zones[0].__continuations()[9].__year == std::chrono::year(42));
+ assert(result.zones[0].__continuations()[9].__in_month == std::chrono::July);
+ assert(std::get<std::chrono::day>(result.zones[0].__continuations()[9].__on) == std::chrono::day(1));
+ assert(result.zones[0].__continuations()[9].__at.__time ==
+ std::chrono::hours(0)); // The man page expresses it in hours
+ assert(result.zones[0].__continuations()[9].__at.__clock == std::chrono::__tz::__clock::__local);
+}
+
+int main(int, const char**) {
+ test_invalid();
+ test_name();
+ test_stdoff();
+ test_rules();
+ test_format();
+ test_until();
+
+ test_continuation();
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index d4c22023db7a0..fd7a63e6c3485 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -127,6 +127,8 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
+chrono variant
+chrono vector
chrono version
cinttypes cstdint
cmath limits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 5eb839a3aa7a6..d1f5fd1c4e9d7 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -127,6 +127,8 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
+chrono variant
+chrono vector
chrono version
cinttypes cstdint
cmath limits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 7b372fb37dcd5..8061a66a9f50f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -127,6 +127,8 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
+chrono variant
+chrono vector
chrono version
cinttypes cstdint
cmath limits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 7b372fb37dcd5..8061a66a9f50f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -127,6 +127,8 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
+chrono variant
+chrono vector
chrono version
cinttypes cstdint
cmath limits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 9b4915a468d1c..1bf66f6599fcf 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -133,6 +133,8 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
+chrono variant
+chrono vector
chrono version
cinttypes cstdint
cmath limits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 7c7099d176f18..7bc6494251ea1 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -87,6 +87,8 @@ chrono stdexcept
chrono string
chrono string_view
chrono tuple
+chrono variant
+chrono vector
chrono version
cinttypes cstdint
cmath limits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 7c7099d176f18..7bc6494251ea1 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -87,6 +87,8 @@ chrono stdexcept
chrono string
chrono string_view
chrono tuple
+chrono variant
+chrono vector
chrono version
cinttypes cstdint
cmath limits
diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb.pass.cpp
index 4fec102b66aa2..3414f1b4855ba 100644
--- a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb.pass.cpp
+++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb.pass.cpp
@@ -19,9 +19,9 @@
// const tzdb& get_tzdb();
-#include <chrono>
-
+#include <algorithm>
#include <cassert>
+#include <chrono>
#include "test_macros.h"
@@ -30,5 +30,15 @@ int main(int, const char**) {
assert(!db.version.empty());
+ LIBCPP_ASSERT(!db.__rules.empty());
+
+ assert(!db.zones.empty());
+ assert(std::ranges::is_sorted(db.zones));
+ assert(std::ranges::adjacent_find(db.zones) == db.zones.end()); // is unique?
+
+ assert(!db.links.empty());
+ assert(std::ranges::is_sorted(db.links));
+ assert(std::ranges::adjacent_find(db.links) == db.links.end()); // is unique?
+
return 0;
}
diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.tzdb/tzdb.members.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.tzdb/tzdb.members.pass.cpp
index 66d939f125eab..703898fead512 100644
--- a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.tzdb/tzdb.members.pass.cpp
+++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.tzdb/tzdb.members.pass.cpp
@@ -37,7 +37,14 @@ int main(int, const char**) {
tzdb.version = "version";
assert(tzdb.version == "version");
- // TODO TZDB add the other data members
+ // TODO TZDB update the tests above to use this style.
+ { [[maybe_unused]] std::same_as<std::string> auto _ = tzdb.version = "version"; }
+ { [[maybe_unused]] std::same_as<std::vector<std::chrono::time_zone>> auto& _ = tzdb.zones; }
+ tzdb.zones.clear(); // is it a non-const member?
+ { [[maybe_unused]] std::same_as<std::vector<std::chrono::time_zone_link>> auto& _ = tzdb.links; }
+ tzdb.links.clear(); // is it a non-const member?
+
+ // TODO TZDB add the leap data member
return 0;
}
diff --git a/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/name.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/name.pass.cpp
new file mode 100644
index 0000000000000..18f75dadc682b
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/name.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// class time_zone_link;
+
+// string_view name() const noexcept;
+
+#include <cassert>
+#include <chrono>
+
+#include "test_macros.h"
+
+int main(int, const char**) {
+ const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
+ assert(tzdb.links.size() > 1);
+
+ [[maybe_unused]] std::same_as<std::string_view> auto _ = tzdb.links[0].name();
+ static_assert(noexcept(tzdb.links[0].name()));
+ assert(tzdb.links[0].name() != tzdb.links[1].name());
+
+ return 0;
+}
diff --git a/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/target.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/target.pass.cpp
new file mode 100644
index 0000000000000..2f8b5b9421d63
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/target.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// class time_zone_link;
+
+// string_view target() const noexcept;
+
+#include <cassert>
+#include <chrono>
+
+#include "test_macros.h"
+
+int main(int, const char**) {
+ const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
+ assert(tzdb.links.size() > 1);
+
+ [[maybe_unused]] std::same_as<std::string_view> auto _ = tzdb.links[0].target();
+ static_assert(noexcept(tzdb.links[0].target()));
+
+ return 0;
+}
diff --git a/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.nonmembers/comparison.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.nonmembers/comparison.pass.cpp
new file mode 100644
index 0000000000000..944818c1ad0c1
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.nonmembers/comparison.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// bool operator==(const time_zone_link& x, const time_zone_link& y) noexcept;
+// strong_ordering operator<=>(const time_zone_link& x, const time_zone_link& y) noexcept;
+
+#include <chrono>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_comparisons.h"
+
+int main(int, const char**) {
+ const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
+ assert(tzdb.links.size() > 2);
+
+ AssertOrderAreNoexcept<std::chrono::time_zone_link>();
+ AssertOrderReturn<std::strong_ordering, std::chrono::time_zone_link>();
+
+ assert(testOrder(tzdb.links[0], tzdb.links[1], std::strong_ordering::less));
+
+ return 0;
+}
diff --git a/libcxx/test/std/time/time.zone/time.zone.link/types.compile.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.link/types.compile.pass.cpp
new file mode 100644
index 0000000000000..7a5abd76a54b4
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.link/types.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// class time_zone_link
+// {
+// time_zone_link(time_zone_link&&) = default;
+// time_zone_link& operator=(time_zone_link&&) = default;
+//
+// ...
+// };
+
+#include <chrono>
+#include <concepts>
+#include <type_traits>
+
+static_assert(std::move_constructible<std::chrono::time_zone_link>);
+static_assert(std::is_move_assignable_v<std::chrono::time_zone_link>);
diff --git a/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/name.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/name.pass.cpp
new file mode 100644
index 0000000000000..d1ff2fe683108
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/name.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// class time_zone;
+
+// string_view name() const noexcept;
+
+#include <cassert>
+#include <chrono>
+
+#include "test_macros.h"
+
+int main(int, const char**) {
+ const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
+ assert(tzdb.zones.size() > 1);
+
+ [[maybe_unused]] std::same_as<std::string_view> auto _ = tzdb.zones[0].name();
+ static_assert(noexcept(tzdb.zones[0].name()));
+ assert(tzdb.zones[0].name() != tzdb.zones[1].name());
+
+ return 0;
+}
diff --git a/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.nonmembers/comparison.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.nonmembers/comparison.pass.cpp
new file mode 100644
index 0000000000000..7c680707bc518
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.nonmembers/comparison.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// bool operator==(const time_zone& x, const time_zone& y) noexcept;
+// strong_ordering operator<=>(const time_zone& x, const time_zone& y) noexcept;
+
+#include <chrono>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_comparisons.h"
+
+int main(int, const char**) {
+ const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
+ assert(tzdb.zones.size() > 2);
+
+ AssertOrderAreNoexcept<std::chrono::time_zone>();
+ AssertOrderReturn<std::strong_ordering, std::chrono::time_zone>();
+
+ assert(testOrder(tzdb.zones[0], tzdb.zones[1], std::strong_ordering::less));
+
+ return 0;
+}
diff --git a/libcxx/test/std/time/time.zone/time.zone.timezone/types.compile.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.timezone/types.compile.pass.cpp
new file mode 100644
index 0000000000000..23dc7e68d276d
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.timezone/types.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// class time_zone
+// {
+// time_zone(time_zone&&) = default;
+// time_zone& operator=(time_zone&&) = default;
+//
+// ...
+// };
+
+#include <chrono>
+#include <concepts>
+#include <type_traits>
+
+static_assert(std::move_constructible<std::chrono::time_zone>);
+static_assert(std::is_move_assignable_v<std::chrono::time_zone>);
>From 96dc1013392d25cdace93a615df7d29c8f6e086a Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 4 Feb 2024 12:42:03 +0100
Subject: [PATCH 2/2] Address review comments.
---
libcxx/docs/Status/Cxx20Papers.csv | 2 +-
libcxx/include/CMakeLists.txt | 1 -
libcxx/include/__chrono/time_zone.h | 27 +++--
libcxx/include/__chrono/time_zone_link.h | 7 +-
libcxx/include/__chrono/tzdb.h | 10 +-
libcxx/include/__chrono/tzdb_list.h | 10 +-
libcxx/include/chrono | 1 -
libcxx/include/module.modulemap.in | 4 -
libcxx/src/CMakeLists.txt | 5 +
libcxx/src/include/tzdb/time_zone.h | 48 ++++++++
.../include/tzdb/types.h} | 64 +++++------
libcxx/src/include/tzdb/tz.h | 28 +++++
libcxx/src/include/tzdb/tzdb_list.h | 104 ++++++++++++++++++
libcxx/src/time_zone.cpp | 30 +++++
libcxx/src/tz.cpp | 75 +++++++------
libcxx/src/tzdb_list.cpp | 74 +------------
libcxx/src/tzdb_zone.cpp | 0
.../time.zone/time.zone.db/rules.pass.cpp | 12 +-
.../time.zone/time.zone.db/zones.pass.cpp | 8 +-
.../test/libcxx/transitive_includes/cxx03.csv | 5 +-
.../test/libcxx/transitive_includes/cxx11.csv | 1 -
.../test/libcxx/transitive_includes/cxx14.csv | 1 -
.../test/libcxx/transitive_includes/cxx17.csv | 1 -
.../test/libcxx/transitive_includes/cxx20.csv | 1 -
.../test/libcxx/transitive_includes/cxx23.csv | 1 -
.../test/libcxx/transitive_includes/cxx26.csv | 1 -
26 files changed, 343 insertions(+), 178 deletions(-)
create mode 100644 libcxx/src/include/tzdb/time_zone.h
rename libcxx/{include/__chrono/time_zone_types.h => src/include/tzdb/types.h} (60%)
create mode 100644 libcxx/src/include/tzdb/tz.h
create mode 100644 libcxx/src/include/tzdb/tzdb_list.h
create mode 100644 libcxx/src/time_zone.cpp
create mode 100644 libcxx/src/tzdb_zone.cpp
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 314b73d6704e1..db6491419a5cc 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -180,7 +180,7 @@
"`P1973R1 <https://wg21.link/P1973R1>`__","LWG","Rename ""_default_init"" Functions, Rev1","Prague","|Complete|","16.0"
"`P1976R2 <https://wg21.link/P1976R2>`__","LWG","Fixed-size span construction from dynamic range","Prague","|Complete|","11.0","|ranges|"
"`P1981R0 <https://wg21.link/P1981R0>`__","LWG","Rename leap to leap_second","Prague","* *",""
-"`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","|Complete|","18.0","|chrono|"
+"`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","|Complete|","19.0","|chrono|"
"`P1983R0 <https://wg21.link/P1983R0>`__","LWG","Wording for GB301, US296, US292, US291, and US283","Prague","|Complete|","15.0","|ranges|"
"`P1994R1 <https://wg21.link/P1994R1>`__","LWG","elements_view needs its own sentinel","Prague","|Complete|","16.0","|ranges|"
"`P2002R1 <https://wg21.link/P2002R1>`__","CWG","Defaulted comparison specification cleanups","Prague","* *",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 73863699715a9..925fc84e7412d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -292,7 +292,6 @@ set(files
__chrono/time_point.h
__chrono/time_zone.h
__chrono/time_zone_link.h
- __chrono/time_zone_types.h
__chrono/tzdb.h
__chrono/tzdb_list.h
__chrono/weekday.h
diff --git a/libcxx/include/__chrono/time_zone.h b/libcxx/include/__chrono/time_zone.h
index b54c58485aef3..20c7758db5d87 100644
--- a/libcxx/include/__chrono/time_zone.h
+++ b/libcxx/include/__chrono/time_zone.h
@@ -16,17 +16,18 @@
// Enable the contents of the header only when libc++ was built with experimental features enabled.
#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
-# include <__chrono/time_zone_types.h>
# include <__compare/strong_order.h>
# include <__config>
-# include <string>
+# include <__memory/unique_ptr.h>
# include <string_view>
-# include <vector>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
# endif
+_LIBCPP_PUSH_MACROS
+# include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
@@ -36,21 +37,17 @@ namespace chrono {
class _LIBCPP_AVAILABILITY_TZDB time_zone {
public:
- explicit _LIBCPP_HIDE_FROM_ABI time_zone(string&& __name) : __name_(std::move(__name)) {}
+ class __impl; // public so it can be used by make_unique.
+ _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI explicit time_zone(unique_ptr<__impl>&& __p);
+ _LIBCPP_EXPORTED_FROM_ABI ~time_zone();
- _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name_; }
+ time_zone(time_zone&&) = default;
+ time_zone& operator=(time_zone&&) = default;
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI vector<__tz::__continuation>& __continuations() { return __continuations_; }
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const vector<__tz::__continuation>& __continuations() const {
- return __continuations_;
- }
+ _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI string_view name() const noexcept;
private:
- string __name_;
- // Note the first line has a name + __continuation, the other lines
- // are just __continuations. So there is always at least one item in
- // the vector.
- vector<__tz::__continuation> __continuations_;
+ unique_ptr<__impl> __impl_;
};
_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline bool
@@ -70,6 +67,8 @@ operator<=>(const time_zone& __x, const time_zone& __y) noexcept {
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
+
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
#endif // _LIBCPP___CHRONO_TIME_ZONE_H
diff --git a/libcxx/include/__chrono/time_zone_link.h b/libcxx/include/__chrono/time_zone_link.h
index a188298a8af21..462cf04c3968f 100644
--- a/libcxx/include/__chrono/time_zone_link.h
+++ b/libcxx/include/__chrono/time_zone_link.h
@@ -25,6 +25,9 @@
# pragma GCC system_header
# endif
+_LIBCPP_PUSH_MACROS
+# include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
@@ -32,7 +35,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono {
-class _LIBCPP_AVAILABILITY_TZDB time_zone_link {
+class time_zone_link {
public:
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI explicit time_zone_link(string_view __name, string_view __target)
: __name_{__name}, __target_{__target} {}
@@ -67,6 +70,8 @@ operator<=>(const time_zone_link& __x, const time_zone_link& __y) noexcept {
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
+
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
#endif // _LIBCPP___CHRONO_TIME_ZONE_LINK_H
diff --git a/libcxx/include/__chrono/tzdb.h b/libcxx/include/__chrono/tzdb.h
index fc230c550327d..582172e5df9dd 100644
--- a/libcxx/include/__chrono/tzdb.h
+++ b/libcxx/include/__chrono/tzdb.h
@@ -16,10 +16,8 @@
// Enable the contents of the header only when libc++ was built with experimental features enabled.
#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
-# include <__availability>
# include <__chrono/time_zone.h>
# include <__chrono/time_zone_link.h>
-# include <__chrono/time_zone_types.h>
# include <__config>
# include <string>
# include <vector>
@@ -28,6 +26,9 @@
# pragma GCC system_header
# endif
+_LIBCPP_PUSH_MACROS
+# include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
@@ -35,9 +36,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono {
-struct _LIBCPP_AVAILABILITY_TZDB tzdb {
+struct tzdb {
string version;
- vector<pair<string, vector<__tz::__rule>>> __rules;
vector<time_zone> zones;
vector<time_zone_link> links;
};
@@ -49,6 +49,8 @@ struct _LIBCPP_AVAILABILITY_TZDB tzdb {
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
+
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
#endif // _LIBCPP___CHRONO_TZDB_H
diff --git a/libcxx/include/__chrono/tzdb_list.h b/libcxx/include/__chrono/tzdb_list.h
index 0494826c01a33..174648e61dec4 100644
--- a/libcxx/include/__chrono/tzdb_list.h
+++ b/libcxx/include/__chrono/tzdb_list.h
@@ -19,7 +19,7 @@
# include <__availability>
# include <__chrono/tzdb.h>
# include <forward_list>
-# include <string_view>
+# include <string>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -34,7 +34,8 @@ namespace chrono {
class _LIBCPP_AVAILABILITY_TZDB tzdb_list {
public:
- _LIBCPP_EXPORTED_FROM_ABI explicit tzdb_list(tzdb&& __tzdb);
+ class __impl; // public to allow construction in dylib
+ _LIBCPP_EXPORTED_FROM_ABI explicit tzdb_list(__impl* __p);
_LIBCPP_EXPORTED_FROM_ABI ~tzdb_list();
tzdb_list(const tzdb_list&) = delete;
@@ -46,16 +47,15 @@ class _LIBCPP_AVAILABILITY_TZDB tzdb_list {
_LIBCPP_EXPORTED_FROM_ABI const_iterator erase_after(const_iterator __p);
- _LIBCPP_EXPORTED_FROM_ABI tzdb& __emplace_front(tzdb&& __tzdb);
-
_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator begin() const noexcept;
_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator end() const noexcept;
_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator cbegin() const noexcept;
_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator cend() const noexcept;
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI __impl& __implementation();
+
private:
- class __impl;
__impl* __impl_;
};
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 036fbac9d6028..4fda1bb10b862 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -865,7 +865,6 @@ constexpr chrono::year operator ""y(unsigned lo
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <__chrono/time_zone.h>
# include <__chrono/time_zone_link.h>
-# include <__chrono/time_zone_types.h>
# include <__chrono/tzdb.h>
# include <__chrono/tzdb_list.h>
#endif
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 42bdac096fbb7..c53cbaae41168 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1163,10 +1163,6 @@ module std_private_chrono_time_zone_link [system] {
header "__chrono/time_zone_link.h"
export *
}
-module std_private_chrono_time_zone_types [system] {
- header "__chrono/time_zone_types.h"
- export *
-}
module std_private_chrono_system_clock [system] {
header "__chrono/system_clock.h"
export std_private_chrono_time_point
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 44a088663463c..29c12f87f821a 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -336,6 +336,11 @@ endif()
if (LIBCXX_ENABLE_LOCALIZATION AND LIBCXX_ENABLE_FILESYSTEM AND LIBCXX_ENABLE_TIME_ZONE_DATABASE)
list(APPEND LIBCXX_EXPERIMENTAL_SOURCES
+ include/tzdb/time_zone.h
+ include/tzdb/types.h
+ include/tzdb/tz.h
+ include/tzdb/tzdb_list.h
+ time_zone.cpp
tz.cpp
tzdb_list.cpp
)
diff --git a/libcxx/src/include/tzdb/time_zone.h b/libcxx/src/include/tzdb/time_zone.h
new file mode 100644
index 0000000000000..79ca6a6bc28c9
--- /dev/null
+++ b/libcxx/src/include/tzdb/time_zone.h
@@ -0,0 +1,48 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+
+#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_H
+#define _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_H
+
+#include <chrono>
+#include <string>
+#include <vector>
+
+#include "types.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+class time_zone::__impl {
+public:
+ explicit _LIBCPP_HIDE_FROM_ABI __impl(string&& __name) : __name_(std::move(__name)) {}
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name_; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI vector<__tz::__continuation>& __continuations() { return __continuations_; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const vector<__tz::__continuation>& __continuations() const {
+ return __continuations_;
+ }
+
+private:
+ string __name_;
+ // Note the first line has a name + __continuation, the other lines
+ // are just __continuations. So there is always at least one item in
+ // the vector.
+ vector<__tz::__continuation> __continuations_;
+};
+
+} // namespace chrono
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_H
diff --git a/libcxx/include/__chrono/time_zone_types.h b/libcxx/src/include/tzdb/types.h
similarity index 60%
rename from libcxx/include/__chrono/time_zone_types.h
rename to libcxx/src/include/tzdb/types.h
index e8d7b7ddf8465..1389b992de218 100644
--- a/libcxx/include/__chrono/time_zone_types.h
+++ b/libcxx/src/include/tzdb/types.h
@@ -9,36 +9,30 @@
// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
-#ifndef _LIBCPP___CHRONO_TIME_ZONE_TYPES_H
-#define _LIBCPP___CHRONO_TIME_ZONE_TYPES_H
-
-#include <version>
-// Enable the contents of the header only when libc++ was built with experimental features enabled.
-#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
-
-# include <__chrono/day.h>
-# include <__chrono/duration.h>
-# include <__chrono/month.h>
-# include <__chrono/weekday.h>
-# include <__chrono/year.h>
-# include <__config>
-# include <string>
-# include <variant>
-
-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-# endif
+#ifndef __LIBCPP_SRC_INCLUDE_TZDB_TYPES_H
+#define __LIBCPP_SRC_INCLUDE_TZDB_TYPES_H
+
+#include <chrono>
+#include <string>
+#include <utility>
+#include <variant>
+#include <vector>
_LIBCPP_BEGIN_NAMESPACE_STD
-# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
- !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+// TODO TZDB
+// The helper classes in this header have no constructor but are loaded with
+// dedicated parse functions. In the original design this header was public and
+// the parsing is done in the dylib. In that design having constructors would
+// expand the ABI interface. Since this header is now in the dylib that design
+// should be reconsidered. (For now the design is kept as is, in case this
+// header needs to be public for unforseen reasons.)
namespace chrono::__tz {
// Sun>=8 first Sunday on or after the eighth
// Sun<=25 last Sunday on or before the 25th
-struct _LIBCPP_AVAILABILITY_TZDB __constrained_weekday {
+struct __constrained_weekday {
/* year_month_day operator()(year __year, month __month);*/ // needed but not implemented
weekday __weekday;
@@ -54,30 +48,35 @@ struct _LIBCPP_AVAILABILITY_TZDB __constrained_weekday {
// Sun<=25 last Sunday on or before the 25th
using __on = variant<day, weekday_last, __constrained_weekday>;
-enum class _LIBCPP_AVAILABILITY_TZDB __clock { __local, __standard, __universal };
+enum class __clock { __local, __standard, __universal };
-struct _LIBCPP_AVAILABILITY_TZDB __at {
+struct __at {
seconds __time{0};
__tz::__clock __clock{__tz::__clock::__local};
};
-struct _LIBCPP_AVAILABILITY_TZDB __save {
+struct __save {
seconds __time;
bool __is_dst;
};
// The names of the fields match the fields of a Rule.
-struct _LIBCPP_AVAILABILITY_TZDB __rule {
+struct __rule {
year __from;
year __to;
- month __in_month; // __in is a reserved name
+ month __in;
__tz::__on __on;
__tz::__at __at;
__tz::__save __save;
string __letters;
};
-struct _LIBCPP_AVAILABILITY_TZDB __continuation {
+using __rules_storage_type = std::vector<std::pair<string, vector<__tz::__rule>>>; // TODO TZDB use flat_map;
+
+struct __continuation {
+ // Non-owning link to the RULE entries.
+ __tz::__rules_storage_type* __rule_database_;
+
seconds __stdoff;
// The RULES is either a SAVE or a NAME.
@@ -95,18 +94,13 @@ struct _LIBCPP_AVAILABILITY_TZDB __continuation {
// Parts of the UNTIL, the optional parts are default initialized
// optional<year> __until_;
year __year = chrono::year::min();
- month __in_month{January}; // __in is a reserved name
+ month __in{January};
__tz::__on __on{chrono::day{1}};
__tz::__at __at{chrono::seconds{0}, __tz::__clock::__local};
};
} // namespace chrono::__tz
-# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM)
- // && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
-
_LIBCPP_END_NAMESPACE_STD
-#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
-
-#endif // _LIBCPP___CHRONO_TIME_ZONE_TYPES_H
+#endif // __LIBCPP_SRC_INCLUDE_TZDB_TYPES_H
diff --git a/libcxx/src/include/tzdb/tz.h b/libcxx/src/include/tzdb/tz.h
new file mode 100644
index 0000000000000..2d2c9b410200c
--- /dev/null
+++ b/libcxx/src/include/tzdb/tz.h
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+
+#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TZ_H
+#define _LIBCPP_SRC_INCLUDE_TZDB_TZ_H
+
+#include <chrono>
+
+#include "types.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules);
+
+} // namespace chrono
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_SRC_INCLUDE_TZDB_TZ_H
diff --git a/libcxx/src/include/tzdb/tzdb_list.h b/libcxx/src/include/tzdb/tzdb_list.h
new file mode 100644
index 0000000000000..47e07b0e71877
--- /dev/null
+++ b/libcxx/src/include/tzdb/tzdb_list.h
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+
+#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_H
+#define _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_H
+
+#include <__mutex/unique_lock.h>
+#include <forward_list>
+
+// When threads are not available the locking is not required.
+#ifndef _LIBCPP_HAS_NO_THREADS
+# include <shared_mutex>
+#endif
+
+#include "types.h"
+#include "tz.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+//===----------------------------------------------------------------------===//
+// Private API
+//===----------------------------------------------------------------------===//
+
+// The tzdb_list stores a list of "tzdb" enatries.
+//
+// The public tzdb database does not store the RULE entries of the IANA
+// database. These entries are considered an implementation detail. Since most
+// of the tzdb_list interface is exposed as "a list of tzdb entries" it's not
+// possible to use a helper struct that stores a tzdb and the RULE database.
+// Instead this class stores these in parallel forward lists.
+//
+// Since the nodes of a forward_list are stable it's possible to store pointers
+// and references to these nodes.
+class tzdb_list::__impl {
+public:
+ __impl() { __load_no_lock(); }
+
+ [[nodiscard]] const tzdb& __load() {
+#ifndef _LIBCPP_HAS_NO_THREADS
+ unique_lock __lock{__mutex_};
+#endif
+ __load_no_lock();
+ return __tzdb_.front();
+ }
+
+ using const_iterator = tzdb_list::const_iterator;
+
+ const tzdb& front() const noexcept {
+#ifndef _LIBCPP_HAS_NO_THREADS
+ shared_lock __lock{__mutex_};
+#endif
+ return __tzdb_.front();
+ }
+
+ const_iterator erase_after(const_iterator __p) {
+#ifndef _LIBCPP_HAS_NO_THREADS
+ unique_lock __lock{__mutex_};
+#endif
+
+ __rules_.erase_after(std::next(__rules_.cbegin(), std::distance(__tzdb_.cbegin(), __p)));
+ return __tzdb_.erase_after(__p);
+ }
+
+ const_iterator begin() const noexcept {
+#ifndef _LIBCPP_HAS_NO_THREADS
+ shared_lock __lock{__mutex_};
+#endif
+ return __tzdb_.begin();
+ }
+ const_iterator end() const noexcept {
+ // forward_list<T>::end does not access the list, so no need to take a lock.
+ return __tzdb_.end();
+ }
+
+ const_iterator cbegin() const noexcept { return begin(); }
+ const_iterator cend() const noexcept { return end(); }
+
+private:
+ // Loads the tzdbs
+ // pre: The caller ensures the locking, if needed, is done.
+ void __load_no_lock() { chrono::__init_tzdb(__tzdb_.emplace_front(), __rules_.emplace_front()); }
+
+#ifndef _LIBCPP_HAS_NO_THREADS
+ mutable shared_mutex __mutex_;
+#endif
+ forward_list<tzdb> __tzdb_;
+
+ forward_list<__tz::__rules_storage_type> __rules_;
+};
+
+} // namespace chrono
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_H
diff --git a/libcxx/src/time_zone.cpp b/libcxx/src/time_zone.cpp
new file mode 100644
index 0000000000000..01b343afaf4b1
--- /dev/null
+++ b/libcxx/src/time_zone.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+
+#include <chrono>
+
+#include "include/tzdb/time_zone.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI time_zone::time_zone(unique_ptr<time_zone::__impl>&& __p)
+ : __impl_(std::move(__p)) {
+ _LIBCPP_ASSERT_NON_NULL(__impl_ != nullptr, "initialized time_zone without a valid pimpl object");
+}
+
+_LIBCPP_EXPORTED_FROM_ABI time_zone::~time_zone() = default;
+
+_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI string_view time_zone::name() const noexcept { return __impl_->name(); }
+
+} // namespace chrono
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/tz.cpp b/libcxx/src/tz.cpp
index 472cac136dd77..9132cd4922d1b 100644
--- a/libcxx/src/tz.cpp
+++ b/libcxx/src/tz.cpp
@@ -15,6 +15,11 @@
#include <stdexcept>
#include <string>
+#include "include/tzdb/time_zone.h"
+#include "include/tzdb/types.h"
+#include "include/tzdb/tz.h"
+#include "include/tzdb/tzdb_list.h"
+
// Contains a parser for the IANA time zone data files.
//
// These files can be found at https://data.iana.org/time-zones/ and are in the
@@ -183,7 +188,7 @@ static void __matches(istream& __input, string_view __expected) {
return Saturday;
case 'u':
- chrono::__skip(__input, "unday");
+ chrono::__skip(__input, "nday");
return Sunday;
}
break;
@@ -281,10 +286,15 @@ static void __matches(istream& __input, string_view __expected) {
if (__negative) [[unlikely]]
__input.get();
- static_assert(year::min() == -year::max());
- int __result = std::min<int64_t>(__parse_integral(__input, true), static_cast<int>(year::max()));
+ int64_t __result = __parse_integral(__input, true);
+ if (__result > static_cast<int>(year::max())) {
+ if (__negative)
+ std::__throw_runtime_error("corrupt tzdb year: year is less than the minimum");
+
+ std::__throw_runtime_error("corrupt tzdb year: year is greater than the minimum");
+ }
- return year{__negative ? -__result : __result};
+ return year{static_cast<int>(__negative ? -__result : __result)};
}
[[nodiscard]] static year __parse_year(istream& __input) {
@@ -299,7 +309,7 @@ static void __matches(istream& __input, string_view __expected) {
[[fallthrough]];
case ' ':
- // The m is minimum, even when that is ambigious.
+ // The m is minimum, even when that is ambiguous.
return year::min();
case 'a':
@@ -423,6 +433,7 @@ static void __matches(istream& __input, string_view __expected) {
[[nodiscard]] static string __parse_letters(istream& __input) {
string __result = __parse_string(__input);
+ // Canonicalize "-" to "" since they are equivalent in the specification.
return __result != "-" ? __result : "";
}
@@ -443,9 +454,11 @@ static void __matches(istream& __input, string_view __expected) {
return chrono::__parse_string(__input);
}
-[[nodiscard]] static __tz::__continuation __parse_continuation(istream& __input) {
+[[nodiscard]] static __tz::__continuation __parse_continuation(__tz::__rules_storage_type& __rules, istream& __input) {
__tz::__continuation __result;
+ __result.__rule_database_ = std::addressof(__rules);
+
// Note STDOFF is specified as
// This field has the same format as the AT and SAVE fields of rule lines;
// These fields have different suffix letters, these letters seem
@@ -465,7 +478,7 @@ static void __matches(istream& __input, string_view __expected) {
if (chrono::__is_eol(__input.peek()))
return __result;
- __result.__in_month = chrono::__parse_month(__input);
+ __result.__in = chrono::__parse_month(__input);
chrono::__skip_optional_whitespace(__input);
if (chrono::__is_eol(__input.peek()))
@@ -497,14 +510,14 @@ static string __parse_version(istream& __input) {
return chrono::__parse_string(__input);
}
-static void __parse_rule(tzdb& __tzdb, istream& __input) {
+static void __parse_rule(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) {
chrono::__skip_mandatory_whitespace(__input);
string __name = chrono::__parse_string(__input);
- if (__tzdb.__rules.empty() || __tzdb.__rules.back().first != __name)
- __tzdb.__rules.emplace_back(__name, vector<__tz::__rule>{});
+ if (__rules.empty() || __rules.back().first != __name)
+ __rules.emplace_back(__name, vector<__tz::__rule>{});
- __tz::__rule& __rule = __tzdb.__rules.back().second.emplace_back();
+ __tz::__rule& __rule = __rules.back().second.emplace_back();
chrono::__skip_mandatory_whitespace(__input);
__rule.__from = chrono::__parse_year(__input);
@@ -513,7 +526,7 @@ static void __parse_rule(tzdb& __tzdb, istream& __input) {
chrono::__skip_mandatory_whitespace(__input);
chrono::__matches(__input, '-');
chrono::__skip_mandatory_whitespace(__input);
- __rule.__in_month = chrono::__parse_month(__input);
+ __rule.__in = chrono::__parse_month(__input);
chrono::__skip_mandatory_whitespace(__input);
__rule.__on = chrono::__parse_on(__input);
chrono::__skip_mandatory_whitespace(__input);
@@ -525,18 +538,20 @@ static void __parse_rule(tzdb& __tzdb, istream& __input) {
chrono::__skip_line(__input);
}
-static void __parse_zone(tzdb& __tzdb, istream& __input) {
+static void __parse_zone(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) {
chrono::__skip_mandatory_whitespace(__input);
- vector<__tz::__continuation>& __continuations =
- __tzdb.zones.emplace_back(chrono::__parse_string(__input)).__continuations();
+ auto __p = std::make_unique<time_zone::__impl>(chrono::__parse_string(__input));
+ vector<__tz::__continuation>& __continuations = __p->__continuations();
chrono::__skip_mandatory_whitespace(__input);
do {
// The first line must be valid, continuations are optional.
- __continuations.emplace_back(__parse_continuation(__input));
+ __continuations.emplace_back(__parse_continuation(__rules, __input));
chrono::__skip_line(__input);
chrono::__skip_optional_whitespace(__input);
} while (std::isdigit(__input.peek()) || __input.peek() == '-');
+
+ __tzdb.zones.emplace_back(std::move(__p));
}
static void __parse_link(tzdb& __tzdb, istream& __input) {
@@ -549,7 +564,7 @@ static void __parse_link(tzdb& __tzdb, istream& __input) {
__tzdb.links.emplace_back(std::move(__name), std::move(__target));
}
-static void __parse_tzdata(tzdb& __db, istream& __input) {
+static void __parse_tzdata(tzdb& __db, __tz::__rules_storage_type& __rules, istream& __input) {
while (true) {
int __c = std::tolower(__input.get());
@@ -568,16 +583,16 @@ static void __parse_tzdata(tzdb& __db, istream& __input) {
case 'r':
chrono::__skip(__input, "ule");
- chrono::__parse_rule(__db, __input);
+ chrono::__parse_rule(__db, __rules, __input);
break;
case 'z':
chrono::__skip(__input, "one");
- chrono::__parse_zone(__db, __input);
+ chrono::__parse_zone(__db, __rules, __input);
break;
case 'l':
- chrono::__skip(__input, "link");
+ chrono::__skip(__input, "ink");
chrono::__parse_link(__db, __input);
break;
@@ -587,19 +602,15 @@ static void __parse_tzdata(tzdb& __db, istream& __input) {
}
}
-static tzdb __make_tzdb() {
- tzdb __result;
-
+void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) {
filesystem::path __root = chrono::__libcpp_tzdb_directory();
ifstream __tzdata{__root / "tzdata.zi"};
- __result.version = chrono::__parse_version(__tzdata);
- chrono::__parse_tzdata(__result, __tzdata);
- std::ranges::sort(__result.__rules, {}, [](const auto& p) { return p.first; });
- std::ranges::sort(__result.zones);
- std::ranges::sort(__result.links);
-
- return __result;
+ __tzdb.version = chrono::__parse_version(__tzdata);
+ chrono::__parse_tzdata(__tzdb, __rules, __tzdata);
+ std::ranges::sort(__tzdb.zones);
+ std::ranges::sort(__tzdb.links);
+ std::ranges::sort(__rules, {}, [](const auto& p) { return p.first; });
}
//===----------------------------------------------------------------------===//
@@ -607,7 +618,7 @@ static tzdb __make_tzdb() {
//===----------------------------------------------------------------------===//
_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list() {
- static tzdb_list __result{chrono::__make_tzdb()};
+ static tzdb_list __result{new tzdb_list::__impl()};
return __result;
}
@@ -615,7 +626,7 @@ _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const tzdb& reload_tzdb() {
if (chrono::remote_version() == chrono::get_tzdb().version)
return chrono::get_tzdb();
- return chrono::get_tzdb_list().__emplace_front(chrono::__make_tzdb());
+ return chrono::get_tzdb_list().__implementation().__load();
}
_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version() {
diff --git a/libcxx/src/tzdb_list.cpp b/libcxx/src/tzdb_list.cpp
index 7eaaedc6eda79..6cfc405c27aaa 100644
--- a/libcxx/src/tzdb_list.cpp
+++ b/libcxx/src/tzdb_list.cpp
@@ -10,75 +10,15 @@
#include <chrono>
-#include <__mutex/unique_lock.h>
-#include <forward_list>
-
-// When threads are not available the locking is not required.
-#ifndef _LIBCPP_HAS_NO_THREADS
-# include <shared_mutex>
-#endif
+#include "include/tzdb/tzdb_list.h"
_LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono {
-//===----------------------------------------------------------------------===//
-// Private API
-//===----------------------------------------------------------------------===//
-
-class tzdb_list::__impl {
-public:
- explicit __impl(tzdb&& __tzdb) { __tzdb_.push_front(std::move(__tzdb)); }
-
- using const_iterator = tzdb_list::const_iterator;
-
- const tzdb& front() const noexcept {
-#ifndef _LIBCPP_HAS_NO_THREADS
- shared_lock __lock{__mutex_};
-#endif
- return __tzdb_.front();
- }
-
- const_iterator erase_after(const_iterator __p) {
-#ifndef _LIBCPP_HAS_NO_THREADS
- unique_lock __lock{__mutex_};
-#endif
- return __tzdb_.erase_after(__p);
- }
-
- tzdb& __emplace_front(tzdb&& __tzdb) {
-#ifndef _LIBCPP_HAS_NO_THREADS
- unique_lock __lock{__mutex_};
-#endif
- return __tzdb_.emplace_front(std::move(__tzdb));
- }
-
- const_iterator begin() const noexcept {
-#ifndef _LIBCPP_HAS_NO_THREADS
- shared_lock __lock{__mutex_};
-#endif
- return __tzdb_.begin();
- }
- const_iterator end() const noexcept {
- // forward_list<T>::end does not access the list, so no need to take a lock.
- return __tzdb_.end();
- }
-
- const_iterator cbegin() const noexcept { return begin(); }
- const_iterator cend() const noexcept { return end(); }
-
-private:
-#ifndef _LIBCPP_HAS_NO_THREADS
- mutable shared_mutex __mutex_;
-#endif
- forward_list<tzdb> __tzdb_;
-};
-
-//===----------------------------------------------------------------------===//
-// Public API
-//===----------------------------------------------------------------------===//
-
-_LIBCPP_EXPORTED_FROM_ABI tzdb_list::tzdb_list(tzdb&& __tzdb) : __impl_{new __impl(std::move(__tzdb))} {}
+_LIBCPP_EXPORTED_FROM_ABI tzdb_list::tzdb_list(tzdb_list::__impl* __p) : __impl_(__p) {
+ _LIBCPP_ASSERT_NON_NULL(__impl_ != nullptr, "initialized time_zone without a valid pimpl object");
+}
_LIBCPP_EXPORTED_FROM_ABI tzdb_list::~tzdb_list() { delete __impl_; }
@@ -90,10 +30,6 @@ _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::erase_after(const
return __impl_->erase_after(__p);
}
-_LIBCPP_EXPORTED_FROM_ABI tzdb& tzdb_list::__emplace_front(tzdb&& __tzdb) {
- return __impl_->__emplace_front(std::move(__tzdb));
-}
-
_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::begin() const noexcept {
return __impl_->begin();
}
@@ -108,6 +44,8 @@ _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_l
return __impl_->cend();
}
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI tzdb_list::__impl& tzdb_list::__implementation() { return *__impl_; }
+
} // namespace chrono
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/tzdb_zone.cpp b/libcxx/src/tzdb_zone.cpp
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp
index 98fb2b02f6b3f..cb73cfe48fbdf 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp
@@ -17,6 +17,8 @@
// Tests the IANA database rules parsing and operations.
// This is not part of the public tzdb interface.
+// TODO TZDB Enable the disabled parts of the tests once we can test them using the public interface.
+
#include <chrono>
#include <fstream>
#include <string>
@@ -44,12 +46,12 @@ void write(std::string_view input) {
f << "# version " << version++ << '\n';
f.write(input.data(), input.size());
}
-
+#if 0
static const std::chrono::tzdb& parse(std::string_view input) {
write(input);
return std::chrono::reload_tzdb();
}
-
+#endif
static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) {
write(input);
@@ -73,6 +75,7 @@ static void test_invalid() {
test_exception("R r x", "corrupt tzdb: expected a digit");
test_exception("R r +", "corrupt tzdb: expected a digit");
test_exception("R r mx", "corrupt tzdb year: expected 'min' or 'max'");
+ test_exception("R r -32768", "corrupt tzdb year: year is less than the minimum");
test_exception("R r mix", "corrupt tzdb: expected whitespace");
test_exception("R r 0", "corrupt tzdb: expected whitespace");
@@ -120,6 +123,7 @@ static void test_invalid() {
test_exception("R r 0 1 - Ja Su>=31 1w 2s ", "corrupt tzdb: expected a string");
}
+#if 0
static void test_name() {
const std::chrono::tzdb& result = parse(
R"(
@@ -549,9 +553,10 @@ R a 0 1 - Ja Su>=31 1w 2s abc
assert(result.__rules[0].second[1].__letters == "a");
assert(result.__rules[0].second[2].__letters == "abc");
}
-
+#endif
int main(int, const char**) {
test_invalid();
+#if 0
test_name();
test_from();
test_to();
@@ -560,6 +565,7 @@ int main(int, const char**) {
test_at();
test_save();
test_letter();
+#endif
return 0;
}
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.db/zones.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.db/zones.pass.cpp
index c37ccc4528d2d..3b50559179c1b 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.db/zones.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/zones.pass.cpp
@@ -17,6 +17,8 @@
// Tests the IANA database zones parsing and operations.
// This is not part of the public tzdb interface.
+// TODO TZDB Enable the disabled parts of the tests once we can test them using the public interface.
+
#include <cassert>
#include <chrono>
#include <fstream>
@@ -92,7 +94,7 @@ Z n 0 r f
assert(result.zones.size() == 1);
assert(result.zones[0].name() == "n");
}
-
+#if 0
static void test_stdoff() {
const std::chrono::tzdb& result = parse(
R"(
@@ -356,16 +358,18 @@ Z na 0 r f
std::chrono::hours(0)); // The man page expresses it in hours
assert(result.zones[0].__continuations()[9].__at.__clock == std::chrono::__tz::__clock::__local);
}
+#endif
int main(int, const char**) {
test_invalid();
test_name();
+#if 0
test_stdoff();
test_rules();
test_format();
test_until();
test_continuation();
-
+#endif
return 0;
}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index fd7a63e6c3485..59c37fc369b85 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -127,7 +127,6 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
-chrono variant
chrono vector
chrono version
cinttypes cstdint
@@ -835,13 +834,17 @@ system_error type_traits
system_error version
thread array
thread atomic
+thread cctype
thread cerrno
thread chrono
+thread clocale
thread compare
thread cstddef
thread cstdint
+thread cstdlib
thread cstring
thread ctime
+thread cwchar
thread functional
thread iosfwd
thread limits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index d1f5fd1c4e9d7..60381e1d99458 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -127,7 +127,6 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
-chrono variant
chrono vector
chrono version
cinttypes cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 8061a66a9f50f..3feee83875206 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -127,7 +127,6 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
-chrono variant
chrono vector
chrono version
cinttypes cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 8061a66a9f50f..3feee83875206 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -127,7 +127,6 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
-chrono variant
chrono vector
chrono version
cinttypes cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 1bf66f6599fcf..25822c942a979 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -133,7 +133,6 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
-chrono variant
chrono vector
chrono version
cinttypes cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 7bc6494251ea1..c759934b1e978 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -87,7 +87,6 @@ chrono stdexcept
chrono string
chrono string_view
chrono tuple
-chrono variant
chrono vector
chrono version
cinttypes cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 7bc6494251ea1..c759934b1e978 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -87,7 +87,6 @@ chrono stdexcept
chrono string
chrono string_view
chrono tuple
-chrono variant
chrono vector
chrono version
cinttypes cstdint
More information about the libcxx-commits
mailing list