[libcxx-commits] [libcxx] [libc++][chrono] Loads tzdata.zi in tzdb. (PR #74928)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Fri Feb 16 08:53:26 PST 2024


https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/74928

>From 965058b93814276cdca3c031d30ec19a40127a58 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/3] [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                 |   2 +
 libcxx/include/__chrono/time_zone.h           |  86 +++
 libcxx/include/__chrono/time_zone_link.h      |  79 +++
 libcxx/include/__chrono/tzdb.h                |  13 +-
 libcxx/include/__chrono/tzdb_list.h           |  16 +-
 libcxx/include/chrono                         |  39 +-
 libcxx/include/libcxx.imp                     |   2 +
 libcxx/include/module.modulemap.in            |   6 +
 libcxx/modules/std/chrono.inc                 |   4 +
 libcxx/src/CMakeLists.txt                     |   8 +-
 .../src/include/tzdb/time_zone_link_private.h |  27 +
 libcxx/src/include/tzdb/time_zone_private.h   |  48 ++
 libcxx/src/include/tzdb/types_private.h       | 106 +++
 libcxx/src/include/tzdb/tzdb_list_private.h   | 104 +++
 libcxx/src/include/tzdb/tzdb_private.h        |  28 +
 libcxx/src/time_zone.cpp                      |  44 ++
 libcxx/src/tz.cpp                             | 146 ----
 libcxx/src/tzdb.cpp                           | 641 ++++++++++++++++++
 libcxx/src/tzdb_list.cpp                      |  74 +-
 ...rono.nodiscard_extensions.compile.pass.cpp |  17 +
 .../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     | 380 +++++++++++
 .../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 +
 .../time.zone.db.access/get_tzdb.pass.cpp     |  14 +-
 .../time.zone.db.tzdb/tzdb.members.pass.cpp   |   8 +-
 .../time.zone.link.members/name.pass.cpp      |  38 ++
 .../time.zone.link.members/target.pass.cpp    |  37 +
 .../comparison.pass.cpp                       |  39 ++
 .../time.zone.link/types.compile.pass.cpp     |  34 +
 .../time.zone.members/name.pass.cpp           |  38 ++
 .../time.zone.nonmembers/comparison.pass.cpp  |  39 ++
 .../time.zone.timezone/types.compile.pass.cpp |  34 +
 42 files changed, 2619 insertions(+), 235 deletions(-)
 create mode 100644 libcxx/include/__chrono/time_zone.h
 create mode 100644 libcxx/include/__chrono/time_zone_link.h
 create mode 100644 libcxx/src/include/tzdb/time_zone_link_private.h
 create mode 100644 libcxx/src/include/tzdb/time_zone_private.h
 create mode 100644 libcxx/src/include/tzdb/types_private.h
 create mode 100644 libcxx/src/include/tzdb/tzdb_list_private.h
 create mode 100644 libcxx/src/include/tzdb/tzdb_private.h
 create mode 100644 libcxx/src/time_zone.cpp
 delete mode 100644 libcxx/src/tz.cpp
 create mode 100644 libcxx/src/tzdb.cpp
 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 d73088687975c2..db6491419a5cc3 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|","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/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv
index aaa9278a50c1e7..c8221078e9a8df 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 6ded426640f0d0..925fc84e7412d0 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -290,6 +290,8 @@ set(files
   __chrono/steady_clock.h
   __chrono/system_clock.h
   __chrono/time_point.h
+  __chrono/time_zone.h
+  __chrono/time_zone_link.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 00000000000000..42e0bd1c85e8d1
--- /dev/null
+++ b/libcxx/include/__chrono/time_zone.h
@@ -0,0 +1,86 @@
+// -*- 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 <__compare/strong_order.h>
+#  include <__config>
+#  include <__memory/unique_ptr.h>
+#  include <string_view>
+
+#  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) &&   \
+      !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+namespace chrono {
+
+class _LIBCPP_AVAILABILITY_TZDB time_zone {
+  _LIBCPP_HIDE_FROM_ABI time_zone() = default;
+
+public:
+  class __impl; // public so it can be used by make_unique.
+
+  // The "constructor".
+  //
+  // The default constructor is private to avoid the constructor from being an
+  // ABI interface. Instead use an __ugly_named function as an ABI interface,
+  // since users should not depend on __ugly_names.
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI static time_zone __create(unique_ptr<__impl>&& __p);
+
+  _LIBCPP_EXPORTED_FROM_ABI ~time_zone();
+
+  _LIBCPP_HIDE_FROM_ABI time_zone(time_zone&&)            = default;
+  _LIBCPP_HIDE_FROM_ABI time_zone& operator=(time_zone&&) = default;
+
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name(); }
+
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const __impl& __implementation() const noexcept;
+
+  // private:
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view __name() const noexcept;
+  unique_ptr<__impl> __impl_;
+};
+
+_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
+
+_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
new file mode 100644
index 00000000000000..884bc635fa858b
--- /dev/null
+++ b/libcxx/include/__chrono/time_zone_link.h
@@ -0,0 +1,79 @@
+// -*- 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_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) &&   \
+      !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+namespace chrono {
+
+class time_zone_link {
+public:
+  struct __constructor_tag;
+  _LIBCPP_NODISCARD_EXT
+  _LIBCPP_HIDE_FROM_ABI explicit time_zone_link(string_view __name, string_view __target, __constructor_tag&&)
+      : __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
+
+_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 bd7b05d478e503..582172e5df9ddb 100644
--- a/libcxx/include/__chrono/tzdb.h
+++ b/libcxx/include/__chrono/tzdb.h
@@ -16,12 +16,19 @@
 // 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.h>
+#  include <__chrono/time_zone_link.h>
+#  include <__config>
 #  include <string>
+#  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) &&   \
@@ -29,8 +36,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace chrono {
 
-struct _LIBCPP_AVAILABILITY_TZDB tzdb {
+struct tzdb {
   string version;
+  vector<time_zone> zones;
+  vector<time_zone_link> links;
 };
 
 } // namespace chrono
@@ -40,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 0494826c01a339..320a6a3285132e 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
@@ -32,9 +32,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace chrono {
 
+// TODO TZDB
+// Libc++ recently switched to only export __ugly_names from the dylib.
+// Since the library is still experimental the functions in this header
+// should be adapted to this new style. The other tzdb headers should be
+// evaluated too.
+
 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 +53,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 f8407419c95444..fe73f7c772b996 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,8 @@ 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/tzdb.h>
 #  include <__chrono/tzdb_list.h>
 #endif
diff --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp
index 69de4705f37886..ee4b4e29fb7509 100644
--- a/libcxx/include/libcxx.imp
+++ b/libcxx/include/libcxx.imp
@@ -287,6 +287,8 @@
   { include: [ "<__chrono/steady_clock.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/system_clock.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/time_point.h>", "private", "<chrono>", "public" ] },
+  { include: [ "<__chrono/time_zone.h>", "private", "<chrono>", "public" ] },
+  { include: [ "<__chrono/time_zone_link.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/tzdb.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/tzdb_list.h>", "private", "<chrono>", "public" ] },
   { include: [ "<__chrono/weekday.h>", "private", "<chrono>", "public" ] },
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 9828c48e0587bb..d4b6847a288405 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1155,6 +1155,12 @@ 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"
+}
+module std_private_chrono_time_zone_link         [system] {
+  header "__chrono/time_zone_link.h"
+}
 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 65dc973936c47f..8ebe8d26065dcd 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/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 44a088663463c9..cc6954a7bac37e 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -336,7 +336,13 @@ endif()
 
 if (LIBCXX_ENABLE_LOCALIZATION AND LIBCXX_ENABLE_FILESYSTEM AND LIBCXX_ENABLE_TIME_ZONE_DATABASE)
   list(APPEND LIBCXX_EXPERIMENTAL_SOURCES
-    tz.cpp
+    include/tzdb/time_zone_link_private.h
+    include/tzdb/time_zone_private.h
+    include/tzdb/types_private.h
+    include/tzdb/tzdb_list_private.h
+    include/tzdb/tzdb_private.h
+    time_zone.cpp
+    tzdb.cpp
     tzdb_list.cpp
     )
 endif()
diff --git a/libcxx/src/include/tzdb/time_zone_link_private.h b/libcxx/src/include/tzdb/time_zone_link_private.h
new file mode 100644
index 00000000000000..139237625274d3
--- /dev/null
+++ b/libcxx/src/include/tzdb/time_zone_link_private.h
@@ -0,0 +1,27 @@
+// -*- 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_LINK_PRIVATE_H
+#define _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_LINK_PRIVATE_H
+
+#include <chrono>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+struct time_zone_link::__constructor_tag {};
+
+} // namespace chrono
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_LINK_PRIVATE_H
diff --git a/libcxx/src/include/tzdb/time_zone_private.h b/libcxx/src/include/tzdb/time_zone_private.h
new file mode 100644
index 00000000000000..29bd485e2d2637
--- /dev/null
+++ b/libcxx/src/include/tzdb/time_zone_private.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_PRIVATE_H
+#define _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_PRIVATE_H
+
+#include <chrono>
+#include <string>
+#include <vector>
+
+#include "types_private.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_PRIVATE_H
diff --git a/libcxx/src/include/tzdb/types_private.h b/libcxx/src/include/tzdb/types_private.h
new file mode 100644
index 00000000000000..4604b9fc88114d
--- /dev/null
+++ b/libcxx/src/include/tzdb/types_private.h
@@ -0,0 +1,106 @@
+// -*- 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_TYPES_PRIVATE_H
+#define __LIBCPP_SRC_INCLUDE_TZDB_TYPES_PRIVATE_H
+
+#include <chrono>
+#include <string>
+#include <utility>
+#include <variant>
+#include <vector>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// 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 was 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 __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 __clock { __local, __standard, __universal };
+
+struct __at {
+  seconds __time{0};
+  __tz::__clock __clock{__tz::__clock::__local};
+};
+
+struct __save {
+  seconds __time;
+  bool __is_dst;
+};
+
+// The names of the fields match the fields of a Rule.
+struct __rule {
+  year __from;
+  year __to;
+  month __in;
+  __tz::__on __on;
+  __tz::__at __at;
+  __tz::__save __save;
+  string __letters;
+};
+
+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.
+  // 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 the until field can 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{January};
+  __tz::__on __on{chrono::day{1}};
+  __tz::__at __at{chrono::seconds{0}, __tz::__clock::__local};
+};
+
+} // namespace chrono::__tz
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // __LIBCPP_SRC_INCLUDE_TZDB_TYPES_PRIVATE_H
diff --git a/libcxx/src/include/tzdb/tzdb_list_private.h b/libcxx/src/include/tzdb/tzdb_list_private.h
new file mode 100644
index 00000000000000..f43d7d8ea772be
--- /dev/null
+++ b/libcxx/src/include/tzdb/tzdb_list_private.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_PRIVATE_H
+#define _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_PRIVATE_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_private.h"
+#include "tzdb_private.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+//===----------------------------------------------------------------------===//
+//                          Private API
+//===----------------------------------------------------------------------===//
+
+// The tzdb_list stores a list of "tzdb" entries.
+//
+// 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_PRIVATE_H
diff --git a/libcxx/src/include/tzdb/tzdb_private.h b/libcxx/src/include/tzdb/tzdb_private.h
new file mode 100644
index 00000000000000..8ec3f890ef65c8
--- /dev/null
+++ b/libcxx/src/include/tzdb/tzdb_private.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_PRIVATE_H
+#define _LIBCPP_SRC_INCLUDE_TZDB_TZ_PRIVATE_H
+
+#include <chrono>
+
+#include "types_private.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_PRIVATE_H
diff --git a/libcxx/src/time_zone.cpp b/libcxx/src/time_zone.cpp
new file mode 100644
index 00000000000000..a05f10a24c8f48
--- /dev/null
+++ b/libcxx/src/time_zone.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_private.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+#if 0
+_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");
+}
+#endif
+
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI time_zone time_zone::__create(unique_ptr<time_zone::__impl>&& __p) {
+  //   : __impl_(std::move(__p)) {
+  _LIBCPP_ASSERT_NON_NULL(__p != nullptr, "initialized time_zone without a valid pimpl object");
+  time_zone result;
+  result.__impl_ = std::move(__p);
+  return result;
+}
+
+_LIBCPP_EXPORTED_FROM_ABI time_zone::~time_zone() = default;
+
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view time_zone::__name() const noexcept { return __impl_->name(); }
+
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const time_zone::__impl& time_zone::__implementation() const noexcept {
+  return *__impl_;
+}
+
+} // namespace chrono
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/tz.cpp b/libcxx/src/tz.cpp
deleted file mode 100644
index 4425f0e6b91bd4..00000000000000
--- a/libcxx/src/tz.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 <filesystem>
-#include <fstream>
-#include <stdexcept>
-#include <string>
-
-// 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
-// public domain. Information regarding the input can be found at
-// https://data.iana.org/time-zones/tz-how-to.html and
-// https://man7.org/linux/man-pages/man8/zic.8.html.
-//
-// As indicated at https://howardhinnant.github.io/date/tz.html#Installation
-// For Windows another file seems to be required
-// https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml
-// This file seems to contain the mapping of Windows time zone name to IANA
-// time zone names.
-//
-// However this article mentions another way to do the mapping on Windows
-// https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255
-// This requires Windows 10 Version 1903, which was released in May of 2019
-// and considered end of life in December 2020
-// https://learn.microsoft.com/en-us/lifecycle/announcements/windows-10-1903-end-of-servicing
-//
-// TODO TZDB Implement the Windows mapping in tzdb::current_zone
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-namespace chrono {
-
-// This function is weak so it can be overriden in the tests. The
-// declaration is in the test header test/support/test_tzdb.h
-_LIBCPP_WEAK string_view __libcpp_tzdb_directory() {
-#if defined(__linux__)
-  return "/usr/share/zoneinfo/";
-#else
-#  error "unknown path to the IANA Time Zone Database"
-#endif
-}
-
-[[nodiscard]] static bool __is_whitespace(int __c) { return __c == ' ' || __c == '\t'; }
-
-static void __skip_optional_whitespace(istream& __input) {
-  while (chrono::__is_whitespace(__input.peek()))
-    __input.get();
-}
-
-static void __skip_mandatory_whitespace(istream& __input) {
-  if (!chrono::__is_whitespace(__input.get()))
-    std::__throw_runtime_error("corrupt tzdb: expected whitespace");
-
-  chrono::__skip_optional_whitespace(__input);
-}
-
-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());
-}
-
-static void __matches(istream& __input, string_view __expected) {
-  for (auto __c : __expected)
-    if (std::tolower(__input.get()) != __c)
-      std::__throw_runtime_error((string("corrupt tzdb: expected string '") + string(__expected) + '\'').c_str());
-}
-
-[[nodiscard]] static string __parse_string(istream& __input) {
-  string __result;
-  while (true) {
-    int __c = __input.get();
-    switch (__c) {
-    case ' ':
-    case '\t':
-    case '\n':
-      __input.unget();
-      [[fallthrough]];
-    case istream::traits_type::eof():
-      if (__result.empty())
-        std::__throw_runtime_error("corrupt tzdb: expected a string");
-
-      return __result;
-
-    default:
-      __result.push_back(__c);
-    }
-  }
-}
-
-static string __parse_version(istream& __input) {
-  // The first line in tzdata.zi contains
-  //    # version YYYYw
-  // The parser expects this pattern
-  // #\s*version\s*\(.*)
-  // This part is not documented.
-  chrono::__matches(__input, '#');
-  chrono::__skip_optional_whitespace(__input);
-  chrono::__matches(__input, "version");
-  chrono::__skip_mandatory_whitespace(__input);
-  return chrono::__parse_string(__input);
-}
-
-static tzdb __make_tzdb() {
-  tzdb __result;
-
-  filesystem::path __root = chrono::__libcpp_tzdb_directory();
-  ifstream __tzdata{__root / "tzdata.zi"};
-
-  __result.version = chrono::__parse_version(__tzdata);
-  return __result;
-}
-
-//===----------------------------------------------------------------------===//
-//                           Public API
-//===----------------------------------------------------------------------===//
-
-_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list() {
-  static tzdb_list __result{chrono::__make_tzdb()};
-  return __result;
-}
-
-_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());
-}
-
-_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version() {
-  filesystem::path __root = chrono::__libcpp_tzdb_directory();
-  ifstream __tzdata{__root / "tzdata.zi"};
-  return chrono::__parse_version(__tzdata);
-}
-
-} // namespace chrono
-
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/tzdb.cpp b/libcxx/src/tzdb.cpp
new file mode 100644
index 00000000000000..a0e7f177dac052
--- /dev/null
+++ b/libcxx/src/tzdb.cpp
@@ -0,0 +1,641 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <algorithm>
+#include <chrono>
+#include <filesystem>
+#include <fstream>
+#include <stdexcept>
+#include <string>
+
+#include "include/tzdb/time_zone_link_private.h"
+#include "include/tzdb/time_zone_private.h"
+#include "include/tzdb/types_private.h"
+#include "include/tzdb/tzdb_list_private.h"
+#include "include/tzdb/tzdb_private.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
+// public domain. Information regarding the input can be found at
+// https://data.iana.org/time-zones/tz-how-to.html and
+// https://man7.org/linux/man-pages/man8/zic.8.html.
+//
+// As indicated at https://howardhinnant.github.io/date/tz.html#Installation
+// For Windows another file seems to be required
+// https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml
+// This file seems to contain the mapping of Windows time zone name to IANA
+// time zone names.
+//
+// However this article mentions another way to do the mapping on Windows
+// https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255
+// This requires Windows 10 Version 1903, which was released in May of 2019
+// and considered end of life in December 2020
+// https://learn.microsoft.com/en-us/lifecycle/announcements/windows-10-1903-end-of-servicing
+//
+// TODO TZDB Implement the Windows mapping in tzdb::current_zone
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+// This function is weak so it can be overriden in the tests. The
+// declaration is in the test header test/support/test_tzdb.h
+_LIBCPP_WEAK string_view __libcpp_tzdb_directory() {
+#if defined(__linux__)
+  return "/usr/share/zoneinfo/";
+#else
+#  error "unknown path to the IANA Time Zone Database"
+#endif
+}
+
+//===----------------------------------------------------------------------===//
+//                           Details
+//===----------------------------------------------------------------------===//
+
+[[nodiscard]] static bool __is_whitespace(int __c) { return __c == ' ' || __c == '\t'; }
+
+static void __skip_optional_whitespace(istream& __input) {
+  while (chrono::__is_whitespace(__input.peek()))
+    __input.get();
+}
+
+static void __skip_mandatory_whitespace(istream& __input) {
+  if (!chrono::__is_whitespace(__input.get()))
+    std::__throw_runtime_error("corrupt tzdb: expected whitespace");
+
+  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());
+}
+
+static void __matches(istream& __input, string_view __expected) {
+  for (auto __c : __expected)
+    if (std::tolower(__input.get()) != __c)
+      std::__throw_runtime_error((string("corrupt tzdb: expected string '") + string(__expected) + '\'').c_str());
+}
+
+[[nodiscard]] static string __parse_string(istream& __input) {
+  string __result;
+  while (true) {
+    int __c = __input.get();
+    switch (__c) {
+    case ' ':
+    case '\t':
+    case '\n':
+      __input.unget();
+      [[fallthrough]];
+    case istream::traits_type::eof():
+      if (__result.empty())
+        std::__throw_runtime_error("corrupt tzdb: expected a string");
+
+      return __result;
+
+    default:
+      __result.push_back(__c);
+    }
+  }
+}
+
+[[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, "nday");
+      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();
+
+  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 maximum");
+  }
+
+  return year{static_cast<int>(__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 ambiguous.
+    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);
+  // Canonicalize "-" to "" since they are equivalent in the specification.
+  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(__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
+  // 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 = 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
+  // The parser expects this pattern
+  // #\s*version\s*\(.*)
+  // This part is not documented.
+  chrono::__matches(__input, '#');
+  chrono::__skip_optional_whitespace(__input);
+  chrono::__matches(__input, "version");
+  chrono::__skip_mandatory_whitespace(__input);
+  return chrono::__parse_string(__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 (__rules.empty() || __rules.back().first != __name)
+    __rules.emplace_back(__name, vector<__tz::__rule>{});
+
+  __tz::__rule& __rule = __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 = 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, __tz::__rules_storage_type& __rules, istream& __input) {
+  chrono::__skip_mandatory_whitespace(__input);
+  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(__rules, __input));
+    chrono::__skip_line(__input);
+    chrono::__skip_optional_whitespace(__input);
+  } while (std::isdigit(__input.peek()) || __input.peek() == '-');
+
+  __tzdb.zones.emplace_back(time_zone::__create(std::move(__p)));
+}
+
+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), time_zone_link::__constructor_tag{});
+}
+
+static void __parse_tzdata(tzdb& __db, __tz::__rules_storage_type& __rules, 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, __rules, __input);
+      break;
+
+    case 'z':
+      chrono::__skip(__input, "one");
+      chrono::__parse_zone(__db, __rules, __input);
+      break;
+
+    case 'l':
+      chrono::__skip(__input, "ink");
+      chrono::__parse_link(__db, __input);
+      break;
+
+    default:
+      std::__throw_runtime_error("corrupt tzdb: unexpected input");
+    }
+  }
+}
+
+void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) {
+  filesystem::path __root = chrono::__libcpp_tzdb_directory();
+  ifstream __tzdata{__root / "tzdata.zi"};
+
+  __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; });
+}
+
+//===----------------------------------------------------------------------===//
+//                           Public API
+//===----------------------------------------------------------------------===//
+
+_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list() {
+  static tzdb_list __result{new tzdb_list::__impl()};
+  return __result;
+}
+
+_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().__implementation().__load();
+}
+
+_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version() {
+  filesystem::path __root = chrono::__libcpp_tzdb_directory();
+  ifstream __tzdata{__root / "tzdata.zi"};
+  return chrono::__parse_version(__tzdata);
+}
+
+} // namespace chrono
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/tzdb_list.cpp b/libcxx/src/tzdb_list.cpp
index 7eaaedc6eda79e..7c521199884443 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_private.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/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp
index 1f30af06fedd87..ec09e4d082cd38 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp
@@ -23,6 +23,10 @@
 
 #include "test_macros.h"
 
