]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libstdc++-v3/include/std/chrono
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / include / std / chrono
index 4c5fbfaeb83bc4d8db9c811e64f33042d095d4d1..b3ad2a0b1acd55e89a5aefac288c3a58fb12c25c 100644 (file)
@@ -1,6 +1,6 @@
 // <chrono> -*- C++ -*-
 
-// Copyright (C) 2008-2022 Free Software Foundation, Inc.
+// Copyright (C) 2008-2024 Free Software Foundation, Inc.
 //
 // This file is part of the GNU ISO C++ Library.  This library is free
 // software; you can redistribute it and/or modify it under the
 # include <sstream>
 # include <string>
 # include <vector>
-# include <bits/charconv.h> // __to_chars_len, __to_chars_10_impl
-# include <bits/stl_algo.h> // upper_bound TODO: move leap_second_info to .so
+# include <bits/stl_algo.h> // upper_bound
 # include <bits/shared_ptr.h>
 # include <bits/unique_ptr.h>
 #endif
 
+#define __glibcxx_want_chrono
+#define __glibcxx_want_chrono_udls
+#include <bits/version.h>
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -128,31 +131,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       using time_point                = chrono::time_point<utc_clock>;
       static constexpr bool is_steady = false;
 
+      [[nodiscard]]
       static time_point
       now()
       { return from_sys(system_clock::now()); }
 
       template<typename _Duration>
+       [[nodiscard]]
        static sys_time<common_type_t<_Duration, seconds>>
        to_sys(const utc_time<_Duration>& __t)
        {
          using _CDur = common_type_t<_Duration, seconds>;
          const auto __li = chrono::get_leap_second_info(__t);
-         sys_time<_CDur> __s{__t.time_since_epoch() - seconds{__li.elapsed}};
+         sys_time<_CDur> __s{__t.time_since_epoch() - __li.elapsed};
          if (__li.is_leap_second)
            __s = chrono::floor<seconds>(__s) + seconds{1} - _CDur{1};
          return __s;
        }
 
       template<typename _Duration>
+       [[nodiscard]]
        static utc_time<common_type_t<_Duration, seconds>>
