Tested x86_64-linux and powerpc64le-linux. Pushed to trunk.

-- >8 --

Also add the basic types for timezones, without the non-inline
definitions needed to actually use them.

The get_leap_second_info function currently uses a hardcoded list of
leap seconds, correct as of the end of 2022. That needs to be replaced
with a dynamically generated list read from the system tzdata. That will
be done in a later patch.

libstdc++-v3/ChangeLog:

        * include/std/chrono (utc_clock, tai_clock, gps_clock): Define.
        (clock_time_conversion, clock_cast): Define.
        (sys_info, local_info): Define structs for timezone information.
        (nonexistent_local_time, ambiguous_local_time): Define
        exceptions for invalid times.
        (time_zone, time_zone_link, leap_second, zoned_traits, tzdb)
        (tzdb_list): Define classes representing time zones.
        (get_leap_second_info): Define new function returning leap
        second offset for a given time point.
        * testsuite/std/time/clock/gps/1.cc: New test.
        * testsuite/std/time/clock/tai/1.cc: New test.
        * testsuite/std/time/clock/utc/1.cc: New test.
---
 libstdc++-v3/include/std/chrono               | 744 +++++++++++++++++-
 .../testsuite/std/time/clock/gps/1.cc         |  38 +
 .../testsuite/std/time/clock/tai/1.cc         |  41 +
 .../testsuite/std/time/clock/utc/1.cc         |  24 +
 4 files changed, 844 insertions(+), 3 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/time/clock/gps/1.cc
 create mode 100644 libstdc++-v3/testsuite/std/time/clock/tai/1.cc
 create mode 100644 libstdc++-v3/testsuite/std/time/clock/utc/1.cc

diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono
index c0c3a679609..90b73f8198e 100644
--- a/libstdc++-v3/include/std/chrono
+++ b/libstdc++-v3/include/std/chrono
@@ -39,9 +39,15 @@
 #else
 
 #include <bits/chrono.h>
-#if __cplusplus > 201703L
-# include <sstream> // ostringstream
-# include <bits/charconv.h>
+
+#if __cplusplus >= 202002L
+# 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/shared_ptr.h>
+# include <bits/unique_ptr.h>
 #endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
@@ -102,6 +108,357 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       seconds elapsed;
     };
 
