[libcxx-commits] [libcxx] [libc++][chrono] implements GPS clock. (PR #125921)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Thu Feb 6 09:51:34 PST 2025


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

>From 7fbba32910a44c3c8b665cc77040ade7a997ce49 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Tue, 4 Feb 2025 20:22:40 +0100
Subject: [PATCH] [libc++][chrono] implements GPS clock.

Completes:
- LWG3359 <chrono> leap second support should allow for negative leap seconds

Implements parts of:
- P0355 Extending <chrono> to Calendars and Time Zones
- P1361 Integration of chrono with text formatting

NOTE The original version of this patch was written before finishing the
tzdb formatters so need to review the status of the "parts of papers"
are they still part of are complete after this patch?
---
 libcxx/docs/Status/FormatPaper.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__chrono/convert_to_tm.h       |   6 +
 libcxx/include/__chrono/formatter.h           |  14 +
 libcxx/include/__chrono/gps_clock.h           |  85 ++
 libcxx/include/__chrono/ostream.h             |   7 +
 libcxx/include/chrono                         |  29 +
 libcxx/include/module.modulemap               |   4 +
 libcxx/modules/std/chrono.inc                 |   2 -
 .../diagnostics/chrono.nodiscard.verify.cpp   |  11 +
 .../assert.from_utc.pass.cpp                  |  73 ++
 .../assert.to_utc.pass.cpp                    |  73 ++
 .../time.clock.gps/gps_time.ostream.pass.cpp  | 164 +++
 .../time.clock.gps.members/from_utc.pass.cpp  | 158 +++
 .../time.clock.gps.members/now.pass.cpp       |  33 +
 .../time.clock.gps.members/to_utc.pass.cpp    | 153 +++
 .../time.clock.gps/types.compile.pass.cpp     |  60 ++
 .../time/time.syn/formatter.gps_time.pass.cpp | 990 ++++++++++++++++++
 18 files changed, 1862 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/include/__chrono/gps_clock.h
 create mode 100644 libcxx/test/libcxx/time/time.clock/time.clock.gps/time.clock.gps.members/assert.from_utc.pass.cpp
 create mode 100644 libcxx/test/libcxx/time/time.clock/time.clock.gps/time.clock.gps.members/assert.to_utc.pass.cpp
 create mode 100644 libcxx/test/std/time/time.clock/time.clock.gps/gps_time.ostream.pass.cpp
 create mode 100644 libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/from_utc.pass.cpp
 create mode 100644 libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/now.pass.cpp
 create mode 100644 libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/to_utc.pass.cpp
 create mode 100644 libcxx/test/std/time/time.clock/time.clock.gps/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/time/time.syn/formatter.gps_time.pass.cpp

diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index beec97b8c01790c..6387b5ac20a3d9a 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -4,7 +4,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_time<Duration>``",,Mark de Wever,|Complete|,17
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,|Complete|,20
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",,Mark de Wever,|Complete|,21
-`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
+`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",,Mark de Wever,|Complete|,21
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|Complete|,17
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_time<Duration>``",,Mark de Wever,|Complete|,17
 `[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",,,|Nothing To Do|,
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index ce805b4eb7b8b4f..4aab32a7850db6e 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -256,6 +256,7 @@ set(files
   __chrono/exception.h
   __chrono/file_clock.h
   __chrono/formatter.h
+  __chrono/gps_clock.h
   __chrono/hh_mm_ss.h
   __chrono/high_resolution_clock.h
   __chrono/leap_second.h
diff --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h
index d9ccf693160d292..0a6b62772609162 100644
--- a/libcxx/include/__chrono/convert_to_tm.h
+++ b/libcxx/include/__chrono/convert_to_tm.h
@@ -15,6 +15,7 @@
 #include <__chrono/day.h>
 #include <__chrono/duration.h>
 #include <__chrono/file_clock.h>
+#include <__chrono/gps_clock.h>
 #include <__chrono/hh_mm_ss.h>
 #include <__chrono/local_info.h>
 #include <__chrono/month.h>
@@ -124,6 +125,11 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::tai_time<_Duration> __tp) {
   return std::__convert_to_tm<_Tm>(chrono::sys_time<_Rp>{__tp.time_since_epoch() - __offset});
 }
 
+template <class _Tm, class _Duration>
+_LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::gps_time<_Duration> __tp) {
+  return std::__convert_to_tm<_Tm>(chrono::utc_clock::to_sys(chrono::gps_clock::to_utc(__tp)));
+}
+
 #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
 #  endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 
diff --git a/libcxx/include/__chrono/formatter.h b/libcxx/include/__chrono/formatter.h
index 13bcb717291f699..a5caecce8a4f6ce 100644
--- a/libcxx/include/__chrono/formatter.h
+++ b/libcxx/include/__chrono/formatter.h
@@ -21,6 +21,7 @@
 #  include <__chrono/day.h>
 #  include <__chrono/duration.h>
 #  include <__chrono/file_clock.h>
+#  include <__chrono/gps_clock.h>
 #  include <__chrono/hh_mm_ss.h>
 #  include <__chrono/local_info.h>
 #  include <__chrono/month.h>
@@ -235,6 +236,8 @@ _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const
 #      if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
   else if constexpr (__is_time_point<_Tp> && requires { requires same_as<typename _Tp::clock, chrono::tai_clock>; })
     return {"TAI", chrono::seconds{0}};
+  else if constexpr (__is_time_point<_Tp> && requires { requires same_as<typename _Tp::clock, chrono::gps_clock>; })
+    return {"GPS", chrono::seconds{0}};
   else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
     return __formatter::__convert_to_time_zone(__value.get_info());
 #      endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
@@ -748,6 +751,17 @@ struct _LIBCPP_TEMPLATE_VIS formatter<chrono::tai_time<_Duration>, _CharT> : pub
   }
 };
 
+template <class _Duration, __fmt_char_type _CharT>
+struct _LIBCPP_TEMPLATE_VIS formatter<chrono::gps_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
+public:
+  using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
+
+  template <class _ParseContext>
+  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
+    return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
+  }
+};
+
 #      endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
 #    endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
 
diff --git a/libcxx/include/__chrono/gps_clock.h b/libcxx/include/__chrono/gps_clock.h
new file mode 100644
index 000000000000000..57e219edf706769
--- /dev/null
+++ b/libcxx/include/__chrono/gps_clock.h
@@ -0,0 +1,85 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___CHRONO_GPS_CLOCK_H
+#define _LIBCPP___CHRONO_GPS_CLOCK_H
+
+#include <version>
+// Enable the contents of the header only when libc++ was built with experimental features enabled.
+#if _LIBCPP_HAS_EXPERIMENTAL_TZDB
+
+#  include <__assert>
+#  include <__chrono/duration.h>
+#  include <__chrono/time_point.h>
+#  include <__chrono/utc_clock.h>
+#  include <__config>
+#  include <__type_traits/common_type.h>
+
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#  if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
+
+namespace chrono {
+
+class gps_clock;
+
+template <class _Duration>
+using gps_time    = time_point<gps_clock, _Duration>;
+using gps_seconds = gps_time<seconds>;
+
+class gps_clock {
+public:
+  using rep                       = utc_clock::rep;
+  using period                    = utc_clock::period;
+  using duration                  = chrono::duration<rep, period>;
+  using time_point                = chrono::time_point<gps_clock>;
+  static constexpr bool is_steady = false; // The utc_clock is not steady.
+
+  // The static difference between UTC and GPS time as specified in the Standard.
+  static constexpr chrono::seconds __offset{315964809};
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static time_point now() { return from_utc(utc_clock::now()); }
+
+  template <class _Duration>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static utc_time<common_type_t<_Duration, seconds>>
+  to_utc(const gps_time<_Duration>& __time) noexcept {
+    using _Rp                    = common_type_t<_Duration, seconds>;
+    _Duration __time_since_epoch = __time.time_since_epoch();
+    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__time_since_epoch >= utc_time<_Rp>::min().time_since_epoch() + __offset,
+                                          "the GPS to UTC conversion would underflow");
+
+    return utc_time<_Rp>{__time_since_epoch + __offset};
+  }
+
+  template <class _Duration>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static gps_time<common_type_t<_Duration, seconds>>
+  from_utc(const utc_time<_Duration>& __time) noexcept {
+    using _Rp                    = common_type_t<_Duration, seconds>;
+    _Duration __time_since_epoch = __time.time_since_epoch();
+    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__time_since_epoch <= utc_time<_Rp>::max().time_since_epoch() - __offset,
+                                          "the UTC to GPS conversion would overflow");
+
+    return gps_time<_Rp>{__time_since_epoch - __offset};
+  }
+};
+
+} // namespace chrono
+
+#  endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM &&
+         // _LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
+
+#endif // _LIBCPP___CHRONO_GPS_CLOCK_H
diff --git a/libcxx/include/__chrono/ostream.h b/libcxx/include/__chrono/ostream.h
index b8cd6a4680662b0..7a01b186780cb5e 100644
--- a/libcxx/include/__chrono/ostream.h
+++ b/libcxx/include/__chrono/ostream.h
@@ -18,6 +18,7 @@
 #  include <__chrono/day.h>
 #  include <__chrono/duration.h>
 #  include <__chrono/file_clock.h>
+#  include <__chrono/gps_clock.h>
 #  include <__chrono/hh_mm_ss.h>
 #  include <__chrono/local_info.h>
 #  include <__chrono/month.h>
@@ -78,6 +79,12 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const tai_time<_Duration>& __tp
   return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
 }
 
+template <class _CharT, class _Traits, class _Duration>
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
+operator<<(basic_ostream<_CharT, _Traits>& __os, const gps_time<_Duration>& __tp) {
+  return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
+}
+
 #      endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
 #    endif   // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
 
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index bd4c98600440c48..8d4adc80dc1e530 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -363,6 +363,33 @@ template<class charT, class traits, class Duration>     // C++20
   basic_ostream<charT, traits>&
     operator<<(basic_ostream<charT, traits>& os, const tai_time<Duration>& t);
 
+// [time.clock.gps], class gps_clock
+class gps_clock {                                      // C++20
+public:
+    using rep                       = a signed arithmetic type;
+    using period                    = ratio<unspecified, unspecified>;
+    using duration                  = chrono::duration<rep, period>;
+    using time_point                = chrono::time_point<gps_clock>;
+    static constexpr bool is_steady = unspecified;
+
+    static time_point now();
+
+    template<class Duration>
+      static utc_time<common_type_t<Duration, seconds>>
+        to_utc(const gps_time<Duration>& t);
+    template<class Duration>
+      static gps_time<common_type_t<Duration, seconds>>
+        from_utc(const utc_time<Duration>& t);
+};
+
+template<class Duration>
+using gps_time  = time_point<gps_clock, Duration>;      // C++20
+using gps_seconds = gps_time<seconds>;                  // C++20
+
+template<class charT, class traits, class Duration>     // C++20
+  basic_ostream<charT, traits>&
+    operator<<(basic_ostream<charT, traits>& os, const gps_time<Duration>& t);
+
 class file_clock                                        // C++20
 {
 public:
@@ -928,6 +955,8 @@ namespace std {
     struct formatter<chrono::utc_time<Duration>, charT>;                          // C++20
   template<class Duration, class charT>
     struct formatter<chrono::tai_time<Duration>, charT>;                          // C++20
+  template<class Duration, class charT>
+    struct formatter<chrono::gps_time<Duration>, charT>;                          // C++20
   template<class Duration, class charT>
     struct formatter<chrono::filetime<Duration>, charT>;                          // C++20
   template<class Duration, class charT>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index fd39c946b992a43..eb46c2f95e055f8 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -935,6 +935,10 @@ module std [system] {
     module exception                  { header "__chrono/exception.h" }
     module file_clock                 { header "__chrono/file_clock.h" }
     module formatter                  { header "__chrono/formatter.h" }
+    module gps_clock {
+      header "__chrono/gps_clock.h"
+      export std.chrono.time_point
+    }
     module hh_mm_ss                   { header "__chrono/hh_mm_ss.h" }
     module high_resolution_clock {
       header "__chrono/high_resolution_clock.h"
diff --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc
index 43e8da36e904489..66eccd8d290ad17 100644
--- a/libcxx/modules/std/chrono.inc
+++ b/libcxx/modules/std/chrono.inc
@@ -103,13 +103,11 @@ export namespace std {
     using std::chrono::tai_seconds;
     using std::chrono::tai_time;
 
-#    if 0
     // [time.clock.gps], class gps_clock
     using std::chrono::gps_clock;
 
     using std::chrono::gps_seconds;
     using std::chrono::gps_time;
-#    endif
 #  endif // _LIBCPP_ENABLE_EXPERIMENTAL
 #endif   //  _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 
diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
index bb40e0cfc4e1b8a..4e7380b86399359 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
@@ -113,4 +113,15 @@ void test(std::chrono::time_zone tz, std::chrono::time_zone_link link, std::chro
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::chrono::tai_clock::from_utc(std::chrono::utc_seconds{});
   }
+
+  { // [time.clock.gps]
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::chrono::gps_clock::now();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::chrono::gps_clock::to_utc(std::chrono::gps_seconds{});
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::chrono::gps_clock::from_utc(std::chrono::utc_seconds{});
+  }
 }
diff --git a/libcxx/test/libcxx/time/time.clock/time.clock.gps/time.clock.gps.members/assert.from_utc.pass.cpp b/libcxx/test/libcxx/time/time.clock/time.clock.gps/time.clock.gps.members/assert.from_utc.pass.cpp
new file mode 100644
index 000000000000000..d8200439d973761
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.clock/time.clock.gps/time.clock.gps.members/assert.from_utc.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
+
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// REQUIRES: has-unix-headers
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <chrono>
+//
+// class gps_clock;
+
+// static gps_time<common_type_t<_Duration, seconds>>
+// from_utc(const utc_time<_Duration>& t) noexcept;
+
+#include <chrono>
+
+#include "check_assertion.h"
+
+// The function is specified as
+//   gps_time<common_type_t<Duration, seconds>>{t.time_since_epoch()} + 378691210s
+// When t == t.max() there will be a signed integral overflow (other values too).
+int main(int, char**) {
+  using namespace std::literals::chrono_literals;
+  constexpr std::chrono::seconds offset{315964809};
+
+  (void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::nanoseconds>::max() - offset);
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::nanoseconds>::max() - offset + 1ns),
+      "the UTC to GPS conversion would overflow");
+
+  (void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::microseconds>::max() - offset);
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::microseconds>::max() - offset + 1us),
+      "the UTC to GPS conversion would overflow");
+
+  (void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::milliseconds>::max() - offset);
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::milliseconds>::max() - offset + 1ms),
+      "the UTC to GPS conversion would overflow");
+
+  (void)std::chrono::gps_clock::from_utc(std::chrono::utc_seconds::max() - offset);
+  TEST_LIBCPP_ASSERT_FAILURE(std::chrono::gps_clock::from_utc(std::chrono::utc_seconds::max() - offset + 1s),
+                             "the UTC to GPS conversion would overflow");
+
+  // The conversion uses `common_type_t<Duration, seconds>` so types "larger"
+  // than seconds are converted to seconds. Types "larger" than seconds are
+  // stored in "smaller" intergral and the overflow can never occur.
+
+  // Validate the types can never overflow on all current (and future) supported platforms.
+  static_assert(std::chrono::utc_time<std::chrono::days>::max() <= std::chrono::utc_seconds::max() - offset);
+  static_assert(std::chrono::utc_time<std::chrono::weeks>::max() <= std::chrono::utc_seconds::max() - offset);
+  static_assert(std::chrono::utc_time<std::chrono::months>::max() <= std::chrono::utc_seconds::max() - offset);
+  static_assert(std::chrono::utc_time<std::chrono::years>::max() <= std::chrono::utc_seconds::max() - offset);
+
+  // Validate the run-time conversion works.
+  (void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::days>::max());
+  (void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::weeks>::max());
+  (void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::months>::max());
+  (void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::years>::max());
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/time/time.clock/time.clock.gps/time.clock.gps.members/assert.to_utc.pass.cpp b/libcxx/test/libcxx/time/time.clock/time.clock.gps/time.clock.gps.members/assert.to_utc.pass.cpp
new file mode 100644
index 000000000000000..d61b3374f661f46
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.clock/time.clock.gps/time.clock.gps.members/assert.to_utc.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
+
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// REQUIRES: has-unix-headers
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <chrono>
+//
+// class gps_clock;
+
+// static utc_time<common_type_t<_Duration, seconds>>
+// to_utc(const gps_time<_Duration>& t) noexcept;
+
+#include <chrono>
+
+#include "check_assertion.h"
+
+// The function is specified as
+//   utc_time<common_type_t<Duration, seconds>>{t.time_since_epoch()} - 378691210s
+// When t == t.min() there will be a signed integral underlow (other values too).
+int main(int, char**) {
+  using namespace std::literals::chrono_literals;
+  constexpr std::chrono::seconds offset{315964809};
+
+  (void)std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::nanoseconds>::min() + offset);
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::nanoseconds>::min() + offset - 1ns),
+      "the GPS to UTC conversion would underflow");
+
+  (void)std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::microseconds>::min() + offset);
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::microseconds>::min() + offset - 1us),
+      "the GPS to UTC conversion would underflow");
+
+  (void)std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::milliseconds>::min() + offset);
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::milliseconds>::min() + offset - 1ms),
+      "the GPS to UTC conversion would underflow");
+
+  (void)std::chrono::gps_clock::to_utc(std::chrono::gps_seconds::min() + offset);
+  TEST_LIBCPP_ASSERT_FAILURE(std::chrono::gps_clock::to_utc(std::chrono::gps_seconds::min() + offset - 1s),
+                             "the GPS to UTC conversion would underflow");
+
+  // The conversion uses `common_type_t<Duration, seconds>` so types "larger"
+  // than seconds are converted to seconds. Types "larger" than seconds are
+  // stored in "smaller" intergral and the underflow can never occur.
+
+  // Validate the types can never underflow on all current (and future) supported platforms.
+  static_assert(std::chrono::gps_time<std::chrono::days>::min() >= std::chrono::gps_seconds::min() + offset);
+  static_assert(std::chrono::gps_time<std::chrono::weeks>::min() >= std::chrono::gps_seconds::min() + offset);
+  static_assert(std::chrono::gps_time<std::chrono::months>::min() >= std::chrono::gps_seconds::min() + offset);
+  static_assert(std::chrono::gps_time<std::chrono::years>::min() >= std::chrono::gps_seconds::min() + offset);
+
+  // Validate the run-time conversion works.
+  (void)std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::days>::min());
+  (void)std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::weeks>::min());
+  (void)std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::months>::min());
+  (void)std::chrono::gps_clock::to_utc(std::chrono::gps_time<std::chrono::years>::min());
+
+  return 0;
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.gps/gps_time.ostream.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.gps/gps_time.ostream.pass.cpp
new file mode 100644
index 000000000000000..3f942bc63ce5a1b
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.gps/gps_time.ostream.pass.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
+
+// REQUIRES: locale.fr_FR.UTF-8
+// REQUIRES: locale.ja_JP.UTF-8
+
+// <chrono>
+
+// class gps_clock;
+
+// template<class charT, class traits, class Duration>
+//   basic_ostream<charT, traits>&
+//     operator<<(basic_ostream<charT, traits>& os, const gps_time<Duration>& tp);
+
+#include <chrono>
+#include <cassert>
+#include <ratio>
+#include <sstream>
+
+#include "make_string.h"
+#include "platform_support.h" // locale name macros
+#include "test_macros.h"
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+template <class CharT, class Duration>
+static std::basic_string<CharT> stream_c_locale(std::chrono::gps_time<Duration> time_point) {
+  std::basic_stringstream<CharT> sstr;
+  sstr << std::fixed << time_point;
+  return sstr.str();
+}
+
+template <class CharT, class Duration>
+static std::basic_string<CharT> stream_fr_FR_locale(std::chrono::gps_time<Duration> time_point) {
+  std::basic_stringstream<CharT> sstr;
+  const std::locale locale(LOCALE_fr_FR_UTF_8);
+  sstr.imbue(locale);
+  sstr << std::fixed << time_point;
+  return sstr.str();
+}
+
+template <class CharT, class Duration>
+static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::gps_time<Duration> time_point) {
+  std::basic_stringstream<CharT> sstr;
+  const std::locale locale(LOCALE_ja_JP_UTF_8);
+  sstr.imbue(locale);
+  sstr << std::fixed << time_point;
+  return sstr.str();
+}
+
+template <class CharT>
+static void test_c() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::nanoseconds>{946'688'523'123'456'789ns}) ==
+         SV("2010-01-05 01:01:48.123456789"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::microseconds>{946'688'523'123'456us}) ==
+         SV("2010-01-05 01:01:48.123456"));
+
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::milliseconds>{946'684'822'123ms}) == SV("2010-01-05 00:00:07.123"));
+  assert(stream_c_locale<CharT>(cr::gps_seconds{1'234'567'890s}) == SV("2019-02-18 23:31:12"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::minutes>{20'576'131min}) == SV("2019-02-18 23:30:42"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::hours>{342'935h}) == SV("2019-02-18 22:59:42"));
+
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<signed char, std::ratio<2, 1>>>{
+             cr::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1980-01-06 00:02:00"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<short, std::ratio<1, 2>>>{
+             cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1980-01-06 00:30:00.0"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<int, std::ratio<1, 4>>>{
+             cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1980-01-06 00:15:00.00"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<long, std::ratio<1, 10>>>{
+             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1980-01-06 01:01:01.1"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<long long, std::ratio<1, 100>>>{
+             cr::duration<long long, std::ratio<1, 100>>{123'456'789'010}}) == SV("2019-02-18 23:31:12.10"));
+}
+
+template <class CharT>
+static void test_fr_FR() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::nanoseconds>{946'688'523'123'456'789ns}) ==
+         SV("2010-01-05 01:01:48,123456789"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::microseconds>{946'688'523'123'456us}) ==
+         SV("2010-01-05 01:01:48,123456"));
+
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::milliseconds>{946'684'822'123ms}) ==
+         SV("2010-01-05 00:00:07,123"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_seconds{1'234'567'890s}) == SV("2019-02-18 23:31:12"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::minutes>{20'576'131min}) == SV("2019-02-18 23:30:42"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::hours>{342'935h}) == SV("2019-02-18 22:59:42"));
+
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<signed char, std::ratio<2, 1>>>{
+             cr::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1980-01-06 00:02:00"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<short, std::ratio<1, 2>>>{
+             cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1980-01-06 00:30:00,0"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<int, std::ratio<1, 4>>>{
+             cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1980-01-06 00:15:00,00"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<long, std::ratio<1, 10>>>{
+             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1980-01-06 01:01:01,1"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<long long, std::ratio<1, 100>>>{
+             cr::duration<long long, std::ratio<1, 100>>{123'456'789'010}}) == SV("2019-02-18 23:31:12,10"));
+}
+
+template <class CharT>
+static void test_ja_JP() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::nanoseconds>{946'688'523'123'456'789ns}) ==
+         SV("2010-01-05 01:01:48.123456789"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::microseconds>{946'688'523'123'456us}) ==
+         SV("2010-01-05 01:01:48.123456"));
+
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::milliseconds>{946'684'822'123ms}) ==
+         SV("2010-01-05 00:00:07.123"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_seconds{1'234'567'890s}) == SV("2019-02-18 23:31:12"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::minutes>{20'576'131min}) == SV("2019-02-18 23:30:42"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::hours>{342'935h}) == SV("2019-02-18 22:59:42"));
+
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<signed char, std::ratio<2, 1>>>{
+             cr::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1980-01-06 00:02:00"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<short, std::ratio<1, 2>>>{
+             cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1980-01-06 00:30:00.0"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<int, std::ratio<1, 4>>>{
+             cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1980-01-06 00:15:00.00"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<long, std::ratio<1, 10>>>{
+             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1980-01-06 01:01:01.1"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<long long, std::ratio<1, 100>>>{
+             cr::duration<long long, std::ratio<1, 100>>{123'456'789'010}}) == SV("2019-02-18 23:31:12.10"));
+}
+
+template <class CharT>
+static void test() {
+  test_c<CharT>();
+  test_fr_FR<CharT>();
+  test_ja_JP<CharT>();
+}
+
+int main(int, char**) {
+  test<char>();
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/from_utc.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/from_utc.pass.cpp
new file mode 100644
index 000000000000000..56411336274eca6
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/from_utc.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+//
+// class gps_clock;
+
+// static gps_time<common_type_t<_Duration, seconds>>
+// from_utc(const utc<_Duration>& __time) noexcept;
+
+#include <chrono>
+#include <cassert>
+#include <source_location>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+static void test_known_values() {
+  namespace cr = std::chrono;
+  using namespace std::literals::chrono_literals;
+
+  // [time.clock.gps.overview]/1
+  //   The clock gps_clock measures seconds since the first Sunday of January,
+  //   1980 00:00:00 UTC.
+  // The first Sunday is 1980-1-6 (so January sixth)
+  // ... 1980-01-06 00:00:00 GPS is equivalent to 1980-01-06 00:00:00 UTC
+
+  assert(cr::gps_clock::from_utc(cr::utc_clock::from_sys(cr::sys_days{cr::January / 6 / 1980})) == cr::gps_seconds{0s});
+}
+
+template <class Duration>
+static void test_leap_seconds(std::chrono::utc_time<Duration> utc,
+                              std::chrono::gps_time<Duration> expected,
+                              std::source_location loc = std::source_location::current()) {
+  auto gps = std::chrono::gps_clock::from_utc(utc);
+  TEST_REQUIRE(gps == expected,
+               TEST_WRITE_CONCATENATED(loc, "\nExpected output ", expected, "\nActual output   ", gps, '\n'));
+}
+
+// Tests set if existing database entries at the time of writing.
+static void test_transitions() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  // "sys" is the time of the transition to the next leap second.
+  // "elapsed" is the number of leap seconds before the transition.
+  auto test_transition = [](cr::sys_days sys, cr::seconds elapsed) {
+    constexpr auto unix_to_gps_epoch_offset =
+        cr::sys_days{cr::January / 1 / 1970} - cr::sys_days{cr::January / 6 / 1980};
+    cr::gps_seconds gps{sys.time_since_epoch() + unix_to_gps_epoch_offset + elapsed};
+
+    test_leap_seconds(cr::utc_clock::from_sys(sys - 1ns), gps - 1ns);
+    test_leap_seconds(cr::utc_clock::from_sys(sys), gps + 1s);
+    test_leap_seconds(cr::utc_clock::from_sys(sys) + 1ns, gps + 1s + 1ns);
+  };
+
+  // Transitions from the start of UTC.
+  test_transition(cr::sys_days{cr::July / 1 / 1972}, -9s);
+  test_transition(cr::sys_days{cr::January / 1 / 1973}, -8s);
+  test_transition(cr::sys_days{cr::January / 1 / 1974}, -7s);
+  test_transition(cr::sys_days{cr::January / 1 / 1975}, -6s);
+  test_transition(cr::sys_days{cr::January / 1 / 1976}, -5s);
+  test_transition(cr::sys_days{cr::January / 1 / 1977}, -4s);
+  test_transition(cr::sys_days{cr::January / 1 / 1978}, -3s);
+  test_transition(cr::sys_days{cr::January / 1 / 1979}, -2s);
+  test_transition(cr::sys_days{cr::January / 1 / 1980}, -1s);
+  test_transition(cr::sys_days{cr::July / 1 / 1981}, 0s);
+  test_transition(cr::sys_days{cr::July / 1 / 1982}, 1s);
+  test_transition(cr::sys_days{cr::July / 1 / 1983}, 2s);
+  test_transition(cr::sys_days{cr::July / 1 / 1985}, 3s);
+  test_transition(cr::sys_days{cr::January / 1 / 1988}, 4s);
+  test_transition(cr::sys_days{cr::January / 1 / 1990}, 5s);
+  test_transition(cr::sys_days{cr::January / 1 / 1991}, 6s);
+  test_transition(cr::sys_days{cr::July / 1 / 1992}, 7s);
+  test_transition(cr::sys_days{cr::July / 1 / 1993}, 8s);
+  test_transition(cr::sys_days{cr::July / 1 / 1994}, 9s);
+  test_transition(cr::sys_days{cr::January / 1 / 1996}, 10s);
+  test_transition(cr::sys_days{cr::July / 1 / 1997}, 11s);
+  test_transition(cr::sys_days{cr::January / 1 / 1999}, 12s);
+  test_transition(cr::sys_days{cr::January / 1 / 2006}, 13s);
+  test_transition(cr::sys_days{cr::January / 1 / 2009}, 14s);
+  test_transition(cr::sys_days{cr::July / 1 / 2012}, 15s);
+  test_transition(cr::sys_days{cr::July / 1 / 2015}, 16s);
+  test_transition(cr::sys_days{cr::January / 1 / 2017}, 17s);
+}
+
+// Tests whether the return type is the expected type.
+static void test_return_type() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::nanoseconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::nanoseconds>{0ns});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::microseconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::microseconds>{0us});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::milliseconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::milliseconds>{0ms});
+  }
+
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::seconds>{cr::seconds{0}});
+  }
+
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::minutes>{cr::minutes{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::hours>{cr::hours{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::days>{cr::days{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::weeks>{cr::weeks{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::months>{cr::months{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::gps_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::from_utc(cr::utc_time<cr::years>{cr::years{0}});
+  }
+}
+
+int main(int, const char**) {
+  using namespace std::literals::chrono_literals;
+  std::chrono::utc_seconds time = std::chrono::utc_seconds{0s};
+  static_assert(noexcept(std::chrono::gps_clock::from_utc(time)));
+
+  test_known_values();
+  test_transitions();
+  test_return_type();
+
+  return 0;
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/now.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/now.pass.cpp
new file mode 100644
index 000000000000000..21f04203c9004d0
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/now.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+//
+// class gps_clock;
+
+// static time_point now();
+
+#include <chrono>
+#include <concepts>
+#include <cassert>
+
+int main(int, const char**) {
+  using clock                                      = std::chrono::gps_clock;
+  std::same_as<clock::time_point> decltype(auto) t = clock::now();
+
+  assert(t >= clock::time_point::min());
+  assert(t <= clock::time_point::max());
+
+  return 0;
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/to_utc.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/to_utc.pass.cpp
new file mode 100644
index 000000000000000..c92ca7f2eda7351
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.gps/time.clock.gps.members/to_utc.pass.cpp
@@ -0,0 +1,153 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+//
+// class gps_clock;
+
+// static utc_time<common_type_t<_Duration, seconds>>
+// to_utc(const gps_time<_Duration>& __time) noexcept;
+
+#include <chrono>
+#include <cassert>
+#include <source_location>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+static void test_known_values() {
+  namespace cr = std::chrono;
+  using namespace std::literals::chrono_literals;
+
+  assert(cr::gps_clock::to_utc(cr::gps_seconds{0s}) == cr::utc_clock::from_sys(cr::sys_days{cr::January / 6 / 1980}));
+}
+
+template <class Duration>
+static void test_leap_seconds(std::chrono::gps_time<Duration> gps,
+                              std::chrono::utc_time<Duration> expected,
+                              std::source_location loc = std::source_location::current()) {
+  auto utc = std::chrono::gps_clock::to_utc(gps);
+  TEST_REQUIRE(utc == expected,
+               TEST_WRITE_CONCATENATED(loc, "\nExpected output ", expected, "\nActual output   ", utc, '\n'));
+}
+
+// Tests set if existing database entries at the time of writing.
+static void test_transitions() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  // "sys" is the time of the transition to the next leap second.
+  // "elapsed" is the number of leap seconds before the transition.
+  auto test_transition = [](cr::sys_days sys, cr::seconds elapsed) {
+    constexpr auto unix_to_gps_epoch_offset =
+        cr::sys_days{cr::January / 1 / 1970} - cr::sys_days{cr::January / 6 / 1980};
+    cr::gps_seconds gps{sys.time_since_epoch() + unix_to_gps_epoch_offset + elapsed};
+
+    test_leap_seconds(gps - 1ns, cr::utc_clock::from_sys(sys - 1ns));
+    test_leap_seconds(gps + 1s, cr::utc_clock::from_sys(sys));
+    test_leap_seconds(gps + 1s + 1ns, cr::utc_clock::from_sys(sys + 1ns));
+  };
+
+  // Transitions from the start of UTC.
+  test_transition(cr::sys_days{cr::July / 1 / 1972}, -9s);
+  test_transition(cr::sys_days{cr::January / 1 / 1973}, -8s);
+  test_transition(cr::sys_days{cr::January / 1 / 1974}, -7s);
+  test_transition(cr::sys_days{cr::January / 1 / 1975}, -6s);
+  test_transition(cr::sys_days{cr::January / 1 / 1976}, -5s);
+  test_transition(cr::sys_days{cr::January / 1 / 1977}, -4s);
+  test_transition(cr::sys_days{cr::January / 1 / 1978}, -3s);
+  test_transition(cr::sys_days{cr::January / 1 / 1979}, -2s);
+  test_transition(cr::sys_days{cr::January / 1 / 1980}, -1s);
+  test_transition(cr::sys_days{cr::July / 1 / 1981}, 0s);
+  test_transition(cr::sys_days{cr::July / 1 / 1982}, 1s);
+  test_transition(cr::sys_days{cr::July / 1 / 1983}, 2s);
+  test_transition(cr::sys_days{cr::July / 1 / 1985}, 3s);
+  test_transition(cr::sys_days{cr::January / 1 / 1988}, 4s);
+  test_transition(cr::sys_days{cr::January / 1 / 1990}, 5s);
+  test_transition(cr::sys_days{cr::January / 1 / 1991}, 6s);
+  test_transition(cr::sys_days{cr::July / 1 / 1992}, 7s);
+  test_transition(cr::sys_days{cr::July / 1 / 1993}, 8s);
+  test_transition(cr::sys_days{cr::July / 1 / 1994}, 9s);
+  test_transition(cr::sys_days{cr::January / 1 / 1996}, 10s);
+  test_transition(cr::sys_days{cr::July / 1 / 1997}, 11s);
+  test_transition(cr::sys_days{cr::January / 1 / 1999}, 12s);
+  test_transition(cr::sys_days{cr::January / 1 / 2006}, 13s);
+  test_transition(cr::sys_days{cr::January / 1 / 2009}, 14s);
+  test_transition(cr::sys_days{cr::July / 1 / 2012}, 15s);
+  test_transition(cr::sys_days{cr::July / 1 / 2015}, 16s);
+  test_transition(cr::sys_days{cr::January / 1 / 2017}, 17s);
+}
+
+// Tests whether the return type is the expected type.
+static void test_return_type() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::nanoseconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::nanoseconds>{0ns});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::microseconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::microseconds>{0us});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::milliseconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::milliseconds>{0ms});
+  }
+
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::seconds>{cr::seconds{0}});
+  }
+
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::minutes>{cr::minutes{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::hours>{cr::hours{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::days>{cr::days{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::weeks>{cr::weeks{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::months>{cr::months{0}});
+  }
+  {
+    [[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
+        cr::gps_clock::to_utc(cr::gps_time<cr::years>{cr::years{0}});
+  }
+}
+
+int main(int, const char**) {
+  using namespace std::literals::chrono_literals;
+
+  std::chrono::gps_seconds time = std::chrono::gps_seconds{0s};
+  static_assert(noexcept(std::chrono::gps_clock::to_utc(time)));
+
+  test_known_values();
+  test_transitions();
+  test_return_type();
+
+  return 0;
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.gps/types.compile.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.gps/types.compile.pass.cpp
new file mode 100644
index 000000000000000..af95c5fe7340235
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.gps/types.compile.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// class gps_clock {
+// public:
+//     using rep                       = a signed arithmetic type;
+//     using period                    = ratio<unspecified, unspecified>;
+//     using duration                  = chrono::duration<rep, period>;
+//     using time_point                = chrono::time_point<gps_clock>;
+//     static constexpr bool is_steady = unspecified;
+//
+//     ...
+// };
+//
+// template<class Duration>
+// using gps_time  = time_point<gps_clock, Duration>;
+// using gps_seconds = gps_time<seconds>;
+
+#include <chrono>
+#include <concepts>
+#include <ratio>
+
+#include "test_macros.h"
+
+// class gps_clock
+using rep                = std::chrono::gps_clock::rep;
+using period             = std::chrono::gps_clock::period;
+using duration           = std::chrono::gps_clock::duration;
+using time_point         = std::chrono::gps_clock::time_point;
+constexpr bool is_steady = std::chrono::gps_clock::is_steady;
+
+// Tests the values. part of them are implementation defined.
+LIBCPP_STATIC_ASSERT(std::same_as<rep, std::chrono::utc_clock::rep>);
+static_assert(std::is_arithmetic_v<rep>);
+static_assert(std::is_signed_v<rep>);
+
+LIBCPP_STATIC_ASSERT(std::same_as<period, std::chrono::utc_clock::period>);
+static_assert(std::same_as<period, std::ratio<period::num, period::den>>);
+
+static_assert(std::same_as<duration, std::chrono::duration<rep, period>>);
+static_assert(std::same_as<time_point, std::chrono::time_point<std::chrono::gps_clock>>);
+LIBCPP_STATIC_ASSERT(is_steady == false);
+
+// typedefs
+static_assert(std::same_as<std::chrono::gps_time<int>, std::chrono::time_point<std::chrono::gps_clock, int>>);
+static_assert(std::same_as<std::chrono::gps_time<long>, std::chrono::time_point<std::chrono::gps_clock, long>>);
+static_assert(std::same_as<std::chrono::gps_seconds, std::chrono::gps_time<std::chrono::seconds>>);
diff --git a/libcxx/test/std/time/time.syn/formatter.gps_time.pass.cpp b/libcxx/test/std/time/time.syn/formatter.gps_time.pass.cpp
new file mode 100644
index 000000000000000..53620e243889981
--- /dev/null
+++ b/libcxx/test/std/time/time.syn/formatter.gps_time.pass.cpp
@@ -0,0 +1,990 @@
+//===----------------------------------------------------------------------===//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
+
+// REQUIRES: locale.fr_FR.UTF-8
+// REQUIRES: locale.ja_JP.UTF-8
+
+// <chrono>
+
+// template<class Duration, class charT>
+//   struct formatter<chrono::gps_time<Duration>, charT>;
+
+#include <chrono>
+#include <format>
+
+#include <cassert>
+#include <concepts>
+#include <locale>
+#include <iostream>
+#include <type_traits>
+
+#include "formatter_tests.h"
+#include "make_string.h"
+#include "platform_support.h" // locale name macros
+#include "test_macros.h"
+
+template <class CharT>
+static void test_no_chrono_specs() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output
+
+  // [time.syn]
+  //   using nanoseconds  = duration<signed integer type of at least 64 bits, nano>;
+  //   using microseconds = duration<signed integer type of at least 55 bits, micro>;
+  //   using milliseconds = duration<signed integer type of at least 45 bits, milli>;
+  //   using seconds      = duration<signed integer type of at least 35 bits>;
+  //   using minutes      = duration<signed integer type of at least 29 bits, ratio<  60>>;
+  //   using hours        = duration<signed integer type of at least 23 bits, ratio<3600>>;
+  check(SV("1435-08-09 22:07:05"), SV("{}"), cr::gps_seconds(-17'179'869'184s)); // Minimum value for 35 bits.
+  check(SV("1911-12-18 20:46:01"), SV("{}"), cr::gps_seconds(-2'147'483'648s));
+
+  check(SV("1980-01-05 00:00:00"), SV("{}"), cr::gps_seconds(-24h));
+  check(SV("1980-01-05 06:00:00"), SV("{}"), cr::gps_seconds(-18h));
+  check(SV("1980-01-05 12:00:00"), SV("{}"), cr::gps_seconds(-12h));
+  check(SV("1980-01-05 18:00:00"), SV("{}"), cr::gps_seconds(-6h));
+  check(SV("1980-01-05 23:59:59"), SV("{}"), cr::gps_seconds(-1s));
+
+  check(SV("1980-01-06 00:00:00"), SV("{}"), cr::gps_seconds(0s));
+  check(SV("2010-01-04 23:59:45"), SV("{}"), cr::gps_seconds(946'684'800s));
+  check(SV("2010-01-05 01:01:48"), SV("{}"), cr::gps_seconds(946'688'523s));
+
+  check(SV("2048-01-24 03:13:49"), SV("{}"), cr::gps_seconds(2'147'483'647s));
+  check(SV("2524-06-03 01:52:45"), SV("{}"), cr::gps_seconds(17'179'869'183s)); // Maximum value for 35 bits.
+
+  check(SV("2010-01-05 01:01:48.123"), SV("{}"), cr::gps_time<cr::milliseconds>(946'688'523'123ms));
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_year() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt =
+      SV("{:%%C='%C'%t%%EC='%EC'%t%%y='%y'%t%%Oy='%Oy'%t%%Ey='%Ey'%t%%Y='%Y'%t%%EY='%EY'%n}");
+  constexpr std::basic_string_view<CharT> lfmt =
+      SV("{:L%%C='%C'%t%%EC='%EC'%t%%y='%y'%t%%Oy='%Oy'%t%%Ey='%Ey'%t%%Y='%Y'%t%%EY='%EY'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%C='19'\t%EC='19'\t%y='80'\t%Oy='80'\t%Ey='80'\t%Y='1980'\t%EY='1980'\n"),
+        fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"),
+        fmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use the global locale (fr_FR)
+  check(SV("%C='19'\t%EC='19'\t%y='80'\t%Oy='80'\t%Ey='80'\t%Y='1980'\t%EY='1980'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use supplied locale (ja_JP). This locale has a different alternate.
+#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%C='19'\t%EC='19'\t%y='80'\t%Oy='80'\t%Ey='80'\t%Y='1980'\t%EY='1980'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#else                                   // defined(_WIN32) || defined(__APPLE__) || defined(_AIX)||defined(__FreeBSD__)
+  check(loc,
+        SV("%C='19'\t%EC='昭和'\t%y='80'\t%Oy='八十'\t%Ey='55'\t%Y='1980'\t%EY='昭和55年'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%C='20'\t%EC='平成'\t%y='09'\t%Oy='九'\t%Ey='21'\t%Y='2009'\t%EY='平成21年'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#endif                                  // defined(_WIN32) || defined(__APPLE__) || defined(_AIX)||defined(__FreeBSD__)
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_month() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt  = SV("{:%%b='%b'%t%%h='%h'%t%%B='%B'%t%%m='%m'%t%%Om='%Om'%n}");
+  constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%b='%b'%t%%h='%h'%t%%B='%B'%t%%m='%m'%t%%Om='%Om'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%b='Jan'\t%h='Jan'\t%B='January'\t%m='01'\t%Om='01'\n"),
+        fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%b='May'\t%h='May'\t%B='May'\t%m='05'\t%Om='05'\n"),
+        fmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS on Wednesday, 18 May 2033
+
+  // Use the global locale (fr_FR)
+#if defined(__APPLE__)
+  check(SV("%b='jan'\t%h='jan'\t%B='janvier'\t%m='01'\t%Om='01'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+#else
+  check(SV("%b='janv.'\t%h='janv.'\t%B='janvier'\t%m='01'\t%Om='01'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+#endif
+
+  check(SV("%b='mai'\t%h='mai'\t%B='mai'\t%m='05'\t%Om='05'\n"),
+        lfmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS on Wednesday, 18 May 2033
+
+  // Use supplied locale (ja_JP). This locale has a different alternate.
+#ifdef _WIN32
+  check(loc,
+        SV("%b='1'\t%h='1'\t%B='1月'\t%m='01'\t%Om='01'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%b='5'\t%h='5'\t%B='5月'\t%m='05'\t%Om='05'\n"),
+        lfmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+#elif defined(_AIX)                       // _WIN32
+  check(loc,
+        SV("%b='1月'\t%h='1月'\t%B='1月'\t%m='01'\t%Om='01'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%b='5月'\t%h='5月'\t%B='5月'\t%m='05'\t%Om='05'\n"),
+        lfmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+#elif defined(__APPLE__)                  // _WIN32
+  check(loc,
+        SV("%b=' 1'\t%h=' 1'\t%B='1月'\t%m='01'\t%Om='01'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%b=' 5'\t%h=' 5'\t%B='5月'\t%m='05'\t%Om='05'\n"),
+        lfmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+#elif defined(__FreeBSD__)                // _WIN32
+  check(loc,
+        SV("%b=' 1月'\t%h=' 1月'\t%B='1月'\t%m='01'\t%Om='01'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%b=' 5月'\t%h=' 5月'\t%B='5月'\t%m='05'\t%Om='05'\n"),
+        lfmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+#else                                     // _WIN32
+  check(loc,
+        SV("%b=' 1月'\t%h=' 1月'\t%B='1月'\t%m='01'\t%Om='一'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%b=' 5月'\t%h=' 5月'\t%B='5月'\t%m='05'\t%Om='五'\n"),
+        lfmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+#endif                                    // _WIN32
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_day() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt  = SV("{:%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%n}");
+  constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%d='06'\t%Od='06'\t%e=' 6'\t%Oe=' 6'\n"), fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"),
+        fmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use the global locale (fr_FR)
+  check(SV("%d='06'\t%Od='06'\t%e=' 6'\t%Oe=' 6'\n"), lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use the global locale (fr_FR)
+  check(SV("%d='06'\t%Od='06'\t%e=' 6'\t%Oe=' 6'\n"), lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  // Use supplied locale (ja_JP). This locale has a different alternate.
+#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%d='06'\t%Od='06'\t%e=' 6'\t%Oe=' 6'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%d='06'\t%Od='六'\t%e=' 6'\t%Oe='六'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%d='13'\t%Od='十三'\t%e='13'\t%Oe='十三'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_weekday() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt =
+      SV("{:%%a='%a'%t%%A='%A'%t%%u='%u'%t%%Ou='%Ou'%t%%w='%w'%t%%Ow='%Ow'%n}");
+  constexpr std::basic_string_view<CharT> lfmt =
+      SV("{:L%%a='%a'%t%%A='%A'%t%%u='%u'%t%%Ou='%Ou'%t%%w='%w'%t%%Ow='%Ow'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%a='Sun'\t%A='Sunday'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+        fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%a='Sun'\t%A='Sunday'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+        fmt,
+        cr::gps_seconds(3'979'002'513s)); // 06:28:15 GPS on Sunday, 7 February 2106
+
+  // Use the global locale (fr_FR)
+#if defined(__APPLE__)
+  check(SV("%a='Dim'\t%A='Dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%a='Dim'\t%A='Dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+        lfmt,
+        cr::gps_seconds(3'979'002'513s)); // 06:28:15 GPS on Sunday, 7 February 2106
+#else
+  check(SV("%a='dim.'\t%A='dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%a='dim.'\t%A='dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+        lfmt,
+        cr::gps_seconds(3'979'002'513s)); // 06:28:15 GPS on Sunday, 7 February 2106
+#endif
+
+  // Use supplied locale (ja_JP).
+  // This locale has a different alternate, but not on all platforms
+#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+        lfmt,
+        cr::gps_seconds(3'979'002'513s)); // 06:28:15 GPS on Sunday, 7 February 2106
+#else  // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='七'\t%w='0'\t%Ow='〇'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='七'\t%w='0'\t%Ow='〇'\n"),
+        lfmt,
+        cr::gps_seconds(3'979'002'513s)); // 06:28:15 GPS on Sunday, 7 February 2106
+#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_day_of_year() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt  = SV("{:%%j='%j'%n}");
+  constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%j='%j'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%j='006'\n"), fmt, cr::gps_seconds(0s));             // 00:00:00 GPS Sunday, 6 January 1980
+  check(SV("%j='138'\n"), fmt, cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+
+  // Use the global locale (fr_FR)
+  check(SV("%j='006'\n"), lfmt, cr::gps_seconds(0s));             // 00:00:00 GPS Sunday, 6 January 1980
+  check(SV("%j='138'\n"), lfmt, cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+
+  // Use supplied locale (ja_JP). This locale has a different alternate.
+  check(loc, SV("%j='006'\n"), lfmt, cr::gps_seconds(0s));             // 00:00:00 GPS Sunday, 6 January 1980
+  check(loc, SV("%j='138'\n"), lfmt, cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_week() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt  = SV("{:%%U='%U'%t%%OU='%OU'%t%%W='%W'%t%%OW='%OW'%n}");
+  constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%U='%U'%t%%OU='%OU'%t%%W='%W'%t%%OW='%OW'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%U='01'\t%OU='01'\t%W='00'\t%OW='00'\n"), fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"),
+        fmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+
+  // Use the global locale (fr_FR)
+  check(SV("%U='01'\t%OU='01'\t%W='00'\t%OW='00'\n"), lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"),
+        lfmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+
+  // Use supplied locale (ja_JP). This locale has a different alternate.
+#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%U='01'\t%OU='001\t%W='00'\t%OW='00'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"),
+        lfmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+#else  // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%U='01'\t%OU='一'\t%W='00'\t%OW='〇'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%U='20'\t%OU='二十'\t%W='20'\t%OW='二十'\n"),
+        lfmt,
+        cr::gps_seconds(1'684'035'218s)); // 03:33:20 GPS Wednesday, 18 May 2033
+#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_iso_8601_week() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt  = SV("{:%%g='%g'%t%%G='%G'%t%%V='%V'%t%%OV='%OV'%n}");
+  constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%g='%g'%t%%G='%G'%t%%V='%V'%t%%OV='%OV'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%g='80'\t%G='1980'\t%V='01'\t%OV='01'\n"), fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"),
+        fmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use the global locale (fr_FR)
+  check(SV("%g='80'\t%G='1980'\t%V='01'\t%OV='01'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use supplied locale (ja_JP). This locale has a different alternate.
+#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%g='80'\t%G='1980'\t%V='01'\t%OV='01'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#else  // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%g='80'\t%G='1980'\t%V='01'\t%OV='一'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%g='09'\t%G='2009'\t%V='07'\t%OV='七'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_date() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt  = SV("{:%%D='%D'%t%%F='%F'%t%%x='%x'%t%%Ex='%Ex'%n}");
+  constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%D='%D'%t%%F='%F'%t%%x='%x'%t%%Ex='%Ex'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%D='01/06/80'\t%F='1980-01-06'\t%x='01/06/80'\t%Ex='01/06/80'\n"),
+        fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='02/13/09'\t%Ex='02/13/09'\n"),
+        fmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use the global locale (fr_FR)
+#if defined(__APPLE__) || defined(__FreeBSD__)
+  check(SV("%D='01/06/80'\t%F='1980-01-01'\t%x='06.01.1980'\t%Ex='06.01.1980'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='13.02.2009'\t%Ex='13.02.2009'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#else
+  check(SV("%D='01/06/80'\t%F='1980-01-06'\t%x='06/01/1980'\t%Ex='06/01/1980'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='13/02/2009'\t%Ex='13/02/2009'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#endif
+
+  // Use supplied locale (ja_JP). This locale has a different alternate.
+#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%D='01/06/80'\t%F='1980-01-06'\t%x='1980/01/06'\t%Ex='1980/01/06'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%D='02/13/09'\t%F='2009-02-13'\t%x='2009/02/13'\t%Ex='2009/02/13'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#else  // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+  check(loc,
+        SV("%D='01/06/80'\t%F='1980-01-06'\t%x='1980年01月06日'\t%Ex='昭和55年01月06日'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%D='02/13/09'\t%F='2009-02-13'\t%x='2009年02月13日'\t%Ex='平成21年02月13日'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_time() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt = SV(
+      "{:"
+      "%%H='%H'%t"
+      "%%OH='%OH'%t"
+      "%%I='%I'%t"
+      "%%OI='%OI'%t"
+      "%%M='%M'%t"
+      "%%OM='%OM'%t"
+      "%%S='%S'%t"
+      "%%OS='%OS'%t"
+      "%%p='%p'%t"
+      "%%R='%R'%t"
+      "%%T='%T'%t"
+      "%%r='%r'%t"
+      "%%X='%X'%t"
+      "%%EX='%EX'%t"
+      "%n}");
+  constexpr std::basic_string_view<CharT> lfmt = SV(
+      "{:L"
+      "%%H='%H'%t"
+      "%%OH='%OH'%t"
+      "%%I='%I'%t"
+      "%%OI='%OI'%t"
+      "%%M='%M'%t"
+      "%%OM='%OM'%t"
+      "%%S='%S'%t"
+      "%%OS='%OS'%t"
+      "%%p='%p'%t"
+      "%%R='%R'%t"
+      "%%T='%T'%t"
+      "%%r='%r'%t"
+      "%%X='%X'%t"
+      "%%EX='%EX'%t"
+      "%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%H='00'\t"
+           "%OH='00'\t"
+           "%I='12'\t"
+           "%OI='12'\t"
+           "%M='00'\t"
+           "%OM='00'\t"
+           "%S='00'\t"
+           "%OS='00'\t"
+           "%p='AM'\t"
+           "%R='00:00'\t"
+           "%T='00:00:00'\t"
+           "%r='12:00:00 AM'\t"
+           "%X='00:00:00'\t"
+           "%EX='00:00:00'\t"
+           "\n"),
+        fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%H='23'\t"
+           "%OH='23'\t"
+           "%I='11'\t"
+           "%OI='11'\t"
+           "%M='31'\t"
+           "%OM='31'\t"
+           "%S='30.123'\t"
+           "%OS='30.123'\t"
+           "%p='PM'\t"
+           "%R='23:31'\t"
+           "%T='23:31:30.123'\t"
+           "%r='11:31:30 PM'\t"
+           "%X='23:31:30'\t"
+           "%EX='23:31:30'\t"
+           "\n"),
+        fmt,
+        cr::gps_time<cr::milliseconds>(918'603'105'123ms)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use the global locale (fr_FR)
+  check(SV("%H='00'\t"
+           "%OH='00'\t"
+           "%I='12'\t"
+           "%OI='12'\t"
+           "%M='00'\t"
+           "%OM='00'\t"
+           "%S='00'\t"
+           "%OS='00'\t"
+#if defined(_AIX)
+           "%p='AM'\t"
+#else
+           "%p=''\t"
+#endif
+           "%R='00:00'\t"
+           "%T='00:00:00'\t"
+#ifdef _WIN32
+           "%r='00:00:00'\t"
+#elif defined(_AIX)
+           "%r='12:00:00 AM'\t"
+#elif defined(__APPLE__) || defined(__FreeBSD__)
+           "%r=''\t"
+#else
+           "%r='12:00:00 '\t"
+#endif
+           "%X='00:00:00'\t"
+           "%EX='00:00:00'\t"
+           "\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%H='23'\t"
+           "%OH='23'\t"
+           "%I='11'\t"
+           "%OI='11'\t"
+           "%M='31'\t"
+           "%OM='31'\t"
+           "%S='30,123'\t"
+           "%OS='30,123'\t"
+#if defined(_AIX)
+           "%p='PM'\t"
+#else
+           "%p=''\t"
+#endif
+           "%R='23:31'\t"
+           "%T='23:31:30,123'\t"
+#ifdef _WIN32
+           "%r='23:31:30'\t"
+#elif defined(_AIX)
+           "%r='11:31:30 PM'\t"
+#elif defined(__APPLE__) || defined(__FreeBSD__)
+           "%r=''\t"
+#else
+           "%r='11:31:30 '\t"
+#endif
+           "%X='23:31:30'\t"
+           "%EX='23:31:30'\t"
+           "\n"),
+        lfmt,
+        cr::gps_time<cr::milliseconds>(918'603'105'123ms)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use supplied locale (ja_JP). This locale has a different alternate.
+#if defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
+  check(loc,
+        SV("%H='00'\t"
+           "%OH='00'\t"
+           "%I='12'\t"
+           "%OI='12'\t"
+           "%M='00'\t"
+           "%OM='00'\t"
+           "%S='00'\t"
+           "%OS='00'\t"
+#  if defined(__APPLE__)
+           "%p='AM'\t"
+#  else
+           "%p='午前'\t"
+#  endif
+           "%R='00:00'\t"
+           "%T='00:00:00'\t"
+#  if defined(__APPLE__) || defined(__FreeBSD__)
+#    if defined(__APPLE__)
+           "%r='12:00:00 AM'\t"
+#    else
+           "%r='12:00:00 午前'\t"
+#    endif
+           "%X='00時00分00秒'\t"
+           "%EX='00時00分00秒'\t"
+#  elif defined(_WIN32)
+           "%r='0:00:00'\t"
+           "%X='0:00:00'\t"
+           "%EX='0:00:00'\t"
+#  else
+           "%r='午前12:00:00'\t"
+           "%X='00:00:00'\t"
+           "%EX='00:00:00'\t"
+#  endif
+           "\n"),
+        lfmt,
+        cr::hh_mm_ss(0s));
+
+  check(loc,
+        SV("%H='23'\t"
+           "%OH='23'\t"
+           "%I='11'\t"
+           "%OI='11'\t"
+           "%M='31'\t"
+           "%OM='31'\t"
+           "%S='30.123'\t"
+           "%OS='30.123'\t"
+#  if defined(__APPLE__)
+           "%p='PM'\t"
+#  else
+           "%p='午後'\t"
+#  endif
+           "%R='23:31'\t"
+           "%T='23:31:30.123'\t"
+#  if defined(__APPLE__) || defined(__FreeBSD__)
+#    if defined(__APPLE__)
+           "%r='11:31:30 PM'\t"
+#    else
+           "%r='11:31:30 午後'\t"
+#    endif
+           "%X='23時31分30秒'\t"
+           "%EX='23時31分30秒'\t"
+#  elif defined(_WIN32)
+           "%r='23:31:30'\t"
+           "%X='23:31:30'\t"
+           "%EX='23:31:30'\t"
+#  else
+           "%r='午後11:31:30'\t"
+           "%X='23:31:30'\t"
+           "%EX='23:31:30'\t"
+#  endif
+           "\n"),
+        lfmt,
+        cr::hh_mm_ss(23h + 31min + 30s + 123ms));
+#else  // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
+  check(loc,
+        SV("%H='00'\t"
+           "%OH='〇'\t"
+           "%I='12'\t"
+           "%OI='十二'\t"
+           "%M='00'\t"
+           "%OM='〇'\t"
+           "%S='00'\t"
+           "%OS='〇'\t"
+           "%p='午前'\t"
+           "%R='00:00'\t"
+           "%T='00:00:00'\t"
+           "%r='午前12時00分00秒'\t"
+           "%X='00時00分00秒'\t"
+           "%EX='00時00分00秒'\t"
+           "\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%H='23'\t"
+           "%OH='二十三'\t"
+           "%I='11'\t"
+           "%OI='十一'\t"
+           "%M='31'\t"
+           "%OM='三十一'\t"
+           "%S='30.123'\t"
+           "%OS='三十.123'\t"
+           "%p='午後'\t"
+           "%R='23:31'\t"
+           "%T='23:31:30.123'\t"
+           "%r='午後11時31分30秒'\t"
+           "%X='23時31分30秒'\t"
+           "%EX='23時31分30秒'\t"
+           "\n"),
+        lfmt,
+        cr::gps_time<cr::milliseconds>(918'603'105'123ms)); // 23:31:30 GPS Friday, 13 February 2009
+#endif // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_date_time() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt  = SV("{:%%c='%c'%t%%Ec='%Ec'%n}");
+  constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%c='%c'%t%%Ec='%Ec'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%c='Sun Jan  6 00:00:00 1980'\t%Ec='Sun Jan  6 00:00:00 1980'\n"),
+        fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(SV("%c='Fri Feb 13 23:31:30 2009'\t%Ec='Fri Feb 13 23:31:30 2009'\n"),
+        fmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use the global locale (fr_FR)
+  check(
+// https://sourceware.org/bugzilla/show_bug.cgi?id=24054
+#if defined(__powerpc__) && defined(__linux__)
+      SV("%c='dim. 06 janv. 1980 00:00:00 GPS'\t%Ec='dim. 06 janv. 1958 00:00:00 GPS'\n"),
+#elif defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29
+      SV("%c='dim. 06 janv. 1980 00:00:00 GMT'\t%Ec='dim. 06 janv. 1980 00:00:00 GMT'\n"),
+#elif defined(_AIX)
+      SV("%c=' 1 janvier 1980 à 00:00:00 GPS'\t%Ec=' 6 janvier 1980 à 00:00:00 GPS'\n"),
+#elif defined(__APPLE__)
+      SV("%c='Dim  6 jan 00:00:00 1980'\t%Ec='Dim  6 jan 00:00:00 1980'\n"),
+#elif defined(_WIN32)
+      SV("%c='06/01/1980 00:00:00'\t%Ec='06/01/1980 00:00:00'\n"),
+#elif defined(__FreeBSD__)
+      SV("%c='dim.  6 janv. 00:00:00 1980'\t%Ec='dim.  6 janv. 00:00:00 1980'\n"),
+#else
+      SV("%c='dim. 06 janv. 1980 00:00:00'\t%Ec='dim. 06 janv. 1980 00:00:00'\n"),
+#endif
+      lfmt,
+      cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(
+// https://sourceware.org/bugzilla/show_bug.cgi?id=24054
+#if defined(__powerpc__) && defined(__linux__)
+      SV("%c='ven. 13 févr. 2009 23:31:30 GPS'\t%Ec='ven. 13 févr. 2009 23:31:30 GPS'\n"),
+#elif defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29
+      SV("%c='ven. 13 févr. 2009 23:31:30 GMT'\t%Ec='ven. 13 févr. 2009 23:31:30 GMT'\n"),
+#elif defined(_AIX)
+      SV("%c='13 février 2009 à 23:31:30 GPS'\t%Ec='13 février 2009 à 23:31:30 GPS'\n"),
+#elif defined(__APPLE__)
+      SV("%c='Ven 13 fév 23:31:30 2009'\t%Ec='Ven 13 fév 23:31:30 2009'\n"),
+#elif defined(_WIN32)
+      SV("%c='13/02/2009 23:31:30'\t%Ec='13/02/2009 23:31:30'\n"),
+#elif defined(__FreeBSD__)
+      SV("%c='ven. 13 févr. 23:31:30 2009'\t%Ec='ven. 13 févr. 23:31:30 2009'\n"),
+#else
+      SV("%c='ven. 13 févr. 2009 23:31:30'\t%Ec='ven. 13 févr. 2009 23:31:30'\n"),
+#endif
+      lfmt,
+      cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+
+  // Use supplied locale (ja_JP). This locale has a different alternate.a
+#if defined(__APPLE__) || defined(__FreeBSD__)
+  check(loc,
+        SV("%c='水  1/ 6 00:00:00 1980'\t%Ec='水  1/ 6 00:00:00 1980'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+  check(loc,
+        SV("%c='金  2/13 23:31:30 2009'\t%Ec='金  2/13 23:31:30 2009'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#elif defined(_AIX)                     // defined(__APPLE__)|| defined(__FreeBSD__)
+  check(loc,
+        SV("%c='1980年01月 6日 00:00:00 GPS'\t%Ec='1980年01月 6日 00:00:00 GPS'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+  check(loc,
+        SV("%c='2009年02月13日 23:31:30 GPS'\t%Ec='2009年02月13日 23:31:30 GPS'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#elif defined(_WIN32)                   // defined(__APPLE__)|| defined(__FreeBSD__)
+  check(loc,
+        SV("%c='1980/01/06 0:00:00'\t%Ec='1980/01/06 0:00:00'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+  check(loc,
+        SV("%c='2009/02/13 23:31:30'\t%Ec='2009/02/13 23:31:30'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#else                                   // defined(__APPLE__)|| defined(__FreeBSD__)
+  check(loc,
+        SV("%c='1980年01月06日 00時00分00秒'\t%Ec='昭和55年01月06日 00時00分00秒'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  check(loc,
+        SV("%c='2009年02月13日 23時31分30秒'\t%Ec='平成21年02月13日 23時31分30秒'\n"),
+        lfmt,
+        cr::gps_seconds(918'603'105s)); // 23:31:30 GPS Friday, 13 February 2009
+#endif                                  // defined(__APPLE__)|| defined(__FreeBSD__)
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_time_zone() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  constexpr std::basic_string_view<CharT> fmt  = SV("{:%%z='%z'%t%%Ez='%Ez'%t%%Oz='%Oz'%t%%Z='%Z'%n}");
+  constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%z='%z'%t%%Ez='%Ez'%t%%Oz='%Oz'%t%%Z='%Z'%n}");
+
+  const std::locale loc(LOCALE_ja_JP_UTF_8);
+  std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
+
+  // Non localized output using C-locale
+  check(SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='GPS'\n"),
+        fmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  // Use the global locale (fr_FR)
+  check(SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='GPS'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  // Use supplied locale (ja_JP).
+  check(loc,
+        SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='GPS'\n"),
+        lfmt,
+        cr::gps_seconds(0s)); // 00:00:00 GPS Sunday, 6 January 1980
+
+  std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values() {
+  test_valid_values_year<CharT>();
+  test_valid_values_month<CharT>();
+  test_valid_values_day<CharT>();
+  test_valid_values_weekday<CharT>();
+  test_valid_values_day_of_year<CharT>();
+  test_valid_values_week<CharT>();
+  test_valid_values_iso_8601_week<CharT>();
+  test_valid_values_date<CharT>();
+  test_valid_values_time<CharT>();
+  test_valid_values_date_time<CharT>();
+  test_valid_values_time_zone<CharT>();
+}
+
+template <class CharT>
+static void test() {
+  using namespace std::literals::chrono_literals;
+  namespace cr = std::chrono;
+
+  test_no_chrono_specs<CharT>();
+  test_valid_values<CharT>();
+  check_invalid_types<CharT>(
+      {SV("a"),  SV("A"),  SV("b"),  SV("B"),  SV("c"),  SV("C"),  SV("d"),  SV("D"),  SV("e"),  SV("F"),  SV("g"),
+       SV("G"),  SV("h"),  SV("H"),  SV("I"),  SV("j"),  SV("m"),  SV("M"),  SV("p"),  SV("r"),  SV("R"),  SV("S"),
+       SV("T"),  SV("u"),  SV("U"),  SV("V"),  SV("w"),  SV("W"),  SV("x"),  SV("X"),  SV("y"),  SV("Y"),  SV("z"),
+       SV("Z"),  SV("Ec"), SV("EC"), SV("Ex"), SV("EX"), SV("Ey"), SV("EY"), SV("Ez"), SV("Od"), SV("Oe"), SV("OH"),
+       SV("OI"), SV("Om"), SV("OM"), SV("OS"), SV("Ou"), SV("OU"), SV("OV"), SV("Ow"), SV("OW"), SV("Oy"), SV("Oz")},
+      cr::gps_seconds(0s));
+
+  check_exception("The format specifier expects a '%' or a '}'", SV("{:A"), cr::gps_seconds(0s));
+  check_exception("The chrono specifiers contain a '{'", SV("{:%%{"), cr::gps_seconds(0s));
+  check_exception("End of input while parsing a conversion specifier", SV("{:%"), cr::gps_seconds(0s));
+  check_exception("End of input while parsing the modifier E", SV("{:%E"), cr::gps_seconds(0s));
+  check_exception("End of input while parsing the modifier O", SV("{:%O"), cr::gps_seconds(0s));
+
+  // Precision not allowed
+  check_exception("The format specifier expects a '%' or a '}'", SV("{:.3}"), cr::gps_seconds(0s));
+}
+
+int main(int, char**) {
+  test<char>();
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+#endif
+
+  return 0;
+}



More information about the libcxx-commits mailing list