-       from_sys(const sys_time<_Duration>& __t)
-       {
-         using _CDur = common_type_t<_Duration, seconds>;
-         utc_time<_Duration> __u(__t.time_since_epoch());
-         const auto __li = chrono::get_leap_second_info(__u);
-         return utc_time<_CDur>{__u} + seconds{__li.elapsed};
-       }
+       from_sys(const sys_time<_Duration>& __t);
     };
 
     /** A clock that measures International Atomic Time.
@@ -171,11 +171,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static constexpr bool is_steady = false; // XXX true for CLOCK_TAI?
 
       // TODO move into lib, use CLOCK_TAI on linux, add extension point.
+      [[nodiscard]]
       static time_point
       now()
       { return from_utc(utc_clock::now()); }
 
       template<typename _Duration>
+       [[nodiscard]]
        static utc_time<common_type_t<_Duration, seconds>>
        to_utc(const tai_time<_Duration>& __t)
        {
@@ -184,6 +186,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
       template<typename _Duration>
+       [[nodiscard]]
        static tai_time<common_type_t<_Duration, seconds>>
        from_utc(const utc_time<_Duration>& __t)
        {
@@ -208,11 +211,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static constexpr bool is_steady = false; // XXX
 
       // TODO move into lib, add extension point.
+      [[nodiscard]]
       static time_point
       now()
       { return from_utc(utc_clock::now()); }
 
       template<typename _Duration>
+       [[nodiscard]]
        static utc_time<common_type_t<_Duration, seconds>>
        to_utc(const gps_time<_Duration>& __t)
        {
@@ -221,6 +226,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
       template<typename _Duration>
+       [[nodiscard]]
        static gps_time<common_type_t<_Duration, seconds>>
        from_utc(const utc_time<_Duration>& __t)
        {
@@ -394,6 +400,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     /// Convert a time point to a different clock.
     template<typename _DestClock, typename _SourceClock, typename _Duration>
+      [[nodiscard]]
       inline auto
       clock_cast(const time_point<_SourceClock, _Duration>& __t)
       requires __detail::__clock_convs<_DestClock, _SourceClock, _Duration>
@@ -617,8 +624,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr year_month_day
       operator/(const year_month& __ym, const day& __d) noexcept;
-
-      // TODO: Implement operator<<, to_stream, from_stream.
     };
 
     // MONTH
@@ -741,8 +746,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr month_weekday_last
       operator/(const weekday_last& __wdl, const month& __m) noexcept;
-
-      // TODO: Implement operator<<, to_stream, from_stream.
     };
 
     inline constexpr month January{1};
@@ -836,29 +839,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr bool
       is_leap() const noexcept
       {
-       // Testing divisibility by 100 first gives better performance, that is,
-       // return (_M_y % 100 != 0 || _M_y % 400 == 0) && _M_y % 4 == 0;
-
-       // It gets even faster if _M_y is in [-536870800, 536870999]
-       // (which is the case here) and _M_y % 100 is replaced by
-       // __is_multiple_of_100 below.
+       // Testing divisibility by 100 first gives better performance [1], i.e.,
+       //     return _M_y % 100 == 0 ? _M_y % 400 == 0 : _M_y % 16 == 0;
+       // Furthermore, if _M_y % 100 == 0, then _M_y % 400 == 0 is equivalent
+       // to _M_y % 16 == 0, so we can simplify it to
+       //     return _M_y % 100 == 0 ? _M_y % 16 == 0 : _M_y % 4 == 0.  // #1
+       // Similarly, we can replace 100 with 25 (which is good since
+       // _M_y % 25 == 0 requires one fewer instruction than _M_y % 100 == 0
+       // [2]):
+       //     return _M_y % 25 == 0 ? _M_y % 16 == 0 : _M_y % 4 == 0.  // #2
+       // Indeed, first assume _M_y % 4 != 0.  Then _M_y % 16 != 0 and hence,
+       // _M_y % 4 == 0 and _M_y % 16 == 0 are both false.  Therefore, #2
+       // returns false as it should (regardless of _M_y % 25.) Now assume
+       // _M_y % 4 == 0.  In this case, _M_y % 25 == 0 if, and only if,
+       // _M_y % 100 == 0, that is, #1 and #2 are equivalent.  Finally, #2 is
+       // equivalent to
+       //     return (_M_y & (_M_y % 25 == 0 ? 15 : 3)) == 0.
 
        // References:
        // [1] https://github.com/cassioneri/calendar
-       // [2] https://accu.org/journals/overload/28/155/overload155.pdf#page=16
-
-       // Furthermore, if y%100 == 0, then y%400==0 is equivalent to y%16==0,
-       // so we can simplify it to (!mult_100 && y % 4 == 0) || y % 16 == 0,
-       // which is equivalent to (y & (mult_100 ? 15 : 3)) == 0.
-       // See https://gcc.gnu.org/pipermail/libstdc++/2021-June/052815.html
-
-       constexpr uint32_t __multiplier   = 42949673;
-       constexpr uint32_t __bound        = 42949669;
-       constexpr uint32_t __max_dividend = 1073741799;
-       constexpr uint32_t __offset       = __max_dividend / 2 / 100 * 100;
-       const bool __is_multiple_of_100
-         = __multiplier * (_M_y + __offset) < __bound;
-       return (_M_y & (__is_multiple_of_100 ? 15 : 3)) == 0;
+       // [2] https://godbolt.org/z/55G8rn77e
+       // [3] https://gcc.gnu.org/pipermail/libstdc++/2021-June/052815.html
+
+       return (_M_y & (_M_y % 25 == 0 ? 15 : 3)) == 0;
       }
 
       explicit constexpr
@@ -919,8 +922,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr year_month_weekday_last
       operator/(const month_weekday_last& __mwdl, const year& __y) noexcept;
-
-      // TODO: Implement operator<<, to_stream, from_stream.
     };
 
     // WEEKDAY
@@ -933,8 +934,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static constexpr weekday
       _S_from_days(const days& __d)
       {
-       auto __n = __d.count();
-       return weekday(__n >= -4 ? (__n + 4) % 7 : (__n + 5) % 7 + 6);
+       using _Rep = days::rep;
+       using _URep = make_unsigned_t<_Rep>;
+       const auto __n = __d.count();
+       const auto __m = static_cast<_URep>(__n);
+
+       // 1970-01-01 (__n =  0, __m = 0        ) -> Thursday (4)
+       // 1969-31-12 (__n = -1, __m = _URep(-1)) -> Wednesday (3)
+       const auto __offset = __n >= 0 ? _URep(4) : 3 - _URep(-1) % 7 - 7;
+       return weekday((__m + __offset) % 7);
       }
 
     public:
@@ -1039,11 +1047,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       friend constexpr days
       operator-(const weekday& __x, const weekday& __y) noexcept
       {
-       auto __n = static_cast<long long>(__x._M_wd) - __y._M_wd;
-       return days{__detail::__modulo(__n, 7)};
+       const auto __n = __x.c_encoding() - __y.c_encoding();
+       return static_cast<int>(__n) >= 0 ? days{__n} : days{__n + 7};
       }
-
-      // TODO: operator<<, from_stream.
     };
 
     inline constexpr weekday Sunday{0};
@@ -1100,8 +1106,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr year_month_weekday
       operator/(const year_month& __ym, const weekday_indexed& __wdi) noexcept;
-
-      // TODO: Implement operator<<.
     };
 
     constexpr weekday_indexed
@@ -1141,8 +1145,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr year_month_weekday_last
       operator/(const year_month& __ym, const weekday_last& __wdl) noexcept;
-
-      // TODO: Implement operator<<.
     };
 
     constexpr weekday_last
@@ -1214,8 +1216,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr year_month_day
       operator/(const month_day& __md, int __y) noexcept;
-
-      // TODO: Implement operator<<, from_stream.
     };
 
     // MONTH_DAY_LAST
@@ -1268,8 +1268,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr year_month_day_last
       operator/(const month_day_last& __mdl, int __y) noexcept;
-
-      // TODO: Implement operator<<.
     };
 
     // MONTH_WEEKDAY
@@ -1329,8 +1327,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr year_month_weekday
       operator/(const month_weekday& __mwd, int __y) noexcept;
-
-      // TODO: Implement operator<<.
     };
 
     // MONTH_WEEKDAY_LAST
@@ -1391,8 +1387,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr year_month_weekday_last
       operator/(const month_weekday_last& __mwdl, int __y) noexcept;
-
-      // TODO: Implement operator<<.
     };
 
     // YEAR_MONTH
@@ -1534,8 +1528,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       friend constexpr year_month_day_last
       operator/(const year_month& __ym, last_spec) noexcept;
-
-      // TODO: Implement operator<<, from_stream.
     };
 
     // YEAR_MONTH_DAY
@@ -1687,8 +1679,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       friend constexpr year_month_day
       operator/(const month_day& __md, int __y) noexcept
       { return chrono::year(__y) / __md; }
-
-      // TODO: Implement operator<<, from_stream.
     };
 
     // Construct from days since 1970/01/01.
@@ -1821,22 +1811,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       {
        const auto __m = static_cast<unsigned>(month());
 
-       // Excluding February, the last day of month __m is either 30 or 31 or,
-       // in another words, it is 30 + b = 30 | b, where b is in {0, 1}.
+       // The result is unspecified if __m < 1 or __m > 12.  Hence, assume
+       // 1 <= __m <= 12.  For __m != 2, day() == 30 or day() == 31 or, in
+       // other words, day () == 30 | b, where b is in {0, 1}.
 
-       // If __m in {1, 3, 4, 5, 6, 7}, then b is 1 if, and only if __m is odd.
-       // Hence, b = __m & 1 = (__m ^ 0) & 1.
+       // If __m in {1, 3, 4, 5, 6, 7}, then b is 1 if, and only if, __m is
+       // odd.  Hence, b = __m & 1 = (__m ^ 0) & 1.
 
-       // If __m in {8, 9, 10, 11, 12}, then b is 1 if, and only if __m is even.
-       // Hence, b = (__m ^ 1) & 1.
+       // If __m in {8, 9, 10, 11, 12}, then b is 1 if, and only if, __m is
+       // even.  Hence, b = (__m ^ 1) & 1.
 
        // Therefore, b = (__m ^ c) & 1, where c = 0, if __m < 8, or c = 1 if
        // __m >= 8, that is, c = __m >> 3.
 
-       // The above mathematically justifies this implementation whose
-       // performance does not depend on look-up tables being on the L1 cache.
-       return chrono::day{__m != 2 ? ((__m ^ (__m >> 3)) & 1) | 30
-                                   : _M_y.is_leap() ? 29 : 28};
+       // Since 30 = (11110)_2 and __m <= 31 = (11111)_2, the "& 1" in b's
+       // calculation is unnecessary.
+
+       // The performance of this implementation does not depend on look-up
+       // tables being on the L1 cache.
+       return chrono::day{__m != 2 ? (__m ^ (__m >> 3)) | 30
+         : _M_y.is_leap() ? 29 : 28};
       }
 
       constexpr
@@ -1918,8 +1912,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       friend constexpr year_month_day_last
       operator/(const chrono::month_day_last& __mdl, int __y) noexcept
       { return chrono::year(__y) / __mdl; }
-
-      // TODO: Implement operator<<.
     };
 
     // year_month_day ctor from year_month_day_last
@@ -2046,7 +2038,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                    - chrono::weekday{sys_days{_M_y / _M_m / 1}}
                    + days((_M_wdi.index()-1)*7 + 1));
        __glibcxx_assert(__d.count() >= 1);
-       return __d.count() <= unsigned{(_M_y / _M_m / last).day()};
+       return (unsigned)__d.count() <= (unsigned)(_M_y / _M_m / last).day();
       }
 
       friend constexpr bool
@@ -2108,8 +2100,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       friend constexpr year_month_weekday
       operator/(const month_weekday& __mwd, int __y) noexcept
       { return chrono::year(__y) / __mwd; }
-
-      // TODO: Implement operator<<.
     };
 
     // YEAR_MONTH_WEEKDAY_LAST
@@ -2257,8 +2247,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       friend constexpr year_month_weekday_last
       operator/(const chrono::month_weekday_last& __mwdl, int __y) noexcept
       { return chrono::year(__y) / __mwdl; }
-
-      // TODO: Implement operator<<.
     };
 
     // HH_MM_SS
@@ -2274,6 +2262,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          __r *= 10;
        return __r;
       }
+
+      template<typename _Duration> struct __utc_leap_second;
     }
     /// @endcond
 
@@ -2313,7 +2303,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
        constexpr
-       hh_mm_ss(_Duration __d, bool __is_neg) noexcept
+       hh_mm_ss(_Duration __d, bool __is_neg)
        : _M_h (duration_cast<chrono::hours>(__d)),
          _M_m (duration_cast<chrono::minutes>(__d - hours())),
          _M_s (duration_cast<chrono::seconds>(__d - hours() - minutes())),
@@ -2326,6 +2316,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
            _M_ss._M_r = duration_cast<precision>(__ss).count();
        }
 
+       static constexpr _Duration
+       _S_abs(_Duration __d)
+       {
+         if constexpr (numeric_limits<typename _Duration::rep>::is_signed)
+           return chrono::abs(__d);
+         else
+           return __d;
+       }
+
       public:
        static constexpr unsigned fractional_width = {_S_fractional_width()};
 
@@ -2337,8 +2336,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        constexpr hh_mm_ss() noexcept = default;
 
        constexpr explicit
-       hh_mm_ss(_Duration __d) noexcept
-       : hh_mm_ss(chrono::abs(__d), __d < _Duration::zero())
+       hh_mm_ss(_Duration __d)
+       : hh_mm_ss(_S_abs(__d), __d < _Duration::zero())
        { }
 
        constexpr bool
@@ -2379,8 +2378,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          return _M_h + _M_m + _M_s + subseconds();
        }
 
-       // TODO: Implement operator<<.
-
       private:
        static constexpr bool _S_is_unsigned
          = __and_v<is_integral<typename _Duration::rep>,
@@ -2410,17 +2407,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
            { return {}; }
          };
 
-       // True if the maximum constructor argument can be represented in _Tp.
-       template<typename _Tp>
-         static constexpr bool __fits
-           = duration_values<typename _Duration::rep>::max()
-               <= duration_values<_Tp>::max();
-
        template<typename _Rep, typename _Period>
          requires (!treat_as_floating_point_v<_Rep>)
            && ratio_less_v<_Period, ratio<1, 1>>
-           && (ratio_greater_equal_v<_Period, ratio<1, 250>>
-                 || __fits<unsigned char>)
+           && ratio_greater_equal_v<_Period, ratio<1, 250>>
          struct __subseconds<duration<_Rep, _Period>>
          {
            unsigned char _M_r{};
@@ -2433,8 +2423,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        template<typename _Rep, typename _Period>
          requires (!treat_as_floating_point_v<_Rep>)
            && ratio_less_v<_Period, ratio<1, 250>>
-           && (ratio_greater_equal_v<_Period, ratio<1, 4000000000>>
-                 || __fits<uint_least32_t>)
+           && ratio_greater_equal_v<_Period, ratio<1, 4000000000>>
          struct __subseconds<duration<_Rep, _Period>>
          {
            uint_least32_t _M_r{};
@@ -2449,8 +2438,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        __byte_duration<ratio<1>>   _M_s{};
        bool                        _M_is_neg{};
        __subseconds<precision>     _M_ss{};
+
+       template<typename> friend struct __detail::__utc_leap_second;
       };
 
+    /// @cond undocumented
+    namespace __detail
+    {
+      // Represents a time that is within a leap second insertion.
+      template<typename _Duration>
+       struct __utc_leap_second
+       {
+         explicit
+         __utc_leap_second(const sys_time<_Duration>& __s)
+         : _M_date(chrono::floor<days>(__s)), _M_time(__s - _M_date)
+         {
+           ++_M_time._M_s;
+         }
+
+         sys_days _M_date;
+         hh_mm_ss<common_type_t<_Duration, days>> _M_time;
+       };
+    }
+    /// @endcond
+
     // 12/24 HOURS FUNCTIONS
 
     constexpr bool
@@ -2490,8 +2501,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
     }
 
+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
     // C++20 [time.zones] Time zones
 
+    struct tzdb;
+
     struct sys_info
     {
       sys_seconds begin;
@@ -2522,9 +2536,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        { __glibcxx_assert(__i.result == local_info::nonexistent); }
 
     private:
-      template<typename _Duration> // TODO
+      template<typename _Duration>
        static string
-       _S_make_what_str(const local_time<_Duration>&, const local_info&);
+       _S_make_what_str(const local_time<_Duration>& __tp,
+                        const local_info& __i)
+       {
+         std::ostringstream __os;
+         __os << __tp << " is in a gap between\n"
+              << local_seconds(__i.first.end.time_since_epoch())
+              + __i.first.offset << ' ' << __i.first.abbrev << " and\n"
+              << local_seconds(__i.second.begin.time_since_epoch())
+              + __i.second.offset << ' ' << __i.second.abbrev
+              << " which are both equivalent to\n"
+              << __i.first.end << " UTC";
+         return std::move(__os).str();
+       }
     };
 
     class ambiguous_local_time : public runtime_error
@@ -2532,16 +2558,40 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     public:
       template<typename _Duration>
        ambiguous_local_time(const local_time<_Duration>& __tp,
-                              const local_info& __i)
+                            const local_info& __i)
        : runtime_error(_S_make_what_str(__tp, __i))
-       { __glibcxx_assert(__i.result == local_info::nonexistent); }
+       { __glibcxx_assert(__i.result == local_info::ambiguous); }
 
     private:
-      template<typename _Duration> // TODO
+      template<typename _Duration>
        static string
-       _S_make_what_str(const local_time<_Duration>&, const local_info&);
+       _S_make_what_str(const local_time<_Duration>& __tp,
+                        const local_info& __i)
+       {
+         std::ostringstream __os;
+         __os << __tp << " is ambiguous.  It could be\n"
+              << __tp << ' ' << __i.first.abbrev << " == "
+              << __tp - __i.first.offset << " UTC or\n"
+              << __tp << ' ' << __i.second.abbrev << " == "
+              << __tp - __i.second.offset << " UTC";
+         return std::move(__os).str();
+       }
     };
 
+    template<typename _Duration>
+      [[noreturn]] void
+      __throw_bad_local_time(const local_time<_Duration>& __tp,
+                            const local_info& __i)
+      {
+#if __cpp_exceptions
+       if (__i.result == local_info::nonexistent)
+         throw nonexistent_local_time(__tp, __i);
+       throw ambiguous_local_time(__tp, __i);
+#else
+       __builtin_abort();
+#endif
+      }
+
     enum class choose { earliest, latest };
 
     class time_zone
@@ -2550,46 +2600,188 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       time_zone(time_zone&&) = default;
       time_zone& operator=(time_zone&&) = default;
 
+      ~time_zone();
+
+      [[nodiscard]]
       string_view name() const noexcept { return _M_name; }
 
       template<typename _Duration>
        sys_info
-       get_info(const sys_time<_Duration>& __st) const;
+       get_info(const sys_time<_Duration>& __st) const
+       { return _M_get_sys_info(chrono::floor<seconds>(__st)); }
 
       template<typename _Duration>
        local_info
-       get_info(const local_time<_Duration>& __tp) const;
+       get_info(const local_time<_Duration>& __tp) const
+       { return _M_get_local_info(chrono::floor<seconds>(__tp)); }
 
       template<typename _Duration>
        sys_time<common_type_t<_Duration, seconds>>
-       to_sys(const local_time<_Duration>& __tp) const;
+       to_sys(const local_time<_Duration>& __tp) const
+       {
+         local_info __info = get_info(__tp);
+
+         if (__info.result != local_info::unique)
+           __throw_bad_local_time(__tp, __info);
+
+         return sys_time<_Duration>(__tp.time_since_epoch())
+                  - __info.first.offset;
+       }
 
       template<typename _Duration>
        sys_time<common_type_t<_Duration, seconds>>
-       to_sys(const local_time<_Duration>& __tp, choose __z) const;
+       to_sys(const local_time<_Duration>& __tp, choose __z) const
+       {
+         local_info __info = get_info(__tp);
+
+         if (__info.result == local_info::nonexistent)
+           return __info.first.end; // Last second of the previous sys_info.
+
+         sys_time<_Duration> __st(__tp.time_since_epoch());
+
+         if (__info.result == local_info::ambiguous && __z == choose::latest)
+           return __st - __info.second.offset; // Time in the later sys_info.
+         // else if __z == earliest, use __info.first.offset as below:
+
+         return __st - __info.first.offset;
+       }
 
       template<typename _Duration>
        local_time<common_type_t<_Duration, seconds>>
-       to_local(const sys_time<_Duration>& __tp) const;
+       to_local(const sys_time<_Duration>& __tp) const
+       {
+         auto __d = (__tp + get_info(__tp).offset).time_since_epoch();
+         return local_time<common_type_t<_Duration, seconds>>(__d);
+       }
 
-      friend bool
+      [[nodiscard]] friend bool
       operator==(const time_zone& __x, const time_zone& __y) noexcept
-      { return __x.name() == __y.name(); }
+      { return __x._M_name == __y._M_name; }
 
-      friend strong_ordering
+      [[nodiscard]] friend strong_ordering
       operator<=>(const time_zone& __x, const time_zone& __y) noexcept
-      { return __x.name() <=> __y.name(); }
+      { return __x._M_name <=> __y._M_name; }
 
     private:
-      string _M_name;
+      sys_info _M_get_sys_info(sys_seconds) const;
+      local_info _M_get_local_info(local_seconds) const;
+
+      friend const tzdb& reload_tzdb();
+      friend struct tzdb;
+      friend class tzdb_list;
+
       struct _Impl;
+
+      explicit time_zone(unique_ptr<_Impl> __p);
+      string _M_name;
       unique_ptr<_Impl> _M_impl;
     };
 
-    struct tzdb;
     const time_zone* locate_zone(string_view __tz_name);
     const time_zone* current_zone();
 
+    /** The list of `chrono::tzdb` objects
+     *
+     * A single object of this type is constructed by the C++ runtime,
+     * and can be accessed by calling `chrono::get_tzdb_list()`.
+     *
+     * The front of the list is the current `tzdb` object and can be accessed
+     * via `chrono::get_tzdb_list().front()` or `chrono::get_tzdb()` or
+     * `*chrono::get_tzdb_list().begin()`.
+     *
+     * The `chrono::reload_tzdb()` function will check for a newer version
+     * and if found, insert it at the front of the list.
+     *
+     * @since C++20
+     */
+    class tzdb_list
+    {
+      struct _Node;
+
+    public:
+      tzdb_list(const tzdb_list&) = delete;
+      tzdb_list& operator=(const tzdb_list&) = delete;
+
+      /** An iterator into the `tzdb_list`
+       *
+       * As a extension, in libstdc++ each `tzdb` is reference-counted
+       * and the `const_iterator` type shares ownership of the object it
+       * refers to. This ensures that a `tzdb` erased from the list will
+       * not be destroyed while there is an iterator that refers to it.
+       */
+      class const_iterator
+      {
+      public:
+       using value_type        = tzdb;
+       using reference         = const tzdb&;
+       using pointer           = const tzdb*;
+       using difference_type   = ptrdiff_t;
+       using iterator_category = forward_iterator_tag;
+
+       constexpr const_iterator() = default;
+       const_iterator(const const_iterator&) = default;
+       const_iterator(const_iterator&&) = default;
+       const_iterator& operator=(const const_iterator&) = default;
+       const_iterator& operator=(const_iterator&&) = default;
+
+       reference operator*() const noexcept;
+       pointer operator->() const noexcept { return &**this; }
+       const_iterator& operator++();
+       const_iterator operator++(int);
+
+       bool operator==(const const_iterator&) const noexcept = default;
+
+      private:
+       explicit const_iterator(const shared_ptr<_Node>&) noexcept;
+
+       friend class tzdb_list;
+
+       shared_ptr<_Node> _M_node;
+       void* _M_reserved = nullptr;
+      };
+
+      /** Access the current `tzdb` at the front of the list.
+       *
+       * This returns a reference to the same object as `chrono::get_tzdb()`.
+       *
+       * @returns A reference to the current tzdb object.
+       * @since C++20
+       */
+      const tzdb& front() const noexcept;
+
+      /** Remove the tzdb object _after_ the one the iterator refers to.
+       *
+       * Calling this function concurently with any of `front()`, `begin()`,
+       * or `end()` does not cause a data race, but in general this function
+       * is not thread-safe. The behaviour may be undefined if erasing an
+       * element from the list while another thread is calling the same
+       * function, or incrementing an iterator into the list, or accessing
+       * the element being erased (unless it is accessed through an iterator).
+       *
+       * @param __p A dereferenceable iterator.
+       * @returns An iterator the element after the one that was erased
+       *          (or `end()` if there is no such element).
+       * @since C++20
+       */
+      const_iterator erase_after(const_iterator __p);
+
+      const_iterator begin() const noexcept;
+      const_iterator end() const noexcept { return {}; }
+      const_iterator cbegin() const noexcept { return begin(); }
+      const_iterator cend() const noexcept { return end(); }
+
+    private:
+      constexpr explicit tzdb_list(nullptr_t);
+
+      friend tzdb_list& get_tzdb_list();
+      friend const tzdb& get_tzdb();
+      friend const tzdb& reload_tzdb();
+      friend struct tzdb;
+      friend class leap_second;
+      friend struct time_zone::_Impl;
+      friend class time_zone_link;
+    };
+
     class time_zone_link
     {
     public:
@@ -2609,7 +2801,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     private:
       friend const tzdb& reload_tzdb();
-      // TODO unspecified additional constructors
+      friend struct tzdb_list::_Node;
+
+      explicit time_zone_link(nullptr_t) { }
+
       string _M_name;
       string _M_target;
     };