+    template<typename _Duration>
+      leap_second_info
+      get_leap_second_info(const utc_time<_Duration>& __ut);
+
+    /** A clock that measures Universal Coordinated Time (UTC).
+     *
+     * The epoch is 1970-01-01 00:00:00.
+     *
+     * @since C++20
+     */
+    class utc_clock
+    {
+    public:
+      using rep                       = system_clock::rep;
+      using period                    = system_clock::period;
+      using duration                  = chrono::duration<rep, period>;
+      using time_point                = chrono::time_point<utc_clock>;
+      static constexpr bool is_steady = false;
+
+      static time_point
+      now()
+      { return from_sys(system_clock::now()); }
+
+      template<typename _Duration>
+       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}};
+         if (__li.is_leap_second)
+           __s = chrono::floor<seconds>(__s) + seconds{1} - _CDur{1};
+         return __s;
+       }
+
+      template<typename _Duration>
+       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};
+       }
+    };
+
+    /** A clock that measures International Atomic Time.
+     *
+     * The epoch is 1958-01-01 00:00:00.
+     *
+     * @since C++20
+     */
+    class tai_clock
+    {
+    public:
+      using rep                       = system_clock::rep;
+      using period                    = system_clock::period;
+      using duration                  = chrono::duration<rep, period>;
+      using time_point                = chrono::time_point<tai_clock>;
+      static constexpr bool is_steady = false; // XXX true for CLOCK_TAI?
+
+      // TODO move into lib, use CLOCK_TAI on linux, add extension point.
+      static time_point
+      now()
+      { return from_utc(utc_clock::now()); }
+
+      template<typename _Duration>
+       static utc_time<common_type_t<_Duration, seconds>>
+       to_utc(const tai_time<_Duration>& __t)
+       {
+         using _CDur = common_type_t<_Duration, seconds>;
+         return utc_time<_CDur>{__t.time_since_epoch()} - 378691210s;
+       }
+
+      template<typename _Duration>
+       static tai_time<common_type_t<_Duration, seconds>>
+       from_utc(const utc_time<_Duration>& __t)
+       {
+         using _CDur = common_type_t<_Duration, seconds>;
+         return tai_time<_CDur>{__t.time_since_epoch()} + 378691210s;
+       }
+    };
+
+    /** A clock that measures GPS time.
+     *
+     * The epoch is 1980-01-06 00:00:00.
+     *
+     * @since C++20
+     */
+    class gps_clock
+    {
+    public:
+      using rep                       = system_clock::rep;
+      using period                    = system_clock::period;
+      using duration                  = chrono::duration<rep, period>;
+      using time_point                = chrono::time_point<gps_clock>;
+      static constexpr bool is_steady = false; // XXX
+
+      // TODO move into lib, add extension point.
+      static time_point
+      now()
+      { return from_utc(utc_clock::now()); }
+
+      template<typename _Duration>
+       static utc_time<common_type_t<_Duration, seconds>>
+       to_utc(const gps_time<_Duration>& __t)
+       {
+         using _CDur = common_type_t<_Duration, seconds>;
+         return utc_time<_CDur>{__t.time_since_epoch()} + 315964809s;
+       }
+
+      template<typename _Duration>
+       static gps_time<common_type_t<_Duration, seconds>>
+       from_utc(const utc_time<_Duration>& __t)
+       {
+         using _CDur = common_type_t<_Duration, seconds>;
+         return gps_time<_CDur>{__t.time_since_epoch()} - 315964809s;
+       }
+    };
+
+
+    template<typename _DestClock, typename _SourceClock>
+      struct clock_time_conversion
+      { };
+
+    // Identity conversions
+
+    template<typename _Clock>
+      struct clock_time_conversion<_Clock, _Clock>
+      {
+       template<typename _Duration>
+         time_point<_Clock, _Duration>
+         operator()(const time_point<_Clock, _Duration>& __t) const
+         { return __t; }
+      };
+
+    template<>
+      struct clock_time_conversion<system_clock, system_clock>
+      {
+       template<typename _Duration>
+         sys_time<_Duration>
+         operator()(const sys_time<_Duration>& __t) const
+         { return __t; }
+      };
+
+    template<>
+      struct clock_time_conversion<utc_clock, utc_clock>
+      {
+       template<typename _Duration>
+         utc_time<_Duration>
+         operator()(const utc_time<_Duration>& __t) const
+         { return __t; }
+      };
+
+    // Conversions between system_clock and utc_clock
+
+    template<>
+      struct clock_time_conversion<utc_clock, system_clock>
+      {
+       template<typename _Duration>
+         utc_time<common_type_t<_Duration, seconds>>
+         operator()(const sys_time<_Duration>& __t) const
+         { return utc_clock::from_sys(__t); }
+      };
+
+    template<>
+      struct clock_time_conversion<system_clock, utc_clock>
+      {
+       template<typename _Duration>
+         sys_time<common_type_t<_Duration, seconds>>
+         operator()(const utc_time<_Duration>& __t) const
+         { return utc_clock::to_sys(__t); }
+      };
+
+    template<typename _Tp, typename _Clock>
+      inline constexpr bool __is_time_point_for_v = false;
+
+    template<typename _Clock, typename _Duration>
+      inline constexpr bool
+       __is_time_point_for_v<time_point<_Clock, _Duration>, _Clock> = true;
+
+    // Conversions between system_clock and other clocks
+
+    template<typename _SourceClock>
+      struct clock_time_conversion<system_clock, _SourceClock>
+      {
+       template<typename _Duration, typename _Src = _SourceClock>
+         auto
+         operator()(const time_point<_SourceClock, _Duration>& __t) const
+         -> decltype(_Src::to_sys(__t))
+         {
+           using _Ret = decltype(_SourceClock::to_sys(__t));
+           static_assert(__is_time_point_for_v<_Ret, system_clock>);
+           return _SourceClock::to_sys(__t);
+         }
+      };
+
+    template<typename _DestClock>
+      struct clock_time_conversion<_DestClock, system_clock>
+      {
+       template<typename _Duration, typename _Dest = _DestClock>
+         auto
+         operator()(const sys_time<_Duration>& __t) const
+         -> decltype(_Dest::from_sys(__t))
+         {
+           using _Ret = decltype(_DestClock::from_sys(__t));
+           static_assert(__is_time_point_for_v<_Ret, _DestClock>);
+           return _DestClock::from_sys(__t);
+         }
+      };
+
+    // Conversions between utc_clock and other clocks
+
+    template<typename _SourceClock>
+      struct clock_time_conversion<utc_clock, _SourceClock>
+      {
+       template<typename _Duration, typename _Src = _SourceClock>
+         auto
+         operator()(const time_point<_SourceClock, _Duration>& __t) const
+         -> decltype(_Src::to_utc(__t))
+         {
+           using _Ret = decltype(_SourceClock::to_utc(__t));
+           static_assert(__is_time_point_for_v<_Ret, utc_clock>);
+           return _SourceClock::to_utc(__t);
+         }
+      };
+
+    template<typename _DestClock>
+      struct clock_time_conversion<_DestClock, utc_clock>
+      {
+       template<typename _Duration, typename _Dest = _DestClock>
+         auto
+         operator()(const utc_time<_Duration>& __t) const
+         -> decltype(_Dest::from_utc(__t))
+         {
+           using _Ret = decltype(_DestClock::from_utc(__t));
+           static_assert(__is_time_point_for_v<_Ret, _DestClock>);
+           return _DestClock::from_utc(__t);
+         }
+      };
+
+    /// @cond undocumented
+    namespace __detail
+    {
+      template<typename _DestClock, typename _SourceClock, typename _Duration>
+       concept __clock_convs
+         = requires (const time_point<_SourceClock, _Duration>& __t) {
+           clock_time_conversion<_DestClock, _SourceClock>{}(__t);
+         };
+
+      template<typename _DestClock, typename _SourceClock, typename _Duration>
+       concept __clock_convs_sys
+         = requires (const time_point<_SourceClock, _Duration>& __t) {
+           clock_time_conversion<_DestClock, system_clock>{}(
+             clock_time_conversion<system_clock, _SourceClock>{}(__t));
+         };
+
+      template<typename _DestClock, typename _SourceClock, typename _Duration>
+       concept __clock_convs_utc
+         = requires (const time_point<_SourceClock, _Duration>& __t) {
+           clock_time_conversion<_DestClock, utc_clock>{}(
+             clock_time_conversion<utc_clock, _SourceClock>{}(__t));
+         };
+
+      template<typename _DestClock, typename _SourceClock, typename _Duration>
+       concept __clock_convs_sys_utc
+         = requires (const time_point<_SourceClock, _Duration>& __t) {
+           clock_time_conversion<_DestClock, utc_clock>{}(
+             clock_time_conversion<utc_clock, system_clock>{}(
+               clock_time_conversion<system_clock, _SourceClock>{}(__t)));
+         };
+
+      template<typename _DestClock, typename _SourceClock, typename _Duration>
+       concept __clock_convs_utc_sys
+         = requires (const time_point<_SourceClock, _Duration>& __t) {
+           clock_time_conversion<_DestClock, system_clock>{}(
+             clock_time_conversion<system_clock, utc_clock>{}(
+               clock_time_conversion<utc_clock, _SourceClock>{}(__t)));
+         };
+
+    } // namespace __detail
+    /// @endcond
+
+    /// Convert a time point to a different clock.
+    template<typename _DestClock, typename _SourceClock, typename _Duration>
+      inline auto
+      clock_cast(const time_point<_SourceClock, _Duration>& __t)
+      requires __detail::__clock_convs<_DestClock, _SourceClock, _Duration>
+       || __detail::__clock_convs_sys<_DestClock, _SourceClock, _Duration>
+       || __detail::__clock_convs_utc<_DestClock, _SourceClock, _Duration>
+       || __detail::__clock_convs_sys_utc<_DestClock, _SourceClock, _Duration>
+       || __detail::__clock_convs_utc_sys<_DestClock, _SourceClock, _Duration>
+      {
+       constexpr bool __direct
+        = __detail::__clock_convs<_DestClock, _SourceClock, _Duration>;
+       if constexpr (__direct)
+        {
+          return clock_time_conversion<_DestClock, _SourceClock>{}(__t);
+        }
+       else
+        {
+          constexpr bool __convert_via_sys_clock
+            = __detail::__clock_convs_sys<_DestClock, _SourceClock, _Duration>;
+          constexpr bool __convert_via_utc_clock
+            = __detail::__clock_convs_utc<_DestClock, _SourceClock, _Duration>;
+          if constexpr (__convert_via_sys_clock)
+            {
+              static_assert(!__convert_via_utc_clock,
+                "clock_cast requires a unique best conversion, but "
+                "conversion is possible via system_clock and also via"
+                "utc_clock");
+              return clock_time_conversion<_DestClock, system_clock>{}(
+                       clock_time_conversion<system_clock, 
_SourceClock>{}(__t));
+            }
+          else if constexpr (__convert_via_utc_clock)
+            {
+              return clock_time_conversion<_DestClock, utc_clock>{}(
+                       clock_time_conversion<utc_clock, _SourceClock>{}(__t));
+            }
+          else
+            {
+              constexpr bool __convert_via_sys_and_utc_clocks
+                = __detail::__clock_convs_sys_utc<_DestClock,
+                                                  _SourceClock,
+                                                  _Duration>;
+
+              if constexpr (__convert_via_sys_and_utc_clocks)
+                {
+                  constexpr bool __convert_via_utc_and_sys_clocks
+                    = __detail::__clock_convs_utc_sys<_DestClock,
+                                                      _SourceClock,
+                                                      _Duration>;
+                  static_assert(!__convert_via_utc_and_sys_clocks,
+                    "clock_cast requires a unique best conversion, but "
+                    "conversion is possible via system_clock followed by "
+                    "utc_clock, and also via utc_clock followed by "
+                    "system_clock");
+                  return clock_time_conversion<_DestClock, utc_clock>{}(
+                           clock_time_conversion<utc_clock, system_clock>{}(
+                             clock_time_conversion<system_clock, 
_SourceClock>{}(__t)));
+                }
+              else
+                {
+                  return clock_time_conversion<_DestClock, system_clock>{}(
+                           clock_time_conversion<system_clock, utc_clock>{}(
+                             clock_time_conversion<utc_clock, 
_SourceClock>{}(__t)));
+                }
+            }
+        }
+      }
+
     // CALENDRICAL TYPES
 
     // CLASS DECLARATIONS