+// These types have "private" constructors.
+extern  std::chrono::time_zone tz;
+extern  std::chrono::time_zone_link link;
+
 void test() {
   std::chrono::tzdb_list& list = std::chrono::get_tzdb_list();
   list.front();
@@ -34,4 +38,17 @@ void test() {
   std::chrono::get_tzdb_list();
   std::chrono::get_tzdb();
   std::chrono::remote_version();
+
+  {
+    tz.name();
+    operator==(tz, tz);
+    operator<=>(tz, tz);
+  }
+
+  {
+    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 ab68b1fb44f96e..4ad9b7105f6926 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
@@ -20,6 +20,10 @@
 
 #include "test_macros.h"
 
+// These types have "private" constructors.
+extern  std::chrono::time_zone tz;
+extern  std::chrono::time_zone_link link;
+
 void test() {
   std::chrono::tzdb_list& list = std::chrono::get_tzdb_list();
   list.front();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -32,4 +36,19 @@ 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}}
+
+  {
+    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}}
+  }
+
+  {
+    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 00000000000000..92d761d46bccef
--- /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 00000000000000..5ae2ed1e91ebe5
--- /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.
+// The test uses private implementation headers.
+// ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../../src/include
+
+#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"
+
+// headers in the dylib
+#include "tzdb/types_private.h"
+#include "tzdb/tzdb_private.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;
+}
+
+static void write(std::string_view input) {
+  static int version = 0;
+
+  std::ofstream f{file};
+  f << "# version " << version++ << '\n';
+  f.write(input.data(), input.size());
+}
+
+struct parse_result {
+  explicit parse_result(std::string_view input) {
+    write(input);
+    std::chrono::tzdb tzdb; // result not needed for the tests.
+    std::chrono::__init_tzdb(tzdb, rules);
+  }
+  std::chrono::__tz::__rules_storage_type rules;
+};
+
+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 -32768", "corrupt tzdb year: year is less than the minimum");
+  test_exception("R r 32768", "corrupt tzdb year: year is greater than the maximum");
+
+  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() {
+  parse_result result{
+      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() {
+  parse_result result{
+      R"(
+# min abbreviations
+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 -
+
+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 -
+)"};
+
+  assert(result.rules.size() == 1);
+  assert(result.rules[0].second.size() == 10);
+
+  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(-1000));
+  assert(result.rules[0].second[6].__from == std::chrono::year(-100));
+  assert(result.rules[0].second[7].__from == std::chrono::year(0));
+  assert(result.rules[0].second[8].__from == std::chrono::year(100));
+  assert(result.rules[0].second[9].__from == std::chrono::year(1000));
+}
+
+static void test_to() {
+  parse_result result{
+      R"(
+# min abbreviations
+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 -
+
+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 -
+
+# 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() == 14);
+
+  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(-1000));
+  assert(result.rules[0].second[6].__to == std::chrono::year(-100));
+  assert(result.rules[0].second[7].__to == std::chrono::year(0));
+  assert(result.rules[0].second[8].__to == std::chrono::year(100));
+  assert(result.rules[0].second[9].__to == std::chrono::year(1000));
+
+  assert(result.rules[0].second[10].__to == std::chrono::year::min());
+  assert(result.rules[0].second[11].__to == std::chrono::year::max());
+  assert(result.rules[0].second[12].__to == std::chrono::year(-100));
+  assert(result.rules[0].second[13].__to == std::chrono::year(100));
+}
+
+static void test_in() {
+  parse_result result{
+      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 == std::chrono::April);
+    assert(result.rules[i].second[1].__in == std::chrono::August);
+    assert(result.rules[i].second[2].__in == std::chrono::December);
+    assert(result.rules[i].second[3].__in == std::chrono::February);
+    assert(result.rules[i].second[4].__in == std::chrono::January);
+    assert(result.rules[i].second[5].__in == std::chrono::July);
+    assert(result.rules[i].second[6].__in == std::chrono::June);
+    assert(result.rules[i].second[7].__in == std::chrono::May);
+    assert(result.rules[i].second[8].__in == std::chrono::March);
+    assert(result.rules[i].second[9].__in == std::chrono::November);
+    assert(result.rules[i].second[10].__in == std::chrono::October);
+    assert(result.rules[i].second[11].__in == std::chrono::September);
+  }
+};
+
+static void test_on_day() {
+  parse_result result{
+      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() {
+  parse_result result{
+      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() {
+  parse_result result{
+      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() {
+  parse_result result{
+      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() {
+  parse_result result{
+      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() {
+  parse_result result{
+      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 00000000000000..8571e0d05ebbd6
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/zones.pass.cpp
@@ -0,0 +1,380 @@
+//===----------------------------------------------------------------------===//
+//
+// 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.
+// The test uses private implementation headers.
+// ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../../src/include
+
+#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"
+
+// headers in the dylib
+#include "tzdb/types_private.h"
+#include "tzdb/tzdb_private.h"
+#include "tzdb/time_zone_private.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;
+}
+
+static 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 const std::vector<std::chrono::__tz::__continuation>& continuations(const std::chrono::time_zone& time_zone) {
+  return time_zone.__implementation().__continuations();
+}
+
+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(continuations(result.zones[0]).size() == 1);
+
+  assert(continuations(result.zones[0])[0].__stdoff == std::chrono::hours(2));
+  assert(continuations(result.zones[1])[0].__stdoff == std::chrono::hours(2));
+  assert(continuations(result.zones[2])[0].__stdoff ==
+         std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
+  assert(continuations(result.zones[3])[0].__stdoff == std::chrono::minutes(19) + std::chrono::seconds(32));
+  assert(continuations(result.zones[4])[0].__stdoff == std::chrono::hours(12));
+  assert(continuations(result.zones[5])[0].__stdoff == std::chrono::hours(15));
+  assert(continuations(result.zones[6])[0].__stdoff == std::chrono::hours(24));
+  assert(continuations(result.zones[7])[0].__stdoff == std::chrono::hours(260));
+  assert(continuations(result.zones[8])[0].__stdoff == -(std::chrono::hours(2) + std::chrono::minutes(30)));
+  assert(continuations(result.zones[9])[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(continuations(result.zones[0]).size() == 1);
+
+  assert(std::holds_alternative<std::monostate>(continuations(result.zones[0])[0].__rules));
+  assert(std::get<std::string>(continuations(result.zones[1])[0].__rules) == "r");
+
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[2])[0].__rules).__time ==
+         std::chrono::hours(2));
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[2])[0].__rules).__is_dst == true);
+
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[3])[0].__rules).__time ==
+         std::chrono::hours(2));
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[3])[0].__rules).__is_dst == false);
+
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[4])[0].__rules).__time ==
+         std::chrono::hours(0));
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[4])[0].__rules).__is_dst == false);
+
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[5])[0].__rules).__time ==
+         std::chrono::seconds(1));
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[5])[0].__rules).__is_dst == true);
+
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[6])[0].__rules).__time ==
+         -std::chrono::seconds(1));
+  assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[6])[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(continuations(result.zones[0]).size() == 1);
+  assert(continuations(result.zones[0])[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(continuations(result.zones[0]).size() == 1);
+
+  std::chrono::__tz::__constrained_weekday r;
+
+  assert(continuations(result.zones[0])[0].__year == std::chrono::year::min());
+  assert(continuations(result.zones[0])[0].__in == std::chrono::January);
+  assert(std::get<std::chrono::day>(continuations(result.zones[0])[0].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[0])[0].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[0])[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[1])[0].__year == std::chrono::year(1000));
+  assert(continuations(result.zones[1])[0].__in == std::chrono::January);
+  assert(std::get<std::chrono::day>(continuations(result.zones[1])[0].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[1])[0].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[1])[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[2])[0].__year == std::chrono::year(-1000));
+  assert(continuations(result.zones[2])[0].__in == std::chrono::November);
+  assert(std::get<std::chrono::day>(continuations(result.zones[2])[0].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[2])[0].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[2])[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[3])[0].__year == std::chrono::year::max());
+  assert(continuations(result.zones[3])[0].__in == std::chrono::September);
+  assert(std::get<std::chrono::day>(continuations(result.zones[3])[0].__on) == std::chrono::day(31));
+  assert(continuations(result.zones[3])[0].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[3])[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[4])[0].__year == std::chrono::year(0));
+  assert(continuations(result.zones[4])[0].__in == std::chrono::January);
+  assert(std::get<std::chrono::weekday_last>(continuations(result.zones[4])[0].__on) ==
+         std::chrono::weekday_last{std::chrono::Wednesday});
+  assert(continuations(result.zones[4])[0].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[4])[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[5])[0].__year == std::chrono::year(-42));
+  assert(continuations(result.zones[5])[0].__in == std::chrono::June);
+  r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[5])[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(continuations(result.zones[5])[0].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[5])[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[6])[0].__year == std::chrono::year(42));
+  assert(continuations(result.zones[6])[0].__in == std::chrono::July);
+  r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[6])[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(continuations(result.zones[6])[0].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[6])[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[7])[0].__year == std::chrono::year(42));
+  assert(continuations(result.zones[7])[0].__in == std::chrono::July);
+  assert(std::get<std::chrono::day>(continuations(result.zones[7])[0].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[7])[0].__at.__time == std::chrono::hours(2));
+  assert(continuations(result.zones[7])[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[8])[0].__year == std::chrono::year(42));
+  assert(continuations(result.zones[8])[0].__in == std::chrono::July);
+  assert(std::get<std::chrono::day>(continuations(result.zones[8])[0].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[8])[0].__at.__time ==
+         std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
+  assert(continuations(result.zones[8])[0].__at.__clock == std::chrono::__tz::__clock::__universal);
+
+  assert(continuations(result.zones[9])[0].__year == std::chrono::year(42));
+  assert(continuations(result.zones[9])[0].__in == std::chrono::July);
+  assert(std::get<std::chrono::day>(continuations(result.zones[9])[0].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[9])[0].__at.__time == std::chrono::hours(0)); // The man page expresses it in hours
+  assert(continuations(result.zones[9])[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(continuations(result.zones[0]).size() == 10);
+
+  std::chrono::__tz::__constrained_weekday r;
+
+  assert(continuations(result.zones[0])[0].__year == std::chrono::year::min());
+  assert(continuations(result.zones[0])[0].__in == std::chrono::January);
+  assert(std::get<std::chrono::day>(continuations(result.zones[0])[0].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[0])[0].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[0])[0].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[0])[1].__year == std::chrono::year(1000));
+  assert(continuations(result.zones[0])[1].__in == std::chrono::January);
+  assert(std::get<std::chrono::day>(continuations(result.zones[0])[1].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[0])[1].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[0])[1].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[0])[2].__year == std::chrono::year(-1000));
+  assert(continuations(result.zones[0])[2].__in == std::chrono::November);
+  assert(std::get<std::chrono::day>(continuations(result.zones[0])[2].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[0])[2].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[0])[2].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[0])[3].__year == std::chrono::year::max());
+  assert(continuations(result.zones[0])[3].__in == std::chrono::September);
+  assert(std::get<std::chrono::day>(continuations(result.zones[0])[3].__on) == std::chrono::day(31));
+  assert(continuations(result.zones[0])[3].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[0])[3].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[0])[4].__year == std::chrono::year(0));
+  assert(continuations(result.zones[0])[4].__in == std::chrono::January);
+  assert(std::get<std::chrono::weekday_last>(continuations(result.zones[0])[4].__on) ==
+         std::chrono::weekday_last{std::chrono::Wednesday});
+  assert(continuations(result.zones[0])[4].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[0])[4].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[0])[5].__year == std::chrono::year(-42));
+  assert(continuations(result.zones[0])[5].__in == std::chrono::June);
+  r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[0])[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(continuations(result.zones[0])[5].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[0])[5].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[0])[6].__year == std::chrono::year(42));
+  assert(continuations(result.zones[0])[6].__in == std::chrono::July);
+  r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[0])[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(continuations(result.zones[0])[6].__at.__time == std::chrono::seconds(0));
+  assert(continuations(result.zones[0])[6].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[0])[7].__year == std::chrono::year(42));
+  assert(continuations(result.zones[0])[7].__in == std::chrono::July);
+  assert(std::get<std::chrono::day>(continuations(result.zones[0])[7].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[0])[7].__at.__time == std::chrono::hours(2));
+  assert(continuations(result.zones[0])[7].__at.__clock == std::chrono::__tz::__clock::__local);
+
+  assert(continuations(result.zones[0])[8].__year == std::chrono::year(42));
+  assert(continuations(result.zones[0])[8].__in == std::chrono::July);
+  assert(std::get<std::chrono::day>(continuations(result.zones[0])[8].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[0])[8].__at.__time ==
+         std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
+  assert(continuations(result.zones[0])[8].__at.__clock == std::chrono::__tz::__clock::__universal);
+
+  assert(continuations(result.zones[0])[9].__year == std::chrono::year(42));
+  assert(continuations(result.zones[0])[9].__in == std::chrono::July);
+  assert(std::get<std::chrono::day>(continuations(result.zones[0])[9].__on) == std::chrono::day(1));
+  assert(continuations(result.zones[0])[9].__at.__time == std::chrono::hours(0)); // The man page expresses it in hours
+  assert(continuations(result.zones[0])[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 d4c22023db7a06..59c37fc369b850 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -127,6 +127,7 @@ chrono string
 chrono string_view
 chrono tuple
 chrono type_traits
+chrono vector
 chrono version
 cinttypes cstdint
 cmath limits
@@ -833,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 5eb839a3aa7a66..60381e1d994587 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -127,6 +127,7 @@ chrono string
 chrono string_view
 chrono tuple
 chrono type_traits
+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 7b372fb37dcd58..3feee838752062 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -127,6 +127,7 @@ chrono string
 chrono string_view
 chrono tuple
 chrono type_traits
+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 7b372fb37dcd58..3feee838752062 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -127,6 +127,7 @@ chrono string
 chrono string_view
 chrono tuple
 chrono type_traits
+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 9b4915a468d1cf..25822c942a9792 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -133,6 +133,7 @@ chrono string
 chrono string_view
 chrono tuple
 chrono type_traits
+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 bd8241118f4b91..64ac8dd324c110 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -87,6 +87,7 @@ chrono stdexcept
 chrono string
 chrono string_view
 chrono tuple
+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 bd8241118f4b91..64ac8dd324c110 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -87,6 +87,7 @@ chrono stdexcept
 chrono string
 chrono string_view
 chrono tuple
+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 4fec102b66aa25..3414f1b4855ba7 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 66d939f125eab7..9bd6a63e3bccb3 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,13 @@ int main(int, const char**) {
   tzdb.version = "version";
   assert(tzdb.version == "version");
 
-  // TODO TZDB add the other data members
+  std::vector<std::chrono::time_zone>& zones = tzdb.zones;
+  zones.clear(); // is it a non-const member?
+
+  std::vector<std::chrono::time_zone_link>& links = tzdb.links;
+  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 00000000000000..747ba42cd0c3da
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/name.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// TODO TZDB Enable tests
+// UNSUPPORTED: c++20, c++23, c++26
+
+// <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);
+
+  std::same_as<std::string_view> auto name = tzdb.links[0].name();
+  static_assert(noexcept(tzdb.links[0].name()));
+  assert(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 00000000000000..ed00ad9c06e4bd
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.members/target.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// TODO TZDB Enable tests
+// UNSUPPORTED: c++20, c++23, c++26
+
+// <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 00000000000000..c0a850cb909c1d
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.link/time.zone.link.nonmembers/comparison.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// TODO TZDB Enable tests
+// UNSUPPORTED: c++20, c++23, c++26
+
+// <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 00000000000000..39369fc6caa169
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.link/types.compile.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
+// {
+//   time_zone_link(time_zone_link&&)            = default;
+//   time_zone_link& operator=(time_zone_link&&) = default;
+//
+//   ...
+// };
+
+#include <chrono>
+#include <concepts>
+#include <type_traits>
+
+#include "test_macros.h"
+
+LIBCPP_STATIC_ASSERT(!std::copy_constructible<std::chrono::time_zone_link>);
+LIBCPP_STATIC_ASSERT(!std::is_copy_assignable_v<std::chrono::time_zone_link>);
+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 00000000000000..d16cb2b082ef23
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/name.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// TODO TZDB Enable tests
+// UNSUPPORTED: c++20, c++23, c++26
+
+// <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 00000000000000..8b53953ceaa0cb
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.nonmembers/comparison.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// TODO TZDB Enable tests
+// UNSUPPORTED: c++20, c++23, c++26
+
+// <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 00000000000000..35970c419803e9
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.timezone/types.compile.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
+// {
+//   time_zone(time_zone&&)            = default;
+//   time_zone& operator=(time_zone&&) = default;
+//
+//   ...
+// };
+
+#include <chrono>
+#include <concepts>
+#include <type_traits>
+
+#include "test_macros.h"
+
+LIBCPP_STATIC_ASSERT(!std::copy_constructible<std::chrono::time_zone>);
+LIBCPP_STATIC_ASSERT(!std::is_copy_assignable_v<std::chrono::time_zone>);
+static_assert(std::move_constructible<std::chrono::time_zone>);
+static_assert(std::is_move_assignable_v<std::chrono::time_zone>);

>From e01ce4cb6f8b336ad9b3c6fb6d8345a71dfe9586 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Tue, 13 Feb 2024 20:46:22 +0100
Subject: [PATCH 2/3] Address review comments and CI fixes.

---
 libcxx/include/__chrono/time_zone.h                | 10 +++++-----
 libcxx/include/__chrono/time_zone_link.h           |  2 +-
 libcxx/include/__chrono/tzdb_list.h                |  2 +-
 libcxx/modules/std/chrono.inc                      |  4 ++++
 libcxx/src/include/tzdb/time_zone_private.h        |  2 +-
 libcxx/src/time_zone.cpp                           | 14 +-------------
 libcxx/src/tzdb.cpp                                |  2 +-
 libcxx/src/tzdb_list.cpp                           |  2 --
 .../chrono.nodiscard_extensions.compile.pass.cpp   |  4 ++--
 .../chrono.nodiscard_extensions.verify.cpp         |  4 ++--
 .../time.zone.db.access/get_tzdb.pass.cpp          |  2 +-
 .../time.zone.db.access/get_tzdb_list.pass.cpp     |  2 +-
 .../time.zone.db/time.zone.db.list/front.pass.cpp  |  2 +-
 .../time.zone.db.list/iterators.pass.cpp           |  2 +-
 .../time.zone.db.remote/reload_tzdb.pass.cpp       |  2 +-
 .../time.zone.db.remote/remote_version.pass.cpp    |  2 +-
 .../time.zone.db.tzdb/tzdb.members.pass.cpp        |  6 ++----
 .../time.zone.link.members/name.pass.cpp           |  2 +-
 .../time.zone.link.members/target.pass.cpp         |  2 +-
 .../time.zone.link.nonmembers/comparison.pass.cpp  |  2 +-
 .../time.zone.link/types.compile.pass.cpp          |  3 +++
 .../time.zone.members/name.pass.cpp                |  2 +-
 .../time.zone.nonmembers/comparison.pass.cpp       |  2 +-
 .../time.zone.timezone/types.compile.pass.cpp      |  3 +++
 24 files changed, 37 insertions(+), 43 deletions(-)

diff --git a/libcxx/include/__chrono/time_zone.h b/libcxx/include/__chrono/time_zone.h
index 42e0bd1c85e8d1..7d97327a6c8e99 100644
--- a/libcxx/include/__chrono/time_zone.h
+++ b/libcxx/include/__chrono/time_zone.h
@@ -43,9 +43,9 @@ class _LIBCPP_AVAILABILITY_TZDB time_zone {
 
   // The "constructor".
   //
-  // The default constructor is private to avoid the constructor from being an
-  // ABI interface. Instead use an __ugly_named function as an ABI interface,
-  // since users should not depend on __ugly_names.
+  // The default constructor is private to avoid the constructor from being
+  // part of the ABI. Instead use an __ugly_named function as an ABI interface,
+  // since that gives us the ability to change it in the future.
   [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI static time_zone __create(unique_ptr<__impl>&& __p);
 
   _LIBCPP_EXPORTED_FROM_ABI ~time_zone();
@@ -55,9 +55,9 @@ class _LIBCPP_AVAILABILITY_TZDB time_zone {
 
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name(); }
 
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const __impl& __implementation() const noexcept;
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const __impl& __implementation() const noexcept { return *__impl_; }
 
-  // private:
+private:
   [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view __name() const noexcept;
   unique_ptr<__impl> __impl_;
 };
diff --git a/libcxx/include/__chrono/time_zone_link.h b/libcxx/include/__chrono/time_zone_link.h
index 884bc635fa858b..17e915d2677a8c 100644
--- a/libcxx/include/__chrono/time_zone_link.h
+++ b/libcxx/include/__chrono/time_zone_link.h
@@ -39,7 +39,7 @@ class time_zone_link {
 public:
   struct __constructor_tag;
   _LIBCPP_NODISCARD_EXT
-  _LIBCPP_HIDE_FROM_ABI explicit time_zone_link(string_view __name, string_view __target, __constructor_tag&&)
+  _LIBCPP_HIDE_FROM_ABI explicit time_zone_link(__constructor_tag&&, string_view __name, string_view __target)
       : __name_{__name}, __target_{__target} {}
 
   _LIBCPP_HIDE_FROM_ABI time_zone_link(time_zone_link&&)            = default;
diff --git a/libcxx/include/__chrono/tzdb_list.h b/libcxx/include/__chrono/tzdb_list.h
index 320a6a3285132e..a961737c54563f 100644
--- a/libcxx/include/__chrono/tzdb_list.h
+++ b/libcxx/include/__chrono/tzdb_list.h
@@ -59,7 +59,7 @@ class _LIBCPP_AVAILABILITY_TZDB tzdb_list {
   _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();
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __impl& __implementation() { return *__impl_; }
 
 private:
   __impl* __impl_;
diff --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc
index 8ebe8d26065dcd..109023a3abc25f 100644
--- a/libcxx/modules/std/chrono.inc
+++ b/libcxx/modules/std/chrono.inc
@@ -222,7 +222,9 @@ export namespace std {
     // [time.zone.timezone], class time_zone
     using std::chrono::choose;
 #  endif
+#  ifdef _LIBCPP_ENABLE_EXPERIMENTAL
     using std::chrono::time_zone;
+#  endif
 #  if 0
 
     // [time.zone.zonedtraits], class template zoned_traits
@@ -237,8 +239,10 @@ export namespace std {
     using std::chrono::leap_second;
 #  endif
 
+#  ifdef _LIBCPP_ENABLE_EXPERIMENTAL
     // [time.zone.link], class time_zone_link
     using std::chrono::time_zone_link;
+#  endif
 
 #  if 0
     // [time.format], formatting
diff --git a/libcxx/src/include/tzdb/time_zone_private.h b/libcxx/src/include/tzdb/time_zone_private.h
index 29bd485e2d2637..039a3b0ffeb7c2 100644
--- a/libcxx/src/include/tzdb/time_zone_private.h
+++ b/libcxx/src/include/tzdb/time_zone_private.h
@@ -26,7 +26,7 @@ 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 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 {
diff --git a/libcxx/src/time_zone.cpp b/libcxx/src/time_zone.cpp
index a05f10a24c8f48..b6bf06a116f68b 100644
--- a/libcxx/src/time_zone.cpp
+++ b/libcxx/src/time_zone.cpp
@@ -16,15 +16,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace chrono {
 
-#if 0
-_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");
-}
-#endif
-
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI time_zone time_zone::__create(unique_ptr<time_zone::__impl>&& __p) {
-  //   : __impl_(std::move(__p)) {
   _LIBCPP_ASSERT_NON_NULL(__p != nullptr, "initialized time_zone without a valid pimpl object");
   time_zone result;
   result.__impl_ = std::move(__p);
@@ -33,11 +25,7 @@ _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI time_zone::time_zone(unique_ptr<
 
 _LIBCPP_EXPORTED_FROM_ABI time_zone::~time_zone() = default;
 
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view time_zone::__name() const noexcept { return __impl_->name(); }
-
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const time_zone::__impl& time_zone::__implementation() const noexcept {
-  return *__impl_;
-}
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view time_zone::__name() const noexcept { return __impl_->__name(); }
 
 } // namespace chrono
 
diff --git a/libcxx/src/tzdb.cpp b/libcxx/src/tzdb.cpp
index a0e7f177dac052..2bb801e48694ee 100644
--- a/libcxx/src/tzdb.cpp
+++ b/libcxx/src/tzdb.cpp
@@ -562,7 +562,7 @@ static void __parse_link(tzdb& __tzdb, istream& __input) {
   string __name = chrono::__parse_string(__input);
   chrono::__skip_line(__input);
 
-  __tzdb.links.emplace_back(std::move(__name), std::move(__target), time_zone_link::__constructor_tag{});
+  __tzdb.links.emplace_back(time_zone_link::__constructor_tag{}, std::move(__name), std::move(__target));
 }
 
 static void __parse_tzdata(tzdb& __db, __tz::__rules_storage_type& __rules, istream& __input) {
diff --git a/libcxx/src/tzdb_list.cpp b/libcxx/src/tzdb_list.cpp
index 7c521199884443..f22921e72a34ad 100644
--- a/libcxx/src/tzdb_list.cpp
+++ b/libcxx/src/tzdb_list.cpp
@@ -44,8 +44,6 @@ _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/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp
index ec09e4d082cd38..8019824046c47f 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp
@@ -24,8 +24,8 @@
 #include "test_macros.h"
 
 // These types have "private" constructors.
-extern  std::chrono::time_zone tz;
-extern  std::chrono::time_zone_link link;
+extern std::chrono::time_zone tz;
+extern std::chrono::time_zone_link link;
 
 void test() {
   std::chrono::tzdb_list& list = std::chrono::get_tzdb_list();
diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
index 4ad9b7105f6926..e9b27559497b7e 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
@@ -21,8 +21,8 @@
 #include "test_macros.h"
 
 // These types have "private" constructors.
-extern  std::chrono::time_zone tz;
-extern  std::chrono::time_zone_link link;
+extern std::chrono::time_zone tz;
+extern std::chrono::time_zone_link link;
 
 void test() {
   std::chrono::tzdb_list& list = std::chrono::get_tzdb_list();
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 3414f1b4855ba7..335e8d20c23910 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
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb_list.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb_list.pass.cpp
index 4d7647145ee089..34af9b576361fa 100644
--- a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb_list.pass.cpp
+++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb_list.pass.cpp
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/front.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/front.pass.cpp
index 485cf9766962a7..ac5fee8183b9bc 100644
--- a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/front.pass.cpp
+++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/front.pass.cpp
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/iterators.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/iterators.pass.cpp
index 7fec4cf8556b54..8bd9b321b2a0fb 100644
--- a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/iterators.pass.cpp
+++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/iterators.pass.cpp
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp
index 35d40b32d485fe..bbf9002c0430ca 100644
--- a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp
+++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/remote_version.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/remote_version.pass.cpp
index f749adc3a63c63..861075cd82aa60 100644
--- a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/remote_version.pass.cpp
+++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/remote_version.pass.cpp
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
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 9bd6a63e3bccb3..51c6d364c9be00 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,11 +37,9 @@ int main(int, const char**) {
   tzdb.version = "version";
   assert(tzdb.version == "version");
 
-  std::vector<std::chrono::time_zone>& zones = tzdb.zones;
-  zones.clear(); // is it a non-const member?
+  [[maybe_unused]] std::vector<std::chrono::time_zone>& zones = tzdb.zones;
 
-  std::vector<std::chrono::time_zone_link>& links = tzdb.links;
-  links.clear(); // is it a non-const member?
+  [[maybe_unused]] std::vector<std::chrono::time_zone_link>& links = tzdb.links;
 
   // TODO TZDB add the leap data member
 
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
index 747ba42cd0c3da..95d86d586666ea 100644
--- 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
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
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
index ed00ad9c06e4bd..305fbd21f625b5 100644
--- 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
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
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
index c0a850cb909c1d..e375d7e443ce49 100644
--- 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
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
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
index 39369fc6caa169..2e4f178c7c0504 100644
--- 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
@@ -28,6 +28,9 @@
 
 #include "test_macros.h"
 
+// It's impossible to actually obtain a non-const reference to a
+// time_zone_link, and as a result the move constructor can never be exercised
+// in runtime code. We still check the property pedantically.
 LIBCPP_STATIC_ASSERT(!std::copy_constructible<std::chrono::time_zone_link>);
 LIBCPP_STATIC_ASSERT(!std::is_copy_assignable_v<std::chrono::time_zone_link>);
 static_assert(std::move_constructible<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
index d16cb2b082ef23..2bbe714b71a683 100644
--- 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
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
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
index 8b53953ceaa0cb..9eae91e80a42d9 100644
--- 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
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-has-no-incomplete-tzdb
 // XFAIL: availability-tzdb-missing
 
-// TODO TZDB Enable tests
+// TODO TZDB (#81654) Enable tests
 // UNSUPPORTED: c++20, c++23, c++26
 
 // <chrono>
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
index 35970c419803e9..915d5596bf22b0 100644
--- 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
@@ -28,6 +28,9 @@
 
 #include "test_macros.h"
 
+// It's impossible to actually obtain a non-const reference to a time_zone, and
+// as a result the move constructor can never be exercised in runtime code. We
+// still check the property pedantically.
 LIBCPP_STATIC_ASSERT(!std::copy_constructible<std::chrono::time_zone>);
 LIBCPP_STATIC_ASSERT(!std::is_copy_assignable_v<std::chrono::time_zone>);
 static_assert(std::move_constructible<std::chrono::time_zone>);

>From b5c1d3a633aa1a2b9639918f01a2032a54da801a Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Fri, 16 Feb 2024 17:34:47 +0100
Subject: [PATCH 3/3] Address final review comments.

---
 libcxx/include/__chrono/tzdb_list.h | 7 +++++--
 libcxx/src/tzdb_list.cpp            | 4 ----
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__chrono/tzdb_list.h b/libcxx/include/__chrono/tzdb_list.h
index a961737c54563f..112e04ff2ee6ac 100644
--- a/libcxx/include/__chrono/tzdb_list.h
+++ b/libcxx/include/__chrono/tzdb_list.h
@@ -18,8 +18,9 @@
 
 #  include <__availability>
 #  include <__chrono/tzdb.h>
+#  include <__config>
+#  include <__fwd/string.h>
 #  include <forward_list>
-#  include <string>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header
@@ -41,7 +42,9 @@ namespace chrono {
 class _LIBCPP_AVAILABILITY_TZDB tzdb_list {
 public:
   class __impl; // public to allow construction in dylib
-  _LIBCPP_EXPORTED_FROM_ABI explicit tzdb_list(__impl* __p);
+  _LIBCPP_HIDE_FROM_ABI explicit 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(const tzdb_list&)            = delete;
diff --git a/libcxx/src/tzdb_list.cpp b/libcxx/src/tzdb_list.cpp
index f22921e72a34ad..d3ee8b58f98bf2 100644
--- a/libcxx/src/tzdb_list.cpp
+++ b/libcxx/src/tzdb_list.cpp
@@ -16,10 +16,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace chrono {
 
-_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_; }
 
 _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const tzdb& tzdb_list::front() const noexcept {



More information about the libcxx-commits mailing list