@@ -2620,6 +2815,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       leap_second(const leap_second&) = default;
       leap_second& operator=(const leap_second&) = default;
 
+      [[nodiscard]]
       constexpr sys_seconds
       date() const noexcept
       {
@@ -2628,6 +2824,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        return sys_seconds(-_M_s);
       }
 
+      [[nodiscard]]
       constexpr seconds
       value() const noexcept
       {
@@ -2638,80 +2835,83 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       // This can be defaulted because the database will never contain two
       // leap_second objects with the same date but different signs.
-      friend constexpr bool
+      [[nodiscard]] friend constexpr bool
       operator==(const leap_second&, const leap_second&) noexcept = default;
 
-      friend constexpr strong_ordering
+      [[nodiscard]] friend constexpr strong_ordering
       operator<=>(const leap_second& __x, const leap_second& __y) noexcept
       { return __x.date() <=> __y.date(); }
 
       template<typename _Duration>
-       friend constexpr bool
+       [[nodiscard]] friend constexpr bool
        operator==(const leap_second& __x,
                   const sys_time<_Duration>& __y) noexcept
        { return __x.date() == __y; }
 
       template<typename _Duration>
-       friend constexpr bool
+       [[nodiscard]] friend constexpr bool
        operator<(const leap_second& __x,
                  const sys_time<_Duration>& __y) noexcept
        { return __x.date() < __y; }
 
       template<typename _Duration>
-       friend constexpr bool
+       [[nodiscard]] friend constexpr bool
        operator<(const sys_time<_Duration>& __x,
                  const leap_second& __y) noexcept
        { return __x < __y.date(); }
 
       template<typename _Duration>
-       friend constexpr bool
+       [[nodiscard]] friend constexpr bool
        operator>(const leap_second& __x,
                  const sys_time<_Duration>& __y) noexcept
        { return __y < __x.date(); }
 
       template<typename _Duration>
-       friend constexpr bool
+       [[nodiscard]] friend constexpr bool
        operator>(const sys_time<_Duration>& __x,
                  const leap_second& __y) noexcept
        { return __y.date() < __x; }
 
       template<typename _Duration>
-       friend constexpr bool
+       [[nodiscard]] friend constexpr bool
        operator<=(const leap_second& __x,
-                 const sys_time<_Duration>& __y) noexcept
+                  const sys_time<_Duration>& __y) noexcept
        { return !(__y < __x.date()); }
 
       template<typename _Duration>