@@ -2055,6 +2412,387 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
            return __h + 12h;
        }
     }
+
+    // C++20 [time.zones] Time zones
+
+    struct sys_info
+    {
+      sys_seconds begin;
+      sys_seconds end;
+      seconds offset;
+      minutes save;
+      string abbrev;
+    };
+
+    struct local_info
+    {
+      static constexpr int unique      = 0;
+      static constexpr int nonexistent = 1;
+      static constexpr int ambiguous   = 2;
+
+      int result;
+      sys_info first;
+      sys_info second;
+    };
+
+    class nonexistent_local_time : public runtime_error
+    {
+    public:
+      template<typename _Duration>
+       nonexistent_local_time(const local_time<_Duration>& __tp,
+                              const local_info& __i)
+       : runtime_error(_S_make_what_str(__tp, __i))
+       { __glibcxx_assert(__i.result == local_info::nonexistent); }
+
+    private:
+      template<typename _Duration> // TODO
+       static string
+       _S_make_what_str(const local_time<_Duration>&, const local_info&);
+    };
+
+    class ambiguous_local_time : public runtime_error
+    {
+    public:
+      template<typename _Duration>
+       ambiguous_local_time(const local_time<_Duration>& __tp,
+                              const local_info& __i)
+       : runtime_error(_S_make_what_str(__tp, __i))
+       { __glibcxx_assert(__i.result == local_info::nonexistent); }
+
+    private:
+      template<typename _Duration> // TODO
+       static string
+       _S_make_what_str(const local_time<_Duration>&, const local_info&);
+    };
+
+    enum class choose { earliest, latest };
+
+    class time_zone
+    {
+    public:
+      time_zone(time_zone&&) = default;
+      time_zone& operator=(time_zone&&) = default;
+
+      string_view name() const noexcept { return _M_name; }
+
+      template<typename _Duration>
+       sys_info
+       get_info(const sys_time<_Duration>& __st) const;
+
+      template<typename _Duration>
+       local_info
+       get_info(const local_time<_Duration>& __tp) const;
+
+      template<typename _Duration>
+       sys_time<common_type_t<_Duration, seconds>>
+       to_sys(const local_time<_Duration>& __tp) const;
+
+      template<typename _Duration>
+       sys_time<common_type_t<_Duration, seconds>>
+       to_sys(const local_time<_Duration>& __tp, choose __z) const;
+
+      template<typename _Duration>
+       local_time<common_type_t<_Duration, seconds>>
+       to_local(const sys_time<_Duration>& __tp) const;
+
+      friend bool
+      operator==(const time_zone& __x, const time_zone& __y) noexcept
+      { return __x.name() == __y.name(); }
+
+      friend strong_ordering
+      operator<=>(const time_zone& __x, const time_zone& __y) noexcept
+      { return __x.name() <=> __y.name(); }
+
+    private:
+      string _M_name;
+      struct _Impl;
+      unique_ptr<_Impl> _M_impl;
+    };
+
+    struct tzdb;
+    const time_zone* locate_zone(string_view __tz_name);
+    const time_zone* current_zone();
+
+    class time_zone_link
+    {
+    public:
+      time_zone_link(time_zone_link&&) = default;
+      time_zone_link& operator=(time_zone_link&&) = default;
+
+      string_view name() const noexcept { return _M_name; }
+      string_view target() const noexcept { return _M_target; }
+
+      friend bool
+      operator==(const time_zone_link& __x, const time_zone_link& __y) noexcept
+      { return __x.name() == __y.name(); }
+
+      friend strong_ordering
+      operator<=>(const time_zone_link& __x, const time_zone_link& __y) 
noexcept
+      { return __x.name() <=> __y.name(); }
+
+    private:
+      friend const tzdb& reload_tzdb();
+      // TODO unspecified additional constructors
+      string _M_name;
+      string _M_target;
+    };
+
+    class leap_second
+    {
+    public:
+      leap_second(const leap_second&) = default;
+      leap_second& operator=(const leap_second&) = default;
+
+      constexpr sys_seconds
+      date() const noexcept
+      {
+       if (_M_s >= _M_s.zero()) [[likely]]
+         return sys_seconds(_M_s);
+       return sys_seconds(-_M_s);
+      }
+
+      constexpr seconds
+      value() const noexcept
+      {
+       if (_M_s >= _M_s.zero()) [[likely]]
+         return seconds(1);
+       return seconds(-1);
+      }
+
+      // This can be defaulted because the database will never contain two
+      // leap_second objects with the same date but different signs.
+      friend constexpr bool
+      operator==(const leap_second&, const leap_second&) noexcept = default;
+
+      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
+       operator==(const leap_second& __x,
+                  const sys_time<_Duration>& __y) noexcept
+       { return __x.date() == __y; }
+
+      template<typename _Duration>
+       friend constexpr bool
+       operator<(const leap_second& __x,
+                 const sys_time<_Duration>& __y) noexcept
+       { return __x.date() < __y; }
+
+      template<typename _Duration>
+       friend constexpr bool
+       operator<(const sys_time<_Duration>& __x,
+                 const leap_second& __y) noexcept
+       { return __x < __y.date(); }
+
+      template<typename _Duration>
+       friend constexpr bool
+       operator>(const leap_second& __x,
+                 const sys_time<_Duration>& __y) noexcept
+       { return __y < __x.date(); }
+
+      template<typename _Duration>
+       friend constexpr bool
+       operator>(const sys_time<_Duration>& __x,
+                 const leap_second& __y) noexcept
+       { return __y.date() < __x; }
+
+      template<typename _Duration>
+       friend constexpr bool
+       operator<=(const leap_second& __x,
+                 const sys_time<_Duration>& __y) noexcept
+       { return !(__y < __x.date()); }
+
+      template<typename _Duration>
+       friend constexpr bool
+       operator<=(const sys_time<_Duration>& __x,
+                 const leap_second& __y) noexcept
+       { return !(__y.date() < __x); }
+
+      template<typename _Duration>
+       friend constexpr bool
+       operator>=(const leap_second& __x,
+                 const sys_time<_Duration>& __y) noexcept
+       { return !(__x.date() < __y); }
+
+      template<typename _Duration>
+       friend constexpr bool
+       operator>=(const sys_time<_Duration>& __x,
+                 const leap_second& __y) noexcept
+       { return !(__x < __y.date()); }
+
+      template<three_way_comparable_with<seconds> _Duration>
+       friend constexpr auto
+       operator<=>(const leap_second& __x,
+                  const sys_time<_Duration>& __y) noexcept
+       { return __x.date() <=> __y; }
+
+    private:
+      explicit leap_second(seconds::rep __s) : _M_s(__s) { }
+
+      friend const tzdb& reload_tzdb();
+      template<typename _Dur>
+       friend leap_second_info
+       get_leap_second_info(const utc_time<_Dur>&);
+
+      seconds _M_s; // == date().time_since_epoch() * value().count()
+    };
+
+    template<class _Tp> struct zoned_traits { };
+
+    template<>
+      struct zoned_traits<const time_zone*>
+      {
+       static const time_zone*
+       default_zone()
+       { return std::chrono::locate_zone("UTC"); }
+
+       static const time_zone*
+       locate_zone(string_view __name)
+       { return std::chrono::locate_zone(__name); }
+      };
+
+    struct tzdb
+    {
+      string version;
+      vector<time_zone> zones;
+      vector<time_zone_link> links;
+      vector<leap_second> leap_seconds;
+
+      const time_zone*
+      locate_zone(string_view __tz_name) const;
+
+      const time_zone*
+      current_zone() const;
+
+    private:
+      friend const tzdb& reload_tzdb();
+
+      struct _Rule;
+      vector<_Rule> _M_rules;
+    };
+
+    class tzdb_list
+    {
+      struct _Node;
+    public:
+      tzdb_list(const tzdb_list&) = delete;
+      tzdb_list& operator=(const tzdb_list&) = delete;
+
+      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;
+
+       shared_ptr<_Node> _M_node;
+       void* _M_reserved = nullptr;
+      };
+
+      // TODO const tzdb& front() const noexcept;
+
+      const_iterator erase_after(const_iterator);
+
+      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 const tzdb_list& get_tzdb_list();
+      friend const tzdb& get_tzdb();
+      friend const tzdb& reload_tzdb();
+
+      static _Node* _S_head;
+      static shared_ptr<_Node> _S_head_owner;
+    };
+
+    // TODO
+    // const tzdb_list& get_tzdb_list();
+    // const tzdb& get_tzdb();
+
+    // const tzdb& reload_tzdb();
+    // string remove_version();
+
+    template<typename _Duration, typename _TimeZonePtr = const time_zone*>
+      class zoned_time; // TODO
+
+    using zoned_seconds = zoned_time<seconds>;
+
+    template<typename _Duration>
+      leap_second_info
+      get_leap_second_info(const utc_time<_Duration>& __ut)
+      {
+       if constexpr (is_same_v<_Duration, seconds>)
+         {
+           // TODO move this function into the library and get leaps from tzdb.
+           vector<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
+           };
+
+           auto __s = __ut.time_since_epoch().count();
+           auto __pos = std::upper_bound(__leaps.begin(), __leaps.end(), __s);
+           return {
+             __pos != __leaps.begin() && __pos[-1] == __s,
+             seconds{__pos - __leaps.begin()}
+           };
+         }
+       else
+         {
+           auto __s = chrono::time_point_cast<seconds>(__ut);
+           return chrono::get_leap_second_info(__s);
+         }
+      }
+
     /// @} group chrono
 #endif // C++20
   } // namespace chrono