-       friend constexpr bool
+       [[nodiscard]] friend constexpr bool
        operator<=(const sys_time<_Duration>& __x,
-                 const leap_second& __y) noexcept
+                  const leap_second& __y) noexcept
        { return !(__y.date() < __x); }
 
       template<typename _Duration>
-       friend constexpr bool
+       [[nodiscard]] friend constexpr bool
        operator>=(const leap_second& __x,
-                 const sys_time<_Duration>& __y) noexcept
+                  const sys_time<_Duration>& __y) noexcept
        { return !(__x.date() < __y); }
 
       template<typename _Duration>
-       friend constexpr bool
+       [[nodiscard]] friend constexpr bool
        operator>=(const sys_time<_Duration>& __x,
-                 const leap_second& __y) noexcept
+                  const leap_second& __y) noexcept
        { return !(__x < __y.date()); }
 
       template<three_way_comparable_with<seconds> _Duration>
-       friend constexpr auto
+       [[nodiscard]] friend constexpr auto
        operator<=>(const leap_second& __x,
-                  const sys_time<_Duration>& __y) noexcept
+                   const sys_time<_Duration>& __y) noexcept
        { return __x.date() <=> __y; }
 
     private:
       explicit leap_second(seconds::rep __s) : _M_s(__s) { }
 
+      friend struct tzdb_list::_Node;
+
       friend const tzdb& reload_tzdb();
-      template<typename _Dur>
+
+      template<typename _Duration>
        friend leap_second_info
-       get_leap_second_info(const utc_time<_Dur>&);
+       get_leap_second_info(const utc_time<_Duration>&);
 
       seconds _M_s; // == date().time_since_epoch() * value().count()
     };
@@ -2733,9 +2933,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct tzdb
     {
       string version;
-      vector<time_zone> zones;
-      vector<time_zone_link> links;
-      vector<leap_second> leap_seconds;
+      _GLIBCXX_STD_C::vector<time_zone> zones;
+      _GLIBCXX_STD_C::vector<time_zone_link> links;
+      _GLIBCXX_STD_C::vector<leap_second> leap_seconds;
 
       const time_zone*
       locate_zone(string_view __tz_name) const;
@@ -2745,146 +2945,353 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     private:
       friend const tzdb& reload_tzdb();
-
-      struct _Rule;
-      vector<_Rule> _M_rules;
+      friend class time_zone;
+      friend struct tzdb_list::_Node;
     };
 
-    class tzdb_list
-    {
-      struct _Node;
-    public:
-      tzdb_list(const tzdb_list&) = delete;
-      tzdb_list& operator=(const tzdb_list&) = delete;
+    tzdb_list& get_tzdb_list();
+    const tzdb& get_tzdb();
 
-      class const_iterator
+    const tzdb& reload_tzdb();
+    string remote_version();
+
+    template<typename _Duration, typename _TimeZonePtr = const time_zone*>
+      class zoned_time
       {
+       static_assert(__is_duration_v<_Duration>);
+
+       using _Traits = zoned_traits<_TimeZonePtr>;
+
+       // Every constructor that accepts a string_view as its first parameter
+       // does not participate in class template argument deduction.
+       using string_view = type_identity_t<std::string_view>;
+
       public:
-       using value_type        = tzdb;
-       using reference         = const tzdb&;
-       using pointer           = const tzdb*;
-       using difference_type   = ptrdiff_t;
-       using iterator_category = forward_iterator_tag;
+       using duration = common_type_t<_Duration, seconds>;
 
-       constexpr const_iterator() = default;
-       const_iterator(const const_iterator&) = default;
-       const_iterator(const_iterator&&) = default;
-       const_iterator& operator=(const const_iterator&) = default;
-       const_iterator& operator=(const_iterator&&) = default;
+       zoned_time() requires requires { _Traits::default_zone(); }
+       { }
 
-       reference operator*() const noexcept;
-       pointer operator->() const noexcept { return &**this; }
-       const_iterator& operator++();
-       const_iterator operator++(int);
+       zoned_time(const zoned_time&) = default;
+       zoned_time& operator=(const zoned_time&) = default;
 
-       bool operator==(const const_iterator&) const noexcept = default;
+       zoned_time(const sys_time<_Duration>& __st)
+         requires requires { _Traits::default_zone(); }
+       : _M_tp(__st)
+       { }
 
-      private:
-       explicit const_iterator(const shared_ptr<_Node>&) noexcept;
+       explicit
+       zoned_time(_TimeZonePtr __z) : _M_zone(std::move(__z)) { }
 
-       shared_ptr<_Node> _M_node;
-       void* _M_reserved = nullptr;
-      };
+       explicit
+       zoned_time(string_view __name)
+         requires requires {
+           _TimeZonePtr{_Traits::locate_zone(std::string_view{})};
+         }
+       : _M_zone(_Traits::locate_zone(__name))
+       { }
 
-      // TODO const tzdb& front() const noexcept;
+       template<typename _Duration2>
+         zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& __zt)
+         requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
+         : _M_zone(__zt._M_zone), _M_tp(__zt._M_tp)
+         { }
 