diff --git a/libstdc++-v3/testsuite/std/time/clock/gps/1.cc 
b/libstdc++-v3/testsuite/std/time/clock/gps/1.cc
new file mode 100644
index 00000000000..9403ee1ecca
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/clock/gps/1.cc
@@ -0,0 +1,38 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  using namespace std::chrono;
+
+  gps_seconds gps_epoch{0s};
+  utc_seconds gps_as_utc{sys_days{1980y/January/Sunday[1]}.time_since_epoch() 
+ 9s};
+
+  VERIFY( clock_cast<utc_clock>(gps_epoch) == gps_as_utc );
+  VERIFY( gps_epoch == clock_cast<gps_clock>(gps_as_utc) );
+
+  tai_seconds tai_epoch{0s};
+  VERIFY( clock_cast<tai_clock>(clock_cast<gps_clock>(tai_epoch)) == tai_epoch 
);
+}
+
+void
+test02()
+{
+  using namespace std::chrono;
+
+  sys_days d{2022y/November/12};
+  VERIFY( clock_cast<system_clock>(clock_cast<gps_clock>(d)) == d );
+  gps_seconds t(1234567s);
+  VERIFY( clock_cast<gps_clock>(clock_cast<system_clock>(t)) == t );
+  VERIFY( clock_cast<gps_clock>(clock_cast<utc_clock>(t)) == t );
+}
+
+int main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/time/clock/tai/1.cc 
b/libstdc++-v3/testsuite/std/time/clock/tai/1.cc
new file mode 100644
index 00000000000..9b36f023c68
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/clock/tai/1.cc
@@ -0,0 +1,41 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  using namespace std::chrono;
+
+  tai_seconds tai_epoch{0s};
+  utc_seconds tai_as_utc{sys_days{1958y/January/1}.time_since_epoch() - 10s};
+
+  VERIFY( clock_cast<utc_clock>(tai_epoch) == tai_as_utc );
+  VERIFY( tai_epoch == clock_cast<tai_clock>(tai_as_utc) );
+
+  sys_days y2k{2000y/January/1};
+  tai_seconds y2k_as_tai{clock_cast<tai_clock>(y2k)};
+  utc_seconds y2k_as_utc = utc_clock::from_sys(y2k);
+  VERIFY( clock_cast<utc_clock>(y2k_as_tai) == y2k_as_utc );
+  VERIFY( y2k_as_tai == clock_cast<tai_clock>(y2k_as_utc) );
+}
+
+void
+test02()
+{
+  using namespace std::chrono;
+
+  sys_days d{2022y/November/12};
+  VERIFY( clock_cast<system_clock>(clock_cast<tai_clock>(d)) == d );
+  tai_seconds t(1234567s);
+  VERIFY( clock_cast<tai_clock>(clock_cast<system_clock>(t)) == t );
+  VERIFY( clock_cast<tai_clock>(clock_cast<utc_clock>(t)) == t );
+}
+
+int main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/time/clock/utc/1.cc 
b/libstdc++-v3/testsuite/std/time/clock/utc/1.cc
new file mode 100644
index 00000000000..eef5f3c3a48
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/clock/utc/1.cc
@@ -0,0 +1,24 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+#include <chrono>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  using namespace std::chrono;
+
+  auto epoch = sys_seconds{sys_days{1970y/January/1}};
+  auto utc_epoch = clock_cast<utc_clock>(epoch);
+  VERIFY( utc_epoch.time_since_epoch() == 0s );
+
+  auto y2k = sys_seconds{sys_days{2000y/January/1}};
+  auto utc_y2k = clock_cast<utc_clock>(y2k);
+  VERIFY( utc_y2k.time_since_epoch() == 946'684'822s );
+}
+
+int main()
+{
+  test01();
+}
-- 
2.38.1

Reply via email to