-      const_iterator erase_after(const_iterator);
+       zoned_time(_TimeZonePtr __z, const sys_time<_Duration>& __st)
+       : _M_zone(std::move(__z)), _M_tp(__st)
+       { }
 
-      const_iterator begin() const noexcept;
-      const_iterator end() const noexcept { return {}; }
-      const_iterator cbegin() const noexcept { return begin(); }
-      const_iterator cend() const noexcept { return end(); }
+       zoned_time(string_view __name, const sys_time<_Duration>& __st)
+       : zoned_time(_Traits::locate_zone(__name), __st)
+       { }
 
-    private:
-      constexpr explicit tzdb_list(nullptr_t);
+       zoned_time(_TimeZonePtr __z, const local_time<_Duration>& __tp)
+       requires requires {
+         { __z->to_sys(__tp) } -> convertible_to<sys_time<_Duration>>;
+       }
+       : _M_zone(std::move(__z)), _M_tp(_M_zone->to_sys(__tp))
+       { }
 
-      friend const tzdb_list& get_tzdb_list();
-      friend const tzdb& get_tzdb();
-      friend const tzdb& reload_tzdb();
+       zoned_time(string_view __name, const local_time<_Duration>& __tp)
+       requires requires (_TimeZonePtr __z) {
+         { _Traits::locate_zone(__name) } -> convertible_to<_TimeZonePtr>;
+         { __z->to_sys(__tp) } -> convertible_to<sys_time<_Duration>>;
+       }
+       : zoned_time(_Traits::locate_zone(__name), __tp)
+       { }
 
-      static _Node* _S_head;
-      static shared_ptr<_Node> _S_head_owner;
-    };
+       zoned_time(_TimeZonePtr __z, const local_time<_Duration>& __tp,
+                  choose __c)
+       requires requires {
+         { __z->to_sys(__tp, __c) } -> convertible_to<sys_time<_Duration>>;
+       }
+       : _M_zone(std::move(__z)), _M_tp(_M_zone->to_sys(__tp, __c))
+       { }
+
+       zoned_time(string_view __name, const local_time<_Duration>& __tp,
+                  choose __c)
+       requires requires (_TimeZonePtr __z) {
+         { _Traits::locate_zone(__name) } -> convertible_to<_TimeZonePtr>;
+         { __z->to_sys(__tp, __c) } -> convertible_to<sys_time<_Duration>>;
+       }
+       : _M_zone(_Traits::locate_zone(__name)),
+         _M_tp(_M_zone->to_sys(__tp, __c))
+       { }
+
+       template<typename _Duration2, typename _TimeZonePtr2>
+         zoned_time(_TimeZonePtr __z,
+                    const zoned_time<_Duration2, _TimeZonePtr2>& __zt)
+         requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
+         : _M_zone(__z), _M_tp(__zt._M_tp)
+         { }
+
+       template<typename _Duration2, typename _TimeZonePtr2>
+         zoned_time(_TimeZonePtr __z,
+                    const zoned_time<_Duration2, _TimeZonePtr2>& __zt,
+                    choose)
+         requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
+         : _M_zone(__z), _M_tp(__zt._M_tp)
+         { }
+
+       template<typename _Duration2, typename _TimeZonePtr2>
+         zoned_time(string_view __name,
+                    const zoned_time<_Duration2, _TimeZonePtr2>& __zt)
+         requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
+         && requires {
+           { _Traits::locate_zone(__name) } -> convertible_to<_TimeZonePtr>;
+         }
+         : _M_zone(_Traits::locate_zone(__name)), _M_tp(__zt._M_tp)
+         { }
+
+       template<typename _Duration2, typename _TimeZonePtr2>
+         zoned_time(string_view __name,
+                    const zoned_time<_Duration2, _TimeZonePtr2>& __zt,
+                    choose)
+         requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
+         && requires {
+           { _Traits::locate_zone(__name) } -> convertible_to<_TimeZonePtr>;
+         }
+         : _M_zone(_Traits::locate_zone(__name)), _M_tp(__zt._M_tp)
+         { }
 
-    // TODO
-    // const tzdb_list& get_tzdb_list();
-    // const tzdb& get_tzdb();
+       zoned_time&
+       operator=(const sys_time<_Duration>& __st)
+       {
+         _M_tp = __st;
+         return *this;
+       }
 
-    // const tzdb& reload_tzdb();
-    // string remove_version();
+       zoned_time&
+       operator=(const local_time<_Duration>& __lt)
+       {
+         _M_tp = _M_zone->to_sys(__lt);
+         return *this;
+       }
 
-    template<typename _Duration, typename _TimeZonePtr = const time_zone*>
-      class zoned_time; // TODO
+       [[nodiscard]]
+       operator sys_time<duration>() const { return _M_tp; }
+
+       [[nodiscard]]
+       explicit operator local_time<duration>() const
+       { return get_local_time(); }
+
+       [[nodiscard]]
+       _TimeZonePtr
+       get_time_zone() const
+       { return _M_zone; }
+
+       [[nodiscard]]
+       local_time<duration>
+       get_local_time() const
+       { return _M_zone->to_local(_M_tp); }
+
+       [[nodiscard]]
+       sys_time<duration>
+       get_sys_time() const
+       { return _M_tp; }
+
+       [[nodiscard]]
+       sys_info
+       get_info() const
+       { return _M_zone->get_info(_M_tp); }
+
+       [[nodiscard]] friend bool
+       operator==(const zoned_time&, const zoned_time&) = default;
+
+      private:
+       _TimeZonePtr       _M_zone{ _Traits::default_zone() };
+       sys_time<duration> _M_tp{};
+
+       template<typename _Duration2, typename _TimeZonePtr2>
+         friend class zoned_time;
+      };
+
+    zoned_time() -> zoned_time<seconds>;
+
+    template<typename _Duration>
+    zoned_time(sys_time<_Duration>)
+      -> zoned_time<common_type_t<_Duration, seconds>>;
+
+  /// @cond undocumented
+  template<typename _TimeZonePtrOrName>
+    using __time_zone_representation
+      = __conditional_t<is_convertible_v<_TimeZonePtrOrName, string_view>,
+                       const time_zone*,
+                       remove_cvref_t<_TimeZonePtrOrName>>;
+  /// @endcond
+
+  template<typename _TimeZonePtrOrName>
+    zoned_time(_TimeZonePtrOrName&&)
+      -> zoned_time<seconds, __time_zone_representation<_TimeZonePtrOrName>>;
+
+  template<typename _TimeZonePtrOrName, typename _Duration>
+    zoned_time(_TimeZonePtrOrName&&, sys_time<_Duration>)
+      -> zoned_time<common_type_t<_Duration, seconds>,
+                    __time_zone_representation<_TimeZonePtrOrName>>;
+
+  template<typename _TimeZonePtrOrName, typename _Duration>
+    zoned_time(_TimeZonePtrOrName&&, local_time<_Duration>,
+               choose = choose::earliest)
+      -> zoned_time<common_type_t<_Duration, seconds>,
+                    __time_zone_representation<_TimeZonePtrOrName>>;
+
+  template<typename _Duration, typename _TimeZonePtrOrName,
+          typename _TimeZonePtr2>
+    zoned_time(_TimeZonePtrOrName&&, zoned_time<_Duration, _TimeZonePtr2>,
+               choose = choose::earliest)
+      -> zoned_time<common_type_t<_Duration, seconds>,
+                    __time_zone_representation<_TimeZonePtrOrName>>;
+
+  template<typename _Dur1, typename _TZPtr1, typename _Dur2, typename _TZPtr2>
+    [[nodiscard]]
+    inline bool
+    operator==(const zoned_time<_Dur1, _TZPtr1>& __x,
+              const zoned_time<_Dur2, _TZPtr2>& __y)
+    {
+      return __x.get_time_zone() == __y.get_time_zone()
+              && __x.get_sys_time() == __y.get_sys_time();
+    }
 
     using zoned_seconds = zoned_time<seconds>;
+#endif // _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
+
+namespace __detail
+{
+    inline leap_second_info
+    __get_leap_second_info(sys_seconds __ss, bool __is_utc)
+    {
+      if (__ss < sys_seconds{}) [[unlikely]]
+       return {};
+
+      const seconds::rep __leaps[] {
+         78796800, // 1 Jul 1972
+         94694400, // 1 Jan 1973
+        126230400, // 1 Jan 1974
+        157766400, // 1 Jan 1975
+        189302400, // 1 Jan 1976
+        220924800, // 1 Jan 1977
+        252460800, // 1 Jan 1978
+        283996800, // 1 Jan 1979
+        315532800, // 1 Jan 1980
+        362793600, // 1 Jul 1981
+        394329600, // 1 Jul 1982
+        425865600, // 1 Jul 1983
+        489024000, // 1 Jul 1985
+        567993600, // 1 Jan 1988
+        631152000, // 1 Jan 1990
+        662688000, // 1 Jan 1991
+        709948800, // 1 Jul 1992
+        741484800, // 1 Jul 1993
+        773020800, // 1 Jul 1994
+        820454400, // 1 Jan 1996
+        867715200, // 1 Jul 1997
+        915148800, // 1 Jan 1999
+       1136073600, // 1 Jan 2006
+       1230768000, // 1 Jan 2009
+       1341100800, // 1 Jul 2012
+       1435708800, // 1 Jul 2015
+       1483228800, // 1 Jan 2017
+      };
+      // The list above is known to be valid until (at least) this date
+      // and only contains positive leap seconds.
+      const sys_seconds __expires(1703721600s); // 2023-12-28 00:00:00 UTC
+
+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
+      if (__ss > __expires)
+       {
+         // Use updated leap_seconds from tzdb.
+         size_t __n = std::size(__leaps);
+
+         auto __db = get_tzdb_list().begin();
+         auto __first = __db->leap_seconds.begin() + __n;
+         auto __last = __db->leap_seconds.end();
+         auto __pos = std::upper_bound(__first, __last, __ss);
+         seconds __elapsed(__n);
+         for (auto __i = __first; __i != __pos; ++__i)
+           __elapsed += __i->value();
+
+         if (__is_utc)
+           {
+             // Convert utc_time to sys_time:
+             __ss -= __elapsed;
+             // See if that sys_time is before (or during) previous leap sec:
+             if (__pos != __first && __ss < __pos[-1])
+               {
+                 if ((__ss + 1s) >= __pos[-1])
+                   return {true, __elapsed};
+                 __elapsed -= __pos[-1].value();
+               }
+           }
+         return {false, __elapsed};
+       }
+      else
+#endif
+       {
+         seconds::rep __s = __ss.time_since_epoch().count();
+         const seconds::rep* __first = std::begin(__leaps);
+         const seconds::rep* __last = std::end(__leaps);
+
+         // Don't bother searching the list if we're after the last one.
+         if (__s > (__last[-1] + (__last - __first) + 1))
+           return { false, seconds(__last - __first) };
+
+         auto __pos = std::upper_bound(__first, __last, __s);
+         seconds __elapsed{__pos - __first};
+         if (__is_utc)
+           {
+             // Convert utc_time to sys_time:
+             __s -= __elapsed.count();
+             // See if that sys_time is before (or during) previous leap sec:
+             if (__pos != __first && __s < __pos[-1])
+               {
+                 if ((__s + 1) >= __pos[-1])
+                   return {true, __elapsed};
+                 --__elapsed;
+               }
+           }
+         return {false, __elapsed};
+       }
+    }
+} // namespace __detail
 
     template<typename _Duration>
-      leap_second_info
+      [[nodiscard]]
+      inline leap_second_info
       get_leap_second_info(const utc_time<_Duration>& __ut)
       {
-       if constexpr (is_same_v<_Duration, seconds>)
-         {
-           const seconds::rep __leaps[] {
-               78796800, // 1 Jul 1972
-               94694400, // 1 Jan 1973
-              126230400, // 1 Jan 1974
-              157766400, // 1 Jan 1975
-              189302400, // 1 Jan 1976
-              220924800, // 1 Jan 1977
-              252460800, // 1 Jan 1978
-              283996800, // 1 Jan 1979
-              315532800, // 1 Jan 1980
-              362793600, // 1 Jul 1981
-              394329600, // 1 Jul 1982
-              425865600, // 1 Jul 1983
-              489024000, // 1 Jul 1985
-              567993600, // 1 Jan 1988
-              631152000, // 1 Jan 1990
-              662688000, // 1 Jan 1991
-              709948800, // 1 Jul 1992
-              741484800, // 1 Jul 1993
-              773020800, // 1 Jul 1994
-              820454400, // 1 Jan 1996
-              867715200, // 1 Jul 1997
-              915148800, // 1 Jan 1999
-             1136073600, // 1 Jan 2006
-             1230768000, // 1 Jan 2009
-             1341100800, // 1 Jul 2012
-             1435708800, // 1 Jul 2015
-             1483228800, // 1 Jan 2017
-           };
-           // The list above is known to be valid until 2023-06-28 00:00:00 UTC
-           const seconds::rep __expires = 1687910400;
-           const seconds::rep __s = __ut.time_since_epoch().count();
-
-           const seconds::rep* __first = std::begin(__leaps);
-           const seconds::rep* __last = std::end(__leaps);
-
-           if (__s > __expires)
-             {
-               // TODO: use updated leap_seconds from tzdb
-#if 0
-               auto __db = get_tzdb_list().begin();
-               __first = __db->leap_seconds.data();
-               __last = __first + __db->leap_seconds.size();
-#endif
-             }
-
-           // Don't bother searching the list if we're after the last one.
-           if (__s > __last[-1])
-             return { false, seconds(__last - __first) };
+       auto __s = chrono::duration_cast<seconds>(__ut.time_since_epoch());
+       return __detail::__get_leap_second_info(sys_seconds(__s), true);
+      }
 
-           auto __pos = std::upper_bound(__first, __last, __s);
-           return {
-             __pos != begin(__leaps) && __pos[-1] == __s,
-             seconds{__pos - __first}
-           };
-         }
-       else
-         {
-           auto __s = chrono::time_point_cast<seconds>(__ut);
-           return chrono::get_leap_second_info(__s);
-         }
+    template<typename _Duration>
+      [[nodiscard]]
+      inline utc_time<common_type_t<_Duration, seconds>>
+      utc_clock::from_sys(const sys_time<_Duration>& __t)
+      {
+       using _CDur = common_type_t<_Duration, seconds>;
+       auto __s = chrono::time_point_cast<seconds>(__t);
+       const auto __li = __detail::__get_leap_second_info(__s, false);
+       return utc_time<_CDur>{__t.time_since_epoch()} + __li.elapsed;
       }
 
     /// @} group chrono
@@ -2915,106 +3322,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     /// @}
   } // inline namespace chrono_literals
   } // inline namespace literals
-
-  namespace chrono
-  {
-    /// @addtogroup chrono
-    /// @{
-
-    /// @cond undocumented
-    namespace __detail
-    {
-      template<typename _Period>
-       const char*
-       __units_suffix_misc(char* __buf, size_t __n) noexcept
-       {
-         namespace __tc = std::__detail;
-         char* __p = __buf;
-         __p[0] = '[';
-         unsigned __nlen = __tc::__to_chars_len((uintmax_t)_Period::num);
-         __tc::__to_chars_10_impl(__p + 1, __nlen, (uintmax_t)_Period::num);
-         __p += 1 + __nlen;
-         if constexpr (_Period::den != 1)
-           {
-             __p[0] = '/';
-             unsigned __dlen = __tc::__to_chars_len((uintmax_t)_Period::den);
-             __tc::__to_chars_10_impl(__p + 1, __dlen, (uintmax_t)_Period::den);
-             __p += 1 + __dlen;
-           }
-         __p[0] = ']';
-         __p[1] = 's';
-         __p[2] = '\0';
-         return __buf;
-       }
-
-      template<typename _Period, typename _CharT>
-       auto
-       __units_suffix(char* __buf, size_t __n) noexcept
-       {
-#define _GLIBCXX_UNITS_SUFFIX(period, suffix) \
-       if constexpr (is_same_v<_Period, period>)       \
-         {                                             \
-           if constexpr (is_same_v<_CharT, wchar_t>)   \
-             return L##suffix;                         \
-           else                                        \
-             return suffix;                            \
-         }                                             \
-       else
-
-         _GLIBCXX_UNITS_SUFFIX(atto, "as")
-         _GLIBCXX_UNITS_SUFFIX(femto, "fs")
-         _GLIBCXX_UNITS_SUFFIX(pico, "ps")
-         _GLIBCXX_UNITS_SUFFIX(nano, "ns")
-         _GLIBCXX_UNITS_SUFFIX(micro, "\u00b5s")
-         _GLIBCXX_UNITS_SUFFIX(milli, "ms")
-         _GLIBCXX_UNITS_SUFFIX(centi, "cs")
-         _GLIBCXX_UNITS_SUFFIX(deci, "ds")
-         _GLIBCXX_UNITS_SUFFIX(ratio<1>, "s")
-         _GLIBCXX_UNITS_SUFFIX(deca, "das")
-         _GLIBCXX_UNITS_SUFFIX(hecto, "hs")
-         _GLIBCXX_UNITS_SUFFIX(kilo, "ks")
-         _GLIBCXX_UNITS_SUFFIX(mega, "Ms")
-         _GLIBCXX_UNITS_SUFFIX(giga, "Gs")
-         _GLIBCXX_UNITS_SUFFIX(tera, "Ts")
-         _GLIBCXX_UNITS_SUFFIX(tera, "Ts")
-         _GLIBCXX_UNITS_SUFFIX(peta, "Ps")
-         _GLIBCXX_UNITS_SUFFIX(exa, "Es")
-         _GLIBCXX_UNITS_SUFFIX(ratio<60>, "min")
-         _GLIBCXX_UNITS_SUFFIX(ratio<3600>, "h")
-         _GLIBCXX_UNITS_SUFFIX(ratio<86400>, "d")
-#undef _GLIBCXX_UNITS_SUFFIX
-         return __detail::__units_suffix_misc<_Period>(__buf, __n);
-       }
-    } // namespace __detail
-    /// @endcond
-
-    template<typename _CharT, typename _Traits,
-            typename _Rep, typename _Period>
-      inline basic_ostream<_CharT, _Traits>&
-      operator<<(std::basic_ostream<_CharT, _Traits>& __os,
-               const duration<_Rep, _Period>& __d)
-      {
-       using period = typename _Period::type;
-       char __buf[sizeof("[/]s") + 2 * numeric_limits<intmax_t>::digits10];
-       std::basic_ostringstream<_CharT, _Traits> __s;
-       __s.flags(__os.flags());
-       __s.imbue(__os.getloc());
-       __s.precision(__os.precision());
-       __s << __d.count();
-       __s << __detail::__units_suffix<period, _CharT>(__buf, sizeof(__buf));
-       __os << std::move(__s).str();
-       return __os;
-      }
-
-    // TODO: from_stream for duration
-
-    /// @} group chrono
-  } // namespace chrono
 #endif // C++20
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
+#if __cplusplus >= 202002L
+# include <bits/chrono_io.h>
+#endif
+
 #endif // C++11
 
 #endif //_GLIBCXX_CHRONO