[libcxx-commits] [libcxx] [libc++][chrono] implements UTC clock. (PR #90393)
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Dec 10 08:18:34 PST 2024
https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/90393
>From 9977622a2ae8e3d4c57727f7a5c7bee6bb7242c9 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Mon, 15 Apr 2024 19:56:41 +0200
Subject: [PATCH 1/3] [libc++][chrono] implements UTC clock.
While implementing this feature and its associated LWG issues it turns out
- LWG3316 Correctly define epoch for utc_clock / utc_timepoint
only added non-normative wording to the standard.
Implements parts of:
- P0355 Extending <chrono> to Calendars and Time Zones
- P1361 Integration of chrono with text formatting
- LWG3359 <chrono> leap second support should allow for negative leap seconds
---
libcxx/benchmarks/CMakeLists.txt | 1 +
libcxx/benchmarks/utc_clock.bench.cpp | 54 +
libcxx/docs/Status/Cxx20.rst | 2 +-
libcxx/docs/Status/Cxx20Issues.csv | 2 +-
libcxx/docs/Status/FormatPaper.csv | 2 +-
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__chrono/convert_to_tm.h | 22 +
libcxx/include/__chrono/formatter.h | 18 +
libcxx/include/__chrono/ostream.h | 12 +
libcxx/include/__chrono/utc_clock.h | 155 +++
libcxx/include/chrono | 38 +
libcxx/include/module.modulemap | 4 +
libcxx/modules/std/chrono.inc | 12 +-
.../diagnostics/chrono.nodiscard.verify.cpp | 14 +
.../get_leap_second_info.pass.cpp | 124 ++
.../time.clock.utc.members/from_sys.pass.cpp | 106 ++
.../time.clock.utc.members/to_sys.pass.cpp | 115 ++
.../get_leap_second_info.pass.cpp | 126 +++
.../leap_second_info.members.pass.cpp | 37 +
.../time.clock.utc.members/from_sys.pass.cpp | 213 ++++
.../time.clock.utc.members/now.pass.cpp | 30 +
.../time.clock.utc.members/to_sys.pass.cpp | 147 +++
.../time.clock.utc/types.compile.pass.cpp | 59 +
.../time.clock.utc/utc_time.ostream.pass.cpp | 165 +++
.../time/time.syn/formatter.utc_time.pass.cpp | 1004 +++++++++++++++++
.../concept.formattable.compile.pass.cpp | 2 +-
26 files changed, 2459 insertions(+), 6 deletions(-)
create mode 100644 libcxx/benchmarks/utc_clock.bench.cpp
create mode 100644 libcxx/include/__chrono/utc_clock.h
create mode 100644 libcxx/test/libcxx/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
create mode 100644 libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
create mode 100644 libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
create mode 100644 libcxx/test/std/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
create mode 100644 libcxx/test/std/time/time.clock/time.clock.utc/leap_second_info.members.pass.cpp
create mode 100644 libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
create mode 100644 libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/now.pass.cpp
create mode 100644 libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
create mode 100644 libcxx/test/std/time/time.clock/time.clock.utc/types.compile.pass.cpp
create mode 100644 libcxx/test/std/time/time.clock/time.clock.utc/utc_time.ostream.pass.cpp
create mode 100644 libcxx/test/std/time/time.syn/formatter.utc_time.pass.cpp
diff --git a/libcxx/benchmarks/CMakeLists.txt b/libcxx/benchmarks/CMakeLists.txt
index 2101f9c71788c1..5a88395cda20ea 100644
--- a/libcxx/benchmarks/CMakeLists.txt
+++ b/libcxx/benchmarks/CMakeLists.txt
@@ -231,6 +231,7 @@ set(BENCHMARK_TESTS
system_error.bench.cpp
to_chars.bench.cpp
unordered_set_operations.bench.cpp
+ utc_clock.bench.cpp
util_smartptr.bench.cpp
variant_visit_1.bench.cpp
variant_visit_2.bench.cpp
diff --git a/libcxx/benchmarks/utc_clock.bench.cpp b/libcxx/benchmarks/utc_clock.bench.cpp
new file mode 100644
index 00000000000000..255b1dde52aa67
--- /dev/null
+++ b/libcxx/benchmarks/utc_clock.bench.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <chrono>
+
+#include "benchmark/benchmark.h"
+
+// Benchmarks the performance of the UTC <-> system time conversions. These
+// operations determine the sum of leap second insertions at a specific time.
+
+static void BM_from_sys(benchmark::State& state) {
+ std::chrono::sys_days date{std::chrono::July / 1 / state.range(0)};
+ for (auto _ : state)
+ benchmark::DoNotOptimize(std::chrono::utc_clock::from_sys(date));
+}
+
+BENCHMARK(BM_from_sys)
+ ->Arg(1970) // before the first leap seconds
+ ->Arg(1979) // in the first half of inserted leap seconds
+ ->Arg(1993) // in the second half of inserted leap seconds
+ ->Arg(2100); // after the last leap second
+
+BENCHMARK(BM_from_sys)->Arg(1970)->Arg(1979)->Arg(1993)->Arg(2100)->Threads(4);
+BENCHMARK(BM_from_sys)->Arg(1970)->Arg(1979)->Arg(1993)->Arg(2100)->Threads(16);
+
+static void BM_to_sys(benchmark::State& state) {
+ // 59 sec offset means we pass th UTC offset for the leap second; assuming
+ // there won't be more than 59 leap seconds ever.
+ std::chrono::utc_seconds date{
+ std::chrono::sys_days{std::chrono::July / 1 / state.range(0)}.time_since_epoch() + std::chrono::seconds{59}};
+ for (auto _ : state)
+ benchmark::DoNotOptimize(std::chrono::utc_clock::to_sys(date));
+}
+
+BENCHMARK(BM_to_sys)
+ ->Arg(1970) // before the first leap seconds
+ ->Arg(1979) // in the first half of inserted leap seconds
+ ->Arg(1993) // in the second half of inserted leap seconds
+ ->Arg(2100); // after the last leap second
+
+BENCHMARK(BM_to_sys)->Arg(1970)->Arg(1979)->Arg(1993)->Arg(2100)->Threads(4);
+BENCHMARK(BM_to_sys)->Arg(1970)->Arg(1979)->Arg(1993)->Arg(2100)->Threads(16);
+
+int main(int argc, char** argv) {
+ benchmark::Initialize(&argc, argv);
+ if (benchmark::ReportUnrecognizedArguments(argc, argv))
+ return 1;
+
+ benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/libcxx/docs/Status/Cxx20.rst b/libcxx/docs/Status/Cxx20.rst
index c00d6fb2372869..9f429c5b73b28a 100644
--- a/libcxx/docs/Status/Cxx20.rst
+++ b/libcxx/docs/Status/Cxx20.rst
@@ -54,9 +54,9 @@ Paper Status
* ``Input parsers`` not done
* ``Stream output`` Obsolete due to `P1361R2 <https://wg21.link/P1361R2>`_ "Integration of chrono with text formatting"
* ``Time zone and leap seconds`` In Progress
+ * ``UTC clock`` Clang 19
* ``TAI clock`` not done
* ``GPS clock`` not done
- * ``UTC clock`` not done
.. _issues-status-cxx20:
diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index f617ffd13315d3..2b650f336b9ce9 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -238,7 +238,7 @@
"`3313 <https://wg21.link/LWG3313>`__","``join_view::iterator::operator--``\ is incorrectly constrained","Prague","|Complete|","14.0","|ranges|"
"`3314 <https://wg21.link/LWG3314>`__","Is stream insertion behavior locale dependent when ``Period::type``\ is ``micro``\ ?","Prague","|Complete|","16.0","|chrono|"
"`3315 <https://wg21.link/LWG3315>`__","Correct Allocator Default Behavior","Prague","",""
-"`3316 <https://wg21.link/LWG3316>`__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","Prague","","","|chrono|"
+"`3316 <https://wg21.link/LWG3316>`__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","Prague","|Nothing To Do|","","|chrono|"
"`3317 <https://wg21.link/LWG3317>`__","Incorrect ``operator<<``\ for floating-point durations","Prague","","","|chrono|"
"`3318 <https://wg21.link/LWG3318>`__","Clarify whether clocks can represent time before their epoch","Prague","","","|chrono|"
"`3319 <https://wg21.link/LWG3319>`__","Properly reference specification of IANA time zone database","Prague","|Nothing To Do|","","|chrono|"
diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index f29f1f7ca74875..bf733e264cde22 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -2,7 +2,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`P1361 <https://wg21.link/P1361>`__ `P2372 <https://wg21.link/P2372>`__,"Formatting chrono"
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::duration<Rep, Period>``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_time<Duration>``",,Mark de Wever,|Complete|,17.0
-`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
+`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",,Mark de Wever,|Complete|,19.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[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::file_time<Duration>``",,Mark de Wever,|Complete|,17.0
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8d0ffd6ed725bd..19c0753527b1dd 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -271,6 +271,7 @@ set(files
__chrono/time_zone_link.h
__chrono/tzdb.h
__chrono/tzdb_list.h
+ __chrono/utc_clock.h
__chrono/weekday.h
__chrono/year.h
__chrono/year_month.h
diff --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h
index 881a4970822d8e..7f9aea00c01250 100644
--- a/libcxx/include/__chrono/convert_to_tm.h
+++ b/libcxx/include/__chrono/convert_to_tm.h
@@ -24,6 +24,7 @@
#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/time_point.h>
+#include <__chrono/utc_clock.h>
#include <__chrono/weekday.h>
#include <__chrono/year.h>
#include <__chrono/year_month.h>
@@ -96,6 +97,25 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const chrono::sys_time<_Duration> __tp
return __result;
}
+# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
+ !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+template <class _Tm, class _Duration>
+_LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::utc_time<_Duration> __tp) {
+ _Tm __result = std::__convert_to_tm<_Tm>(chrono::utc_clock::to_sys(__tp));
+
+ if (chrono::get_leap_second_info(__tp).is_leap_second)
+ ++__result.tm_sec;
+
+ return __result;
+}
+
+# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
+ // !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
// Convert a chrono (calendar) time point, or dururation to the given _Tm type,
// which must have the same properties as std::tm.
template <class _Tm, class _ChronoT>
@@ -108,6 +128,8 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
if constexpr (__is_time_point<_ChronoT>) {
if constexpr (same_as<typename _ChronoT::clock, chrono::system_clock>)
return std::__convert_to_tm<_Tm>(__value);
+ else if constexpr (same_as<typename _ChronoT::clock, chrono::utc_clock>)
+ return std::__convert_to_tm<_Tm>(__value);
else if constexpr (same_as<typename _ChronoT::clock, chrono::file_clock>)
return std::__convert_to_tm<_Tm>(_ChronoT::clock::to_sys(__value));
else if constexpr (same_as<typename _ChronoT::clock, chrono::local_t>)
diff --git a/libcxx/include/__chrono/formatter.h b/libcxx/include/__chrono/formatter.h
index e9b81c3de8a700..6a6f60341c1394 100644
--- a/libcxx/include/__chrono/formatter.h
+++ b/libcxx/include/__chrono/formatter.h
@@ -28,6 +28,7 @@
#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/time_point.h>
+#include <__chrono/utc_clock.h>
#include <__chrono/weekday.h>
#include <__chrono/year.h>
#include <__chrono/year_month.h>
@@ -673,6 +674,23 @@ struct _LIBCPP_TEMPLATE_VIS formatter<chrono::sys_time<_Duration>, _CharT> : pub
}
};
+# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
+ !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+template <class _Duration, __fmt_char_type _CharT>
+struct _LIBCPP_TEMPLATE_VIS formatter<chrono::utc_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
+public:
+ using _Base = __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 // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
+ // !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
template <class _Duration, __fmt_char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
public:
diff --git a/libcxx/include/__chrono/ostream.h b/libcxx/include/__chrono/ostream.h
index bb0341bc3ec631..5a5f379d4dddfe 100644
--- a/libcxx/include/__chrono/ostream.h
+++ b/libcxx/include/__chrono/ostream.h
@@ -22,6 +22,7 @@
#include <__chrono/statically_widen.h>
#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
+#include <__chrono/utc_clock.h>
#include <__chrono/weekday.h>
#include <__chrono/year.h>
#include <__chrono/year_month.h>
@@ -56,6 +57,17 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_days& __dp) {
return __os << year_month_day{__dp};
}
+# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
+ !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+template <class _CharT, class _Traits, class _Duration>
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
+operator<<(basic_ostream<_CharT, _Traits>& __os, const utc_time<_Duration>& __tp) {
+ return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
+}
+# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
+ // !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
template <class _CharT, class _Traits, class _Duration>
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const file_time<_Duration> __tp) {
diff --git a/libcxx/include/__chrono/utc_clock.h b/libcxx/include/__chrono/utc_clock.h
new file mode 100644
index 00000000000000..a3db972f26a4e2
--- /dev/null
+++ b/libcxx/include/__chrono/utc_clock.h
@@ -0,0 +1,155 @@
+// -*- 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_UTC_CLOCK_H
+#define _LIBCPP___CHRONO_UTC_CLOCK_H
+
+#include <version>
+// Enable the contents of the header only when libc++ was built with experimental features enabled.
+#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+# include <__chrono/duration.h>
+# include <__chrono/system_clock.h>
+# include <__chrono/time_point.h>
+# include <__chrono/tzdb.h>
+# include <__chrono/tzdb_list.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 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
+ !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+namespace chrono {
+
+class utc_clock;
+
+template <class _Duration>
+using utc_time = time_point<utc_clock, _Duration>;
+using utc_seconds = utc_time<seconds>;
+
+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; // The system_clock is not steady.
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static time_point now() { return from_sys(system_clock::now()); }
+
+ template <class _Duration>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static sys_time<common_type_t<_Duration, seconds>>
+ to_sys(const utc_time<_Duration>& __time);
+
+ template <class _Duration>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static utc_time<common_type_t<_Duration, seconds>>
+ from_sys(const sys_time<_Duration>& __time) {
+ using _Rp = utc_time<common_type_t<_Duration, seconds>>;
+ // TODO TZDB investigate optimizations.
+ //
+ // The leap second database stores all transitions, this mean to calculate
+ // the current number of leap seconds the code needs to iterate over all
+ // leap seconds to accumulate the sum. Then the sum can be used to determine
+ // the sys_time. Accessing the database involves acquiring a mutex.
+ //
+ // The historic entries in the database are immutable. Hard-coding these
+ // values in a table would allow:
+ // - To store the sum, allowing a binary search on the data.
+ // - Avoid acquiring a mutex.
+ // The disadvantage are:
+ // - A slightly larger code size.
+ //
+ // There are two optimization directions
+ // - hard-code the database and do a linear search for future entries. This
+ // search can start at the back, and should probably contain very few
+ // entries. (Adding leap seconds is quite rare and new release of libc++
+ // can add the new entries; they are announced half a year before they are
+ // added.)
+ // - During parsing the leap seconds store an additional database in the
+ // dylib with the list of the sum of the leap seconds. In that case there
+ // can be a private function __get_utc_to_sys_table that returns the
+ // table.
+ //
+ // Note for to_sys there are no optimizations to be done; it uses
+ // get_leap_second_info. The function get_leap_second_info could benefit
+ // from optimizations as described above; again both options apply.
+
+ // Both UTC and the system clock use the same epoch. The Standard
+ // specifies from 1970-01-01 even when UTC starts at
+ // 1972-01-01 00:00:10 TAI. So when the sys_time is before epoch we can be
+ // sure there both clocks return the same value.
+
+ const tzdb& __tzdb = chrono::get_tzdb();
+ _Rp __result{__time.time_since_epoch()};
+ for (const auto& __leap_second : __tzdb.leap_seconds) {
+ if (__time < __leap_second)
+ return __result;
+
+ __result += __leap_second.value();
+ }
+ return __result;
+ }
+};
+
+struct leap_second_info {
+ bool is_leap_second;
+ seconds elapsed;
+};
+
+template <class _Duration>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI leap_second_info get_leap_second_info(const utc_time<_Duration>& __time) {
+ const tzdb& __tzdb = chrono::get_tzdb();
+ if (__tzdb.leap_seconds.empty())
+ return {false, chrono::seconds{0}};
+
+ sys_seconds __sys{chrono::floor<seconds>(__time).time_since_epoch()};
+ seconds __elapsed{0};
+ for (const auto& __leap_second : __tzdb.leap_seconds) {
+ if (__sys == __leap_second.date() + __elapsed)
+ return {__leap_second.value() > 0s, // only positive leap seconds are considered leap seconds
+ __elapsed + __leap_second.value()};
+
+ if (__sys < __leap_second.date() + __elapsed)
+ return {false, __elapsed};
+
+ __elapsed += __leap_second.value();
+ }
+
+ return {false, __elapsed};
+}
+
+template <class _Duration>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time<common_type_t<_Duration, seconds>>
+utc_clock::to_sys(const utc_time<_Duration>& __time) {
+ using _Dp = common_type_t<_Duration, seconds>;
+ leap_second_info __info = get_leap_second_info(__time);
+
+ sys_time<common_type_t<_Duration, seconds>> __result{__time.time_since_epoch() - __info.elapsed};
+ if (__info.is_leap_second)
+ return chrono::floor<seconds>(__result) + chrono::seconds{1} - _Dp{1};
+
+ return __result;
+}
+
+} // namespace chrono
+
+# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM)
+ // && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
+
+#endif // _LIBCPP___CHRONO_UTC_CLOCK_H
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 23441ddb239865..0b1c8040b26012 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -300,6 +300,41 @@ template<class charT, class traits> // C++20
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os, const sys_days& dp);
+// [time.clock.utc], class utc_clock
+class utc_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<utc_clock>;
+ static constexpr bool is_steady = unspecified;
+
+ static time_point now();
+
+ template<class Duration>
+ static sys_time<common_type_t<Duration, seconds>>
+ to_sys(const utc_time<Duration>& t);
+ template<class Duration>
+ static utc_time<common_type_t<Duration, seconds>>
+ from_sys(const sys_time<Duration>& t);
+};
+
+template<class Duration>
+using utc_time = time_point<utc_clock, Duration>; // C++20
+using utc_seconds = utc_time<seconds>; // C++20
+
+template<class charT, class traits, class Duration> // C++20
+ basic_ostream<charT, traits>&
+ operator<<(basic_ostream<charT, traits>& os, const utc_time<Duration>& t);
+
+struct leap_second_info { // C++20
+ bool is_leap_second;
+ seconds elapsed;
+};
+
+template<class Duration> // C++20
+ leap_second_info get_leap_second_info(const utc_time<Duration>& ut);
+
class file_clock // C++20
{
public:
@@ -846,6 +881,8 @@ strong_ordering operator<=>(const time_zone_link& x, const time_zone_link& y);
namespace std {
template<class Duration, class charT>
struct formatter<chrono::sys_time<Duration>, charT>; // C++20
+ template<class Duration, class charT>
+ struct formatter<chrono::utc_time<Duration>, charT>; // C++20
template<class Duration, class charT>
struct formatter<chrono::filetime<Duration>, charT>; // C++20
template<class Duration, class charT>
@@ -962,6 +999,7 @@ constexpr chrono::year operator ""y(unsigned lo
# include <__chrono/time_zone_link.h>
# include <__chrono/tzdb.h>
# include <__chrono/tzdb_list.h>
+# include <__chrono/utc_clock.h>
# include <__chrono/zoned_time.h>
# endif
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 9ffccf66ff0948..8b22023ed9e44e 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1150,6 +1150,10 @@ module std_private_chrono_tzdb_list [system] {
export *
}
module std_private_chrono_time_point [system] { header "__chrono/time_point.h" }
+module std_private_chrono_utc_clock [system] {
+ header "__chrono/utc_clock.h"
+ export std_private_chrono_time_point
+}
module std_private_chrono_weekday [system] { header "__chrono/weekday.h" }
module std_private_chrono_year [system] { header "__chrono/year.h" }
module std_private_chrono_year_month [system] { header "__chrono/year_month.h" }
diff --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc
index 87e32afbe4bdca..b8bb32e195bc4b 100644
--- a/libcxx/modules/std/chrono.inc
+++ b/libcxx/modules/std/chrono.inc
@@ -85,7 +85,10 @@ export namespace std {
using std::chrono::sys_seconds;
using std::chrono::sys_time;
-#if 0
+#if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
+ !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+# ifdef _LIBCPP_ENABLE_EXPERIMENTAL
+
// [time.clock.utc], class utc_clock
using std::chrono::utc_clock;
@@ -95,6 +98,8 @@ export namespace std {
using std::chrono::leap_second_info;
using std::chrono::get_leap_second_info;
+
+# if 0
// [time.clock.tai], class tai_clock
using std::chrono::tai_clock;
@@ -106,7 +111,10 @@ export namespace std {
using std::chrono::gps_seconds;
using std::chrono::gps_time;
-#endif
+# endif
+# endif // _LIBCPP_ENABLE_EXPERIMENTAL
+#endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
+
// [time.clock.file], type file_clock
using std::chrono::file_clock;
diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
index 6fed41bdb43edb..6726cb5af33bc6 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
@@ -78,4 +78,18 @@ void test() {
t::default_zone(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
t::locate_zone(""); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}
+
+ { // [time.clock.utc]
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::chrono::utc_clock::now();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::chrono::utc_clock::to_sys(std::chrono::utc_seconds{});
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::chrono::utc_clock::from_sys(std::chrono::sys_seconds{});
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::chrono::get_leap_second_info(std::chrono::utc_seconds{});
+ }
}
diff --git a/libcxx/test/libcxx/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp b/libcxx/test/libcxx/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
new file mode 100644
index 00000000000000..df137de346b884
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+//
+// class utc_clock;
+
+// template<class Duration>
+// leap_second_info get_leap_second_info(const utc_time<Duration>& ut);
+
+#include <chrono>
+#include <cassert>
+#include <fstream>
+#include <string>
+#include <string_view>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "test_tzdb.h"
+
+scoped_test_env env;
+[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
+const std::filesystem::path tzdata = env.create_file("zoneinfo/tzdata.zi");
+const std::filesystem::path leap_seconds = env.create_file("zoneinfo/leap-seconds.list");
+
+std::string_view std::chrono::__libcpp_tzdb_directory() {
+ static std::string result = dir.string();
+ return result;
+}
+
+static void write(std::string_view input) {
+ static int version = 0;
+
+ std::ofstream f{tzdata};
+ f << "# version " << version++ << '\n';
+ std::ofstream{leap_seconds}.write(input.data(), input.size());
+}
+
+template <class Duration>
+static void test_leap_second_info(
+ std::chrono::time_point<std::chrono::utc_clock, Duration> time, bool is_leap_second, std::chrono::seconds elapsed) {
+ std::chrono::leap_second_info result = std::chrono::get_leap_second_info(time);
+ TEST_REQUIRE(
+ result.is_leap_second == is_leap_second && result.elapsed == elapsed,
+ TEST_WRITE_CONCATENATED(
+ "\nExpected output [",
+ is_leap_second,
+ ", ",
+ elapsed,
+ "]\nActual output [",
+ result.is_leap_second,
+ ", ",
+ result.elapsed,
+ "]\n"));
+}
+
+// Note at the time of writing all leap seconds are positive. This test uses
+// fake data to test the behaviour of negative leap seconds.
+int main(int, const char**) {
+ using namespace std::literals::chrono_literals;
+
+ // Use small values for simplicity. The dates are seconds since 1.1.1900.
+ write(
+ R"(
+1 10
+60 11
+120 12
+180 11
+240 12
+300 13
+360 12
+)");
+
+ // Transitions from the start of UTC.
+ auto test_transition = [](std::chrono::utc_seconds time, std::chrono::seconds elapsed, bool positive) {
+ if (positive) {
+ // Every transition has the following tests
+ // - 1ns before the start of the transition is_leap_second -> false, elapsed -> elapsed
+ // - at the start of the transition is_leap_second -> true, elapsed -> elapsed + 1
+ // - 1ns after the start of the transition is_leap_second -> true, elapsed -> elapsed + 1
+ // - 1ns before the end of the transition is_leap_second -> true, elapsed -> elapsed + 1
+ // - at the end of the transition is_leap_second -> false, elapsed -> elapsed + 1
+
+ test_leap_second_info(time - 1ns, false, elapsed);
+ test_leap_second_info(time, true, elapsed + 1s);
+ test_leap_second_info(time + 1ns, true, elapsed + 1s);
+ test_leap_second_info(time + 1s - 1ns, true, elapsed + 1s);
+ test_leap_second_info(time + 1s, false, elapsed + 1s);
+ } else {
+ // Every transition has the following tests
+ // - 1ns before the transition is_leap_second -> false, elapsed -> elapsed
+ // - at the transition is_leap_second -> false elapsed -> elapsed - 1
+ // - 1ns after the transition is_leap_second -> false, elapsed -> elapsed - 1
+ test_leap_second_info(time - 1ns, false, elapsed);
+ test_leap_second_info(time, false, elapsed - 1s);
+ test_leap_second_info(time + 1ns, false, elapsed - 1s);
+ }
+ };
+
+ std::chrono::utc_seconds epoch{std::chrono::sys_days{std::chrono::January / 1 / 1900}.time_since_epoch()};
+ test_leap_second_info(epoch, false, 0s);
+
+ // The UTC times are:
+ // epoch + transition time in the database + leap seconds before the transition.
+ test_transition(epoch + 60s + 0s, 0s, true);
+ test_transition(epoch + 120s + 1s, 1s, true);
+ test_transition(epoch + 180s + 2s, 2s, false);
+ test_transition(epoch + 240s + 1s, 1s, true);
+ test_transition(epoch + 300s + 2s, 2s, true);
+ test_transition(epoch + 360s + 3s, 3s, false);
+}
diff --git a/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp b/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
new file mode 100644
index 00000000000000..8308ac95aed5bf
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+//
+// class utc_clock;
+
+// template<class Duration>
+// static utc_time<common_type_t<Duration, seconds>>
+// from_sys(const sys_time<Duration>& time);
+
+#include <chrono>
+#include <cassert>
+#include <fstream>
+#include <string>
+#include <string_view>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "test_tzdb.h"
+
+scoped_test_env env;
+[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
+const std::filesystem::path tzdata = env.create_file("zoneinfo/tzdata.zi");
+const std::filesystem::path leap_seconds = env.create_file("zoneinfo/leap-seconds.list");
+
+std::string_view std::chrono::__libcpp_tzdb_directory() {
+ static std::string result = dir.string();
+ return result;
+}
+
+static void write(std::string_view input) {
+ static int version = 0;
+
+ std::ofstream f{tzdata};
+ f << "# version " << version++ << '\n';
+ std::ofstream{leap_seconds}.write(input.data(), input.size());
+}
+
+template <class Duration>
+static void
+test_leap_seconds(std::chrono::time_point<std::chrono::system_clock, Duration> time, std::chrono::seconds expected) {
+ auto utc = std::chrono::utc_clock::from_sys(time);
+ auto diff = utc.time_since_epoch() - time.time_since_epoch();
+ TEST_REQUIRE(
+ diff == expected,
+ TEST_WRITE_CONCATENATED("\tTime: ", time, "\nExpected output ", expected, "\nActual output ", diff, '\n'));
+}
+
+// Note at the time of writing all leap seconds are positive. This test uses
+// fake data to test the behaviour of negative leap seconds.
+int main(int, const char**) {
+ using namespace std::literals::chrono_literals;
+
+ // Use small values for simplicity. The dates are seconds since 1.1.1970.
+ write(
+ R"(
+1 10
+60 11
+120 12
+180 11
+240 12
+300 13
+360 12
+)");
+
+ std::chrono::sys_days epoch = {std::chrono::January / 1 / 1900};
+ test_leap_seconds(epoch, 0s);
+
+ test_leap_seconds(epoch + 60s - 1ns, 0s);
+ test_leap_seconds(epoch + 60s, 1s);
+ test_leap_seconds(epoch + 60s + 1ns, 1s);
+
+ test_leap_seconds(epoch + 120s - 1ns, 1s);
+ test_leap_seconds(epoch + 120s, 2s);
+ test_leap_seconds(epoch + 120s + 1ns, 2s);
+
+ test_leap_seconds(epoch + 180s - 1ns, 2s);
+ test_leap_seconds(epoch + 180s, 1s);
+ test_leap_seconds(epoch + 180s + 1ns, 1s);
+
+ test_leap_seconds(epoch + 240s - 1ns, 1s);
+ test_leap_seconds(epoch + 240s, 2s);
+ test_leap_seconds(epoch + 240s + 1ns, 2s);
+
+ test_leap_seconds(epoch + 300s - 1ns, 2s);
+ test_leap_seconds(epoch + 300s, 3s);
+ test_leap_seconds(epoch + 300s + 1ns, 3s);
+
+ test_leap_seconds(epoch + 360s - 1ns, 3s);
+ test_leap_seconds(epoch + 360s, 2s);
+ test_leap_seconds(epoch + 360s + 1ns, 2s);
+}
diff --git a/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp b/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
new file mode 100644
index 00000000000000..7ab45aa6ae07d5
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+//
+// class utc_clock;
+
+// static sys_time<common_type_t<_Duration, seconds>>
+// to_sys(const utc_time<_Duration>& __time);
+
+#include <chrono>
+#include <cassert>
+#include <fstream>
+#include <string>
+#include <string_view>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "test_tzdb.h"
+
+scoped_test_env env;
+[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
+const std::filesystem::path tzdata = env.create_file("zoneinfo/tzdata.zi");
+const std::filesystem::path leap_seconds = env.create_file("zoneinfo/leap-seconds.list");
+
+std::string_view std::chrono::__libcpp_tzdb_directory() {
+ static std::string result = dir.string();
+ return result;
+}
+
+static void write(std::string_view input) {
+ static int version = 0;
+
+ std::ofstream f{tzdata};
+ f << "# version " << version++ << '\n';
+ std::ofstream{leap_seconds}.write(input.data(), input.size());
+}
+
+template <class Duration>
+static void test_leap_seconds(std::chrono::utc_time<Duration> time, std::chrono::sys_time<Duration> expected) {
+ auto result = std::chrono::utc_clock::to_sys(time);
+ TEST_REQUIRE(result == expected,
+ TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output ", result, '\n'));
+}
+
+// Note at the time of writing all leap seconds are positive. This test uses
+// fake data to test the behaviour of negative leap seconds.
+int main(int, const char**) {
+ using namespace std::literals::chrono_literals;
+
+ // Use small values for simplicity. The dates are seconds since 1.1.1970.
+ write(
+ R"(
+1 10
+60 11
+120 12
+180 11
+240 12
+300 13
+360 12
+)");
+
+ std::chrono::sys_seconds sys_epoch{std::chrono::sys_days{std::chrono::January / 1 / 1900}};
+ std::chrono::utc_seconds utc_epoch{sys_epoch.time_since_epoch()};
+
+ test_leap_seconds(utc_epoch, sys_epoch);
+ auto test_transition = [](std::chrono::sys_seconds sys, std::chrono::seconds elapsed, bool positive) {
+ std::chrono::utc_seconds utc = std::chrono::utc_seconds{sys.time_since_epoch()} + elapsed;
+ if (positive) {
+ // Every transition has the following tests
+ // - 1ns before the start of the transition no adjustment needed
+ // - at the start of the transition sys is clamped at the time just prior to the moment
+ // of the leap second insertion. The exact value depends
+ // on the resolution of the result type.
+ // - 1ns before the end of the transition sys is still clamped like before
+ // - at the end of the transition sys is 1s behind the utc time
+ // - 1ns after the end of the transition sys is still 1s behind the utc time
+ test_leap_seconds(utc - 1ns, sys - 1ns);
+ test_leap_seconds(utc, sys - 1s);
+ test_leap_seconds(utc + 0ns, sys - 1ns);
+ test_leap_seconds(utc + 1s - 1ns, sys - 1ns);
+ test_leap_seconds(utc + 1s, sys);
+ test_leap_seconds(utc + 1s + 0ns, sys + 0ns);
+ test_leap_seconds(utc + 1s + 1ns, sys + 1ns);
+ } else {
+ // Every transition has the following tests
+ // - 1ns before the transition no adjustment needed
+ // - at the transition sys is 1s ahead of the utc time
+ // - 1ns after the transition sys is still 1s ahead of the utc time
+ test_leap_seconds(utc - 1ns, sys - 1ns);
+ test_leap_seconds(utc, sys + 1s);
+ test_leap_seconds(utc + 1ns, sys + 1s + 1ns);
+ }
+ };
+
+ test_transition(sys_epoch + 60s, 0s, true);
+ test_transition(sys_epoch + 120s, 1s, true);
+ test_transition(sys_epoch + 180s, 2s, false);
+ test_transition(sys_epoch + 240s, 1s, true);
+ test_transition(sys_epoch + 300s, 2s, true);
+ test_transition(sys_epoch + 360s, 3s, false);
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
new file mode 100644
index 00000000000000..d6d1602da5a1ca
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+//
+// class utc_clock;
+
+// template<class Duration>
+// leap_second_info get_leap_second_info(const utc_time<Duration>& ut);
+
+#include <chrono>
+#include <cassert>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+template <class Duration>
+static void test_leap_second_info(
+ std::chrono::time_point<std::chrono::utc_clock, Duration> time, bool is_leap_second, std::chrono::seconds elapsed) {
+ std::chrono::leap_second_info result = std::chrono::get_leap_second_info(time);
+ TEST_REQUIRE(
+ result.is_leap_second == is_leap_second && result.elapsed == elapsed,
+ TEST_WRITE_CONCATENATED(
+ "\nExpected output [",
+ is_leap_second,
+ ", ",
+ elapsed,
+ "]\nActual output [",
+ result.is_leap_second,
+ ", ",
+ result.elapsed,
+ "]\n"));
+}
+
+static std::chrono::utc_seconds get_utc_time(long long seconds_since_1900) {
+ // The file leap-seconds.list stores dates since 1 January 1900, 00:00:00, we want
+ // seconds since 1 January 1970.
+ constexpr auto __offset =
+ std::chrono::sys_days{std::chrono::January / 1 / 1970} - std::chrono::sys_days{std::chrono::January / 1 / 1900};
+ return std::chrono::utc_seconds{std::chrono::seconds{seconds_since_1900} - __offset};
+}
+
+// Tests set of existing database entries at the time of writing.
+int main(int, const char**) {
+ using namespace std::literals::chrono_literals;
+
+ test_leap_second_info(std::chrono::utc_seconds::min(), false, 0s);
+
+ // Epoch transition no transitions.
+ test_leap_second_info(std::chrono::utc_seconds{-1s}, false, 0s);
+ test_leap_second_info(std::chrono::utc_seconds{0s}, false, 0s);
+ test_leap_second_info(std::chrono::utc_seconds{1s}, false, 0s);
+
+ // Transitions from the start of UTC.
+ auto test_transition = [](std::chrono::utc_seconds time, std::chrono::seconds elapsed, bool positive) {
+ // Note at the time of writing all leap seconds are positive so the else
+ // branch is never executed. The private test for this function tests
+ // negative leap seconds and uses the else branch.
+
+ if (positive) {
+ // Every transition has the following tests
+ // - 1ns before the start of the transition is_leap_second -> false, elapsed -> elapsed
+ // - at the start of the transition is_leap_second -> true, elapsed -> elapsed + 1
+ // - 1ns after the start of the transition is_leap_second -> true, elapsed -> elapsed + 1
+ // - 1ns before the end of the transition is_leap_second -> true, elapsed -> elapsed + 1
+ // - at the end of the transition is_leap_second -> false, elapsed -> elapsed + 1
+
+ test_leap_second_info(time - 1ns, false, elapsed);
+ test_leap_second_info(time, true, elapsed + 1s);
+ test_leap_second_info(time + 1ns, true, elapsed + 1s);
+ test_leap_second_info(time + 1s - 1ns, true, elapsed + 1s);
+ test_leap_second_info(time + 1s, false, elapsed + 1s);
+ } else {
+ // Every transition has the following tests
+ // - 1ns before the transition is_leap_second -> false, elapsed -> elapsed
+ // - at the transition is_leap_second -> false elapsed -> elapsed - 1
+ // - 1ns after the transition is_leap_second -> false, elapsed -> elapsed - 1
+ test_leap_second_info(time - 1ns, false, elapsed);
+ test_leap_second_info(time, false, elapsed - 1s);
+ test_leap_second_info(time + 1ns, false, elapsed - 1s);
+ }
+ };
+
+ // The timestamps are from leap-seconds.list in the IANA database.
+ // Note the times stamps are timestamps without leap seconds so the number
+ // here are incremented by x "leap seconds".
+ test_transition(get_utc_time(2287785600 + 0), 0s, true); // 1 Jul 1972
+ test_transition(get_utc_time(2303683200 + 1), 1s, true); // 1 Jan 1973
+ test_transition(get_utc_time(2335219200 + 2), 2s, true); // 1 Jan 1974
+ test_transition(get_utc_time(2366755200 + 3), 3s, true); // 1 Jan 1975
+ test_transition(get_utc_time(2398291200 + 4), 4s, true); // 1 Jan 1976
+ test_transition(get_utc_time(2429913600 + 5), 5s, true); // 1 Jan 1977
+ test_transition(get_utc_time(2461449600 + 6), 6s, true); // 1 Jan 1978
+ test_transition(get_utc_time(2492985600 + 7), 7s, true); // 1 Jan 1979
+ test_transition(get_utc_time(2524521600 + 8), 8s, true); // 1 Jan 1980
+ test_transition(get_utc_time(2571782400 + 9), 9s, true); // 1 Jul 1981
+ test_transition(get_utc_time(2603318400 + 10), 10s, true); // 1 Jul 1982
+ test_transition(get_utc_time(2634854400 + 11), 11s, true); // 1 Jul 1983
+ test_transition(get_utc_time(2698012800 + 12), 12s, true); // 1 Jul 1985
+ test_transition(get_utc_time(2776982400 + 13), 13s, true); // 1 Jan 1988
+ test_transition(get_utc_time(2840140800 + 14), 14s, true); // 1 Jan 1990
+ test_transition(get_utc_time(2871676800 + 15), 15s, true); // 1 Jan 1991
+ test_transition(get_utc_time(2918937600 + 16), 16s, true); // 1 Jul 1992
+ test_transition(get_utc_time(2950473600 + 17), 17s, true); // 1 Jul 1993
+ test_transition(get_utc_time(2982009600 + 18), 18s, true); // 1 Jul 1994
+ test_transition(get_utc_time(3029443200 + 19), 19s, true); // 1 Jan 1996
+ test_transition(get_utc_time(3076704000 + 20), 20s, true); // 1 Jul 1997
+ test_transition(get_utc_time(3124137600 + 21), 21s, true); // 1 Jan 1999
+ test_transition(get_utc_time(3345062400 + 22), 22s, true); // 1 Jan 2006
+ test_transition(get_utc_time(3439756800 + 23), 23s, true); // 1 Jan 2009
+ test_transition(get_utc_time(3550089600 + 24), 24s, true); // 1 Jul 2012
+ test_transition(get_utc_time(3644697600 + 25), 25s, true); // 1 Jul 2015
+ test_transition(get_utc_time(3692217600 + 26), 26s, true); // 1 Jan 2017
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/leap_second_info.members.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/leap_second_info.members.pass.cpp
new file mode 100644
index 00000000000000..79072254947ce7
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/leap_second_info.members.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// struct leap_second_info {
+// bool is_leap_second;
+// seconds elapsed;
+// };
+
+#include <chrono>
+#include <type_traits>
+
+// Validates whether:
+// - The members are present as non-const members.
+// - The struct is an aggregate.
+int main(int, const char**) {
+ static_assert(std::is_aggregate_v<std::chrono::leap_second_info>);
+
+ std::chrono::leap_second_info leap_second_info{.is_leap_second = false, .elapsed = std::chrono::seconds(0)};
+
+ [[maybe_unused]] bool is_leap_second = leap_second_info.is_leap_second;
+ [[maybe_unused]] std::chrono::seconds& elapsed = leap_second_info.elapsed;
+
+ return 0;
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
new file mode 100644
index 00000000000000..2dd061e230d7ce
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
@@ -0,0 +1,213 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+//
+// class utc_clock;
+
+// template<class Duration>
+// static utc_time<common_type_t<Duration, seconds>>
+// from_sys(const sys_time<Duration>& time);
+
+#include <chrono>
+#include <cassert>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+template <class Duration>
+static void test_leap_seconds(std::chrono::time_point<std::chrono::system_clock, Duration> time,
+ std::chrono::seconds leap_seconds) {
+ auto utc = std::chrono::utc_clock::from_sys(time);
+ auto diff = utc.time_since_epoch() - time.time_since_epoch();
+ TEST_REQUIRE(
+ diff == leap_seconds,
+ TEST_WRITE_CONCATENATED("\tTime: ", time, "\nExpected output ", leap_seconds, "\nActual output ", diff, '\n'));
+}
+
+// This test is based on the example in [time.clock.utc.members]/3
+static void test_example_standard() {
+ using namespace std::literals::chrono_literals;
+
+ auto t = std::chrono::sys_days{std::chrono::July / 1 / 2015} - 2ns;
+ test_leap_seconds(t, 25s);
+
+ t += 1ns;
+ test_leap_seconds(t, 25s);
+
+ t += 1ns;
+ test_leap_seconds(t, 26s);
+
+ t += 1ns;
+ test_leap_seconds(t, 26s);
+}
+
+// Tests set of existing database entries at the time of writing.
+static void test_transitions() {
+ using namespace std::literals::chrono_literals;
+
+ test_leap_seconds(std::chrono::sys_seconds::min(), 0s);
+ test_leap_seconds(std::chrono::sys_days::min(), 0s);
+
+ // Epoch transition no transitions.
+ test_leap_seconds(std::chrono::sys_seconds{-1s}, 0s);
+ test_leap_seconds(std::chrono::sys_seconds{0s}, 0s);
+ test_leap_seconds(std::chrono::sys_seconds{1s}, 0s);
+
+ // Transitions from the start of UTC.
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1972} - 1ns, 0s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1972}, 0s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1972} + 1ns, 0s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1972} - 1ns, 0s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1972}, 1s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1972} + 1ns, 1s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1973} - 1ns, 1s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1973}, 2s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1973} + 1ns, 2s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1974} - 1ns, 2s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1974}, 3s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1974} + 1ns, 3s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1975} - 1ns, 3s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1975}, 4s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1975} + 1ns, 4s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1976} - 1ns, 4s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1976}, 5s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1976} + 1ns, 5s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1977} - 1ns, 5s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1977}, 6s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1977} + 1ns, 6s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1978} - 1ns, 6s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1978}, 7s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1978} + 1ns, 7s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1979} - 1ns, 7s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1979}, 8s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1979} + 1ns, 8s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1980} - 1ns, 8s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1980}, 9s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1980} + 1ns, 9s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1981} - 1ns, 9s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1981}, 10s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1981} + 1ns, 10s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1982} - 1ns, 10s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1982}, 11s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1982} + 1ns, 11s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1983} - 1ns, 11s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1983}, 12s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1983} + 1ns, 12s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1985} - 1ns, 12s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1985}, 13s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1985} + 1ns, 13s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1988} - 1ns, 13s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1988}, 14s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1988} + 1ns, 14s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1990} - 1ns, 14s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1990}, 15s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1990} + 1ns, 15s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1991} - 1ns, 15s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1991}, 16s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1991} + 1ns, 16s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1992} - 1ns, 16s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1992}, 17s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1992} + 1ns, 17s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1993} - 1ns, 17s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1993}, 18s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1993} + 1ns, 18s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1994} - 1ns, 18s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1994}, 19s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1994} + 1ns, 19s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1996} - 1ns, 19s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1996}, 20s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1996} + 1ns, 20s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1997} - 1ns, 20s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1997}, 21s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 1997} + 1ns, 21s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1999} - 1ns, 21s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1999}, 22s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 1999} + 1ns, 22s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2006} - 1ns, 22s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2006}, 23s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2006} + 1ns, 23s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2009} - 1ns, 23s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2009}, 24s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2009} + 1ns, 24s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 2012} - 1ns, 24s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 2012}, 25s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 2012} + 1ns, 25s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 2015} - 1ns, 25s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 2015}, 26s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::July / 1 / 2015} + 1ns, 26s);
+
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2017} - 1ns, 26s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2017}, 27s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2017} + 1ns, 27s);
+
+ // This validates status when the tests were written.
+ // It's not possible to test the future; there might be additional leap
+ // seconds in the future.
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2024} - 1ns, 27s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2024}, 27s);
+ test_leap_seconds(std::chrono::sys_days{std::chrono::January / 1 / 2024} + 1ns, 27s);
+}
+
+// Tests whether the return type is the expected type.
+static void test_return_type() {
+ using namespace std::chrono;
+ using namespace std::literals::chrono_literals;
+
+ { std::same_as<utc_time<nanoseconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<nanoseconds>{0ns}); }
+ { std::same_as<utc_time<microseconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<microseconds>{0us}); }
+ { std::same_as<utc_time<milliseconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<milliseconds>{0ms}); }
+
+ { std::same_as<utc_time<seconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<seconds>{seconds{0}}); }
+
+ { std::same_as<utc_time<seconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<minutes>{minutes{0}}); }
+ { std::same_as<utc_time<seconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<hours>{hours{0}}); }
+ { std::same_as<utc_time<seconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<days>{days{0}}); }
+ { std::same_as<utc_time<seconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<weeks>{weeks{0}}); }
+ { std::same_as<utc_time<seconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<months>{months{0}}); }
+ { std::same_as<utc_time<seconds>> decltype(auto) _ = utc_clock::from_sys(sys_time<years>{years{0}}); }
+}
+
+int main(int, const char**) {
+ test_example_standard();
+ test_transitions();
+ test_return_type();
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/now.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/now.pass.cpp
new file mode 100644
index 00000000000000..9a90e618432ce9
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/now.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+//
+// class utc_clock;
+
+// static time_point now();
+
+#include <chrono>
+#include <cassert>
+
+int main(int, const char**) {
+ using clock = std::chrono::utc_clock;
+ std::same_as<clock::time_point> decltype(auto) t = clock::now();
+
+ assert(t >= clock::time_point::min());
+ assert(t <= clock::time_point::max());
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
new file mode 100644
index 00000000000000..7bac59a10ed29c
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// class utc_clock;
+
+// static sys_time<common_type_t<_Duration, seconds>>
+// to_sys(const utc_time<_Duration>& __time);
+
+#include <chrono>
+#include <cassert>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+template <class Duration>
+static void test_leap_seconds(std::chrono::utc_time<Duration> time, std::chrono::sys_time< Duration> expected) {
+ auto result = std::chrono::utc_clock::to_sys(time);
+ TEST_REQUIRE(
+ result == expected,
+ TEST_WRITE_CONCATENATED("\tTime: ", time, "\nExpected output ", expected, "\nActual output ", result, '\n'));
+}
+
+static std::chrono::sys_seconds get_sys_time(long long seconds_since_1900) {
+ // The file leap-seconds.list stores dates since 1 January 1900, 00:00:00, we want
+ // seconds since 1 January 1970.
+ constexpr auto __offset =
+ std::chrono::sys_days{std::chrono::January / 1 / 1970} - std::chrono::sys_days{std::chrono::January / 1 / 1900};
+ return std::chrono::sys_seconds{std::chrono::seconds{seconds_since_1900} - __offset};
+}
+
+// Tests the set of existing database entries at the time of writing. Since
+// the last leap second insertion is several years ago, it's expected all
+// systems have the same information. (Adding new entries in the future does
+// not affect this test.)
+static void test_transitions() {
+ using namespace std::literals::chrono_literals;
+
+ test_leap_seconds(std::chrono::utc_seconds::min(), std::chrono::sys_seconds::min());
+
+ // Epoch transition no transitions.
+ test_leap_seconds(std::chrono::utc_seconds{-1s}, std::chrono::sys_seconds{-1s});
+ test_leap_seconds(std::chrono::utc_seconds{0s}, std::chrono::sys_seconds{0s});
+ test_leap_seconds(std::chrono::utc_seconds{1s}, std::chrono::sys_seconds{1s});
+
+ // "sys" is the time of the transition to the next leap second.
+ // "elapsed" is the number of leap seconds before the transition.
+ // "positive" is the leap second added +1s? If not it's -1s.
+ auto test_transition = [](std::chrono::sys_seconds sys, std::chrono::seconds elapsed, bool positive) {
+ // Note at the time of writing all leap seconds are positive so the else
+ // branch is never executed. The private test for this function tests
+ // negative leap seconds and uses the else branch.
+
+ std::chrono::utc_seconds utc = std::chrono::utc_seconds{sys.time_since_epoch()} + elapsed;
+ if (positive) {
+ // Every transition has the following tests
+ // - 1ns before the start of the transition no adjustment needed
+ // - at the start of the transition sys is clamped at the time just prior to the moment
+ // of the leap second insertion. The exact value depends
+ // on the resolution of the result type.
+ // - 1ns before the end of the transition sys is still clamped like before
+ // - at the end of the transition sys is 1s behind the utc time
+ // - 1ns after the end of the transition sys is still 1s behind the utc time
+ test_leap_seconds(utc - 1ns, sys - 1ns);
+ test_leap_seconds(utc, sys - 1s);
+ test_leap_seconds(utc + 0ns, sys - 1ns);
+ test_leap_seconds(utc + 1s - 1ns, sys - 1ns);
+ test_leap_seconds(utc + 1s, sys);
+ test_leap_seconds(utc + 1s + 0ns, sys + 0ns);
+ test_leap_seconds(utc + 1s + 1ns, sys + 1ns);
+ } else {
+ // Every transition has the following tests
+ // - 1ns before the transition no adjustment needed
+ // - at the transition sys is 1s ahead of the utc time
+ // - 1ns after the transition sys is still 1s ahead of the utc time
+ test_leap_seconds(utc - 1ns, sys - 1ns);
+ test_leap_seconds(utc, sys + 1s);
+ test_leap_seconds(utc + 1ns, sys + 1s + 1ns);
+ }
+ };
+
+ // Transitions from the start of UTC.
+ test_transition(get_sys_time(2287785600), 0s, true); // 1 Jul 1972
+ test_transition(get_sys_time(2303683200), 1s, true); // 1 Jan 1973
+ test_transition(get_sys_time(2335219200), 2s, true); // 1 Jan 1974
+ test_transition(get_sys_time(2366755200), 3s, true); // 1 Jan 1975
+ test_transition(get_sys_time(2398291200), 4s, true); // 1 Jan 1976
+ test_transition(get_sys_time(2429913600), 5s, true); // 1 Jan 1977
+ test_transition(get_sys_time(2461449600), 6s, true); // 1 Jan 1978
+ test_transition(get_sys_time(2492985600), 7s, true); // 1 Jan 1979
+ test_transition(get_sys_time(2524521600), 8s, true); // 1 Jan 1980
+ test_transition(get_sys_time(2571782400), 9s, true); // 1 Jul 1981
+ test_transition(get_sys_time(2603318400), 10s, true); // 1 Jul 1982
+ test_transition(get_sys_time(2634854400), 11s, true); // 1 Jul 1983
+ test_transition(get_sys_time(2698012800), 12s, true); // 1 Jul 1985
+ test_transition(get_sys_time(2776982400), 13s, true); // 1 Jan 1988
+ test_transition(get_sys_time(2840140800), 14s, true); // 1 Jan 1990
+ test_transition(get_sys_time(2871676800), 15s, true); // 1 Jan 1991
+ test_transition(get_sys_time(2918937600), 16s, true); // 1 Jul 1992
+ test_transition(get_sys_time(2950473600), 17s, true); // 1 Jul 1993
+ test_transition(get_sys_time(2982009600), 18s, true); // 1 Jul 1994
+ test_transition(get_sys_time(3029443200), 19s, true); // 1 Jan 1996
+ test_transition(get_sys_time(3076704000), 20s, true); // 1 Jul 1997
+ test_transition(get_sys_time(3124137600), 21s, true); // 1 Jan 1999
+ test_transition(get_sys_time(3345062400), 22s, true); // 1 Jan 2006
+ test_transition(get_sys_time(3439756800), 23s, true); // 1 Jan 2009
+ test_transition(get_sys_time(3550089600), 24s, true); // 1 Jul 2012
+ test_transition(get_sys_time(3644697600), 25s, true); // 1 Jul 2015
+ test_transition(get_sys_time(3692217600), 26s, true); // 1 Jan 2017
+}
+
+// Tests whether the return type is the expected type.
+static void test_return_type() {
+ using namespace std::chrono;
+ using namespace std::literals::chrono_literals;
+
+ { std::same_as<sys_time<nanoseconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<nanoseconds>{0ns}); }
+ { std::same_as<sys_time<microseconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<microseconds>{0us}); }
+ { std::same_as<sys_time<milliseconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<milliseconds>{0ms}); }
+
+ { std::same_as<sys_time<seconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<seconds>{seconds{0}}); }
+
+ { std::same_as<sys_time<seconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<minutes>{minutes{0}}); }
+ { std::same_as<sys_time<seconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<hours>{hours{0}}); }
+ { std::same_as<sys_time<seconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<days>{days{0}}); }
+ { std::same_as<sys_time<seconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<weeks>{weeks{0}}); }
+ { std::same_as<sys_time<seconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<months>{months{0}}); }
+ { std::same_as<sys_time<seconds>> decltype(auto) _ = utc_clock::to_sys(utc_time<years>{years{0}}); }
+}
+
+int main(int, const char**) {
+ test_transitions();
+ test_return_type();
+}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/types.compile.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/types.compile.pass.cpp
new file mode 100644
index 00000000000000..97985ea74f7ad7
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/types.compile.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// class utc_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<utc_clock>;
+// static constexpr bool is_steady = unspecified;
+//
+// ...
+// };
+//
+// template<class Duration>
+// using utc_time = time_point<utc_clock, Duration>;
+// using utc_seconds = utc_time<seconds>;
+
+#include <concepts>
+#include <chrono>
+
+#include "test_macros.h"
+
+// class utc_clock
+using rep = std::chrono::utc_clock::rep;
+using period = std::chrono::utc_clock::period;
+using duration = std::chrono::utc_clock::duration;
+using time_point = std::chrono::utc_clock::time_point;
+constexpr bool is_steady = std::chrono::utc_clock::is_steady;
+
+// Tests the values. part of them are implementation defined.
+LIBCPP_STATIC_ASSERT(std::same_as<rep, std::chrono::system_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::system_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::utc_clock>>);
+LIBCPP_STATIC_ASSERT(is_steady == false);
+
+// typedefs
+static_assert(std::same_as<std::chrono::utc_time<int>, std::chrono::time_point<std::chrono::utc_clock, int>>);
+static_assert(std::same_as<std::chrono::utc_time<long>, std::chrono::time_point<std::chrono::utc_clock, long>>);
+static_assert(std::same_as<std::chrono::utc_seconds, std::chrono::utc_time<std::chrono::seconds>>);
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/utc_time.ostream.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/utc_time.ostream.pass.cpp
new file mode 100644
index 00000000000000..5813f478c2c32c
--- /dev/null
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/utc_time.ostream.pass.cpp
@@ -0,0 +1,165 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+// 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-incomplete-tzdb
+// XFAIL: availability-tzdb-missing
+
+// REQUIRES: locale.fr_FR.UTF-8
+// REQUIRES: locale.ja_JP.UTF-8
+
+// <chrono>
+
+// class utctem_clock;
+
+// template<class charT, class traits, class Duration>
+// basic_ostream<charT, traits>&
+// operator<<(basic_ostream<charT, traits>& os, const utc_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::utc_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::utc_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::utc_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;
+
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::nanoseconds>{946'688'523'123'456'789ns}) ==
+ SV("2000-01-01 01:01:41.123456789"));
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::microseconds>{946'688'523'123'456us}) ==
+ SV("2000-01-01 01:01:41.123456"));
+
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::milliseconds>{946'684'822'123ms}) ==
+ SV("2000-01-01 00:00:00.123"));
+ assert(stream_c_locale<CharT>(std::chrono::utc_seconds{1'234'567'890s}) == SV("2009-02-13 23:31:06"));
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::minutes>{20'576'131min}) ==
+ SV("2009-02-13 23:30:36"));
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::hours>{342'935h}) == SV("2009-02-13 22:59:36"));
+
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::duration<signed char, std::ratio<2, 1>>>{
+ std::chrono::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1970-01-01 00:02:00"));
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::duration<short, std::ratio<1, 2>>>{
+ std::chrono::duration<short, std::ratio<1, 2>>{3600}}) == SV("1970-01-01 00:30:00.0"));
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::duration<int, std::ratio<1, 4>>>{
+ std::chrono::duration<int, std::ratio<1, 4>>{3600}}) == SV("1970-01-01 00:15:00.00"));
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::duration<long, std::ratio<1, 10>>>{
+ std::chrono::duration<long, std::ratio<1, 10>>{36611}}) == SV("1970-01-01 01:01:01.1"));
+ assert(stream_c_locale<CharT>(std::chrono::utc_time<std::chrono::duration<long long, std::ratio<1, 100>>>{
+ std::chrono::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("2009-02-13 23:31:06.10"));
+}
+
+template <class CharT>
+static void test_fr_FR() {
+ using namespace std::literals::chrono_literals;
+
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::nanoseconds>{946'688'523'123'456'789ns}) ==
+ SV("2000-01-01 01:01:41,123456789"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::microseconds>{946'688'523'123'456us}) ==
+ SV("2000-01-01 01:01:41,123456"));
+
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::milliseconds>{946'684'822'123ms}) ==
+ SV("2000-01-01 00:00:00,123"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_seconds{1'234'567'890s}) == SV("2009-02-13 23:31:06"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::minutes>{20'576'131min}) ==
+ SV("2009-02-13 23:30:36"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::hours>{342'935h}) == SV("2009-02-13 22:59:36"));
+
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::duration<signed char, std::ratio<2, 1>>>{
+ std::chrono::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1970-01-01 00:02:00"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::duration<short, std::ratio<1, 2>>>{
+ std::chrono::duration<short, std::ratio<1, 2>>{3600}}) == SV("1970-01-01 00:30:00,0"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::duration<int, std::ratio<1, 4>>>{
+ std::chrono::duration<int, std::ratio<1, 4>>{3600}}) == SV("1970-01-01 00:15:00,00"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::duration<long, std::ratio<1, 10>>>{
+ std::chrono::duration<long, std::ratio<1, 10>>{36611}}) == SV("1970-01-01 01:01:01,1"));
+ assert(stream_fr_FR_locale<CharT>(std::chrono::utc_time<std::chrono::duration<long long, std::ratio<1, 100>>>{
+ std::chrono::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("2009-02-13 23:31:06,10"));
+}
+
+template <class CharT>
+static void test_ja_JP() {
+ using namespace std::literals::chrono_literals;
+
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::nanoseconds>{946'688'523'123'456'789ns}) ==
+ SV("2000-01-01 01:01:41.123456789"));
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::microseconds>{946'688'523'123'456us}) ==
+ SV("2000-01-01 01:01:41.123456"));
+
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::milliseconds>{946'684'822'123ms}) ==
+ SV("2000-01-01 00:00:00.123"));
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_seconds{1'234'567'890s}) == SV("2009-02-13 23:31:06"));
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::minutes>{20'576'131min}) ==
+ SV("2009-02-13 23:30:36"));
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::hours>{342'935h}) == SV("2009-02-13 22:59:36"));
+
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::duration<signed char, std::ratio<2, 1>>>{
+ std::chrono::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1970-01-01 00:02:00"));
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::duration<short, std::ratio<1, 2>>>{
+ std::chrono::duration<short, std::ratio<1, 2>>{3600}}) == SV("1970-01-01 00:30:00.0"));
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::duration<int, std::ratio<1, 4>>>{
+ std::chrono::duration<int, std::ratio<1, 4>>{3600}}) == SV("1970-01-01 00:15:00.00"));
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::duration<long, std::ratio<1, 10>>>{
+ std::chrono::duration<long, std::ratio<1, 10>>{36611}}) == SV("1970-01-01 01:01:01.1"));
+ assert(stream_ja_JP_locale<CharT>(std::chrono::utc_time<std::chrono::duration<long long, std::ratio<1, 100>>>{
+ std::chrono::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("2009-02-13 23:31:06.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.syn/formatter.utc_time.pass.cpp b/libcxx/test/std/time/time.syn/formatter.utc_time.pass.cpp
new file mode 100644
index 00000000000000..82ac50f4ea2a31
--- /dev/null
+++ b/libcxx/test/std/time/time.syn/formatter.utc_time.pass.cpp
@@ -0,0 +1,1004 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+// 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-incomplete-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::utc_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;
+
+ 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("1425-08-04 22:06:56"), SV("{}"), std::chrono::utc_seconds(-17'179'869'184s)); // Minimum value for 35 bits.
+ check(SV("1901-12-13 20:45:52"), SV("{}"), std::chrono::utc_seconds(-2'147'483'648s));
+
+ check(SV("1969-12-31 00:00:00"), SV("{}"), std::chrono::utc_seconds(-24h));
+ check(SV("1969-12-31 06:00:00"), SV("{}"), std::chrono::utc_seconds(-18h));
+ check(SV("1969-12-31 12:00:00"), SV("{}"), std::chrono::utc_seconds(-12h));
+ check(SV("1969-12-31 18:00:00"), SV("{}"), std::chrono::utc_seconds(-6h));
+ check(SV("1969-12-31 23:59:59"), SV("{}"), std::chrono::utc_seconds(-1s));
+
+ check(SV("1970-01-01 00:00:00"), SV("{}"), std::chrono::utc_seconds(0s));
+ check(SV("2000-01-01 00:00:00"), SV("{}"), std::chrono::utc_seconds(946'684'800s + 22s));
+ check(SV("2000-01-01 01:02:03"), SV("{}"), std::chrono::utc_seconds(946'688'523s + 22s));
+
+ check(SV("2038-01-19 03:14:07"), SV("{}"), std::chrono::utc_seconds(2'147'483'647s + 27s));
+ check(SV("2514-05-30 01:53:03"),
+ SV("{}"),
+ std::chrono::utc_seconds(17'179'869'183s + 27s)); // Maximum value for 35 bits.
+
+ check(SV("2000-01-01 01:02:03.123"),
+ SV("{}"),
+ std::chrono::utc_time<std::chrono::milliseconds>(946'688'523'123ms + 22s));
+
+ std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_valid_values_year() {
+ using namespace std::literals::chrono_literals;
+
+ 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='70'\t%Oy='70'\t%Ey='70'\t%Y='1970'\t%EY='1970'\n"),
+ fmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"),
+ fmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+
+ // Use the global locale (fr_FR)
+ check(SV("%C='19'\t%EC='19'\t%y='70'\t%Oy='70'\t%Ey='70'\t%Y='1970'\t%EY='1970'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on 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='70'\t%Oy='70'\t%Ey='70'\t%Y='1970'\t%EY='1970'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ 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,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX)||defined(__FreeBSD__)
+ check(loc,
+ SV("%C='19'\t%EC='昭和'\t%y='70'\t%Oy='七十'\t%Ey='45'\t%Y='1970'\t%EY='昭和45年'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%C='20'\t%EC='平成'\t%y='09'\t%Oy='九'\t%Ey='21'\t%Y='2009'\t%EY='平成21年'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on 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;
+
+ 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,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%b='May'\t%h='May'\t%B='May'\t%m='05'\t%Om='05'\n"),
+ fmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC 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,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+#else
+ check(SV("%b='janv.'\t%h='janv.'\t%B='janvier'\t%m='01'\t%Om='01'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+#endif
+
+ check(SV("%b='mai'\t%h='mai'\t%B='mai'\t%m='05'\t%Om='05'\n"),
+ lfmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC 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,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%b='5'\t%h='5'\t%B='5月'\t%m='05'\t%Om='05'\n"),
+ lfmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on 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,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%b='5月'\t%h='5月'\t%B='5月'\t%m='05'\t%Om='05'\n"),
+ lfmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on 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,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%b=' 5'\t%h=' 5'\t%B='5月'\t%m='05'\t%Om='05'\n"),
+ lfmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on 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,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%b=' 5月'\t%h=' 5月'\t%B='5月'\t%m='05'\t%Om='05'\n"),
+ lfmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033
+#else // _WIN32
+ check(loc,
+ SV("%b=' 1月'\t%h=' 1月'\t%B='1月'\t%m='01'\t%Om='一'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%b=' 5月'\t%h=' 5月'\t%B='5月'\t%m='05'\t%Om='五'\n"),
+ lfmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on 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;
+
+ 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='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"),
+ fmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"),
+ fmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+
+ // Use the global locale (fr_FR)
+ check(SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on 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("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+ check(loc,
+ SV("%d='01'\t%Od='一'\t%e=' 1'\t%Oe='一'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%d='13'\t%Od='十三'\t%e='13'\t%Oe='十三'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on 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;
+
+ 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='Thu'\t%A='Thursday'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"),
+ fmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%a='Sun'\t%A='Sunday'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+ fmt,
+ std::chrono::utc_seconds(4'294'967'295s)); // 06:28:15 UTC on Sunday, 7 February 2106
+
+ // Use the global locale (fr_FR)
+#if defined(__APPLE__)
+ check(SV("%a='Jeu'\t%A='Jeudi'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%a='Dim'\t%A='Dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+ lfmt,
+ std::chrono::utc_seconds(4'294'967'295s)); // 06:28:15 UTC on Sunday, 7 February 2106
+#else
+ check(SV("%a='jeu.'\t%A='jeudi'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%a='dim.'\t%A='dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+ lfmt,
+ std::chrono::utc_seconds(4'294'967'295s)); // 06:28:15 UTC 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='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
+ lfmt,
+ std::chrono::utc_seconds(4'294'967'295s)); // 06:28:15 UTC on Sunday, 7 February 2106
+#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+ check(loc,
+ SV("%a='木'\t%A='木曜日'\t%u='4'\t%Ou='四'\t%w='4'\t%Ow='四'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='七'\t%w='0'\t%Ow='〇'\n"),
+ lfmt,
+ std::chrono::utc_seconds(4'294'967'295s)); // 06:28:15 UTC 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;
+
+ 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='001'\n"), fmt, std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+ check(SV("%j='138'\n"), fmt, std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033
+
+ // Use the global locale (fr_FR)
+ check(SV("%j='001'\n"), lfmt, std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+ check(SV("%j='138'\n"), lfmt, std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033
+
+ // Use supplied locale (ja_JP). This locale has a different alternate.
+ check(loc, SV("%j='001'\n"), lfmt, std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(
+ loc, SV("%j='138'\n"), lfmt, std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on 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;
+
+ 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='00'\t%OU='00'\t%W='00'\t%OW='00'\n"),
+ fmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"),
+ fmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033
+
+ // Use the global locale (fr_FR)
+ check(SV("%U='00'\t%OU='00'\t%W='00'\t%OW='00'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"),
+ lfmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on 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='00'\t%OU='00'\t%W='00'\t%OW='00'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"),
+ lfmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on Wednesday, 18 May 2033
+#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+ check(loc,
+ SV("%U='00'\t%OU='〇'\t%W='00'\t%OW='〇'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%U='20'\t%OU='二十'\t%W='20'\t%OW='二十'\n"),
+ lfmt,
+ std::chrono::utc_seconds(2'000'000'000s)); // 03:33:20 UTC on 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;
+
+ 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='70'\t%G='1970'\t%V='01'\t%OV='01'\n"),
+ fmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"),
+ fmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+
+ // Use the global locale (fr_FR)
+ check(SV("%g='70'\t%G='1970'\t%V='01'\t%OV='01'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on 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='70'\t%G='1970'\t%V='01'\t%OV='01'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+ check(loc,
+ SV("%g='70'\t%G='1970'\t%V='01'\t%OV='一'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%g='09'\t%G='2009'\t%V='07'\t%OV='七'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on 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;
+
+ 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/01/70'\t%F='1970-01-01'\t%x='01/01/70'\t%Ex='01/01/70'\n"),
+ fmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='02/13/09'\t%Ex='02/13/09'\n"),
+ fmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+
+ // Use the global locale (fr_FR)
+#if defined(__APPLE__) || defined(__FreeBSD__)
+ check(SV("%D='01/01/70'\t%F='1970-01-01'\t%x='01.01.1970'\t%Ex='01.01.1970'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='13.02.2009'\t%Ex='13.02.2009'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+#else
+ check(SV("%D='01/01/70'\t%F='1970-01-01'\t%x='01/01/1970'\t%Ex='01/01/1970'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='13/02/2009'\t%Ex='13/02/2009'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on 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/01/70'\t%F='1970-01-01'\t%x='1970/01/01'\t%Ex='1970/01/01'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%D='02/13/09'\t%F='2009-02-13'\t%x='2009/02/13'\t%Ex='2009/02/13'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
+ check(loc,
+ SV("%D='01/01/70'\t%F='1970-01-01'\t%x='1970年01月01日'\t%Ex='昭和45年01月01日'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%D='02/13/09'\t%F='2009-02-13'\t%x='2009年02月13日'\t%Ex='平成21年02月13日'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on 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;
+
+ 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,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ 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,
+ std::chrono::utc_time<std::chrono::milliseconds>(
+ 1'234'567'890'123ms + 24s)); // 23:31:30 UTC on 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,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ 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,
+ std::chrono::utc_time<std::chrono::milliseconds>(
+ 1'234'567'890'123ms + 24s)); // 23:31:30 UTC on 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,
+ std::chrono::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,
+ std::chrono::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,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ 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,
+ std::chrono::utc_time<std::chrono::milliseconds>(
+ 1'234'567'890'123ms + 24s)); // 23:31:30 UTC on 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;
+
+ 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='Thu Jan 1 00:00:00 1970'\t%Ec='Thu Jan 1 00:00:00 1970'\n"),
+ fmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(SV("%c='Fri Feb 13 23:31:30 2009'\t%Ec='Fri Feb 13 23:31:30 2009'\n"),
+ fmt,
+ std::chrono::utc_seconds(1'234'567'890s + 24s)); // 23:31:30 UTC on 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='jeu. 01 janv. 1970 00:00:00 UTC'\t%Ec='jeu. 01 janv. 1970 00:00:00 UTC'\n"),
+#elif defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29
+ SV("%c='jeu. 01 janv. 1970 00:00:00 GMT'\t%Ec='jeu. 01 janv. 1970 00:00:00 GMT'\n"),
+#elif defined(_AIX)
+ SV("%c=' 1 janvier 1970 à 00:00:00 UTC'\t%Ec=' 1 janvier 1970 à 00:00:00 UTC'\n"),
+#elif defined(__APPLE__)
+ SV("%c='Jeu 1 jan 00:00:00 1970'\t%Ec='Jeu 1 jan 00:00:00 1970'\n"),
+#elif defined(_WIN32)
+ SV("%c='01/01/1970 00:00:00'\t%Ec='01/01/1970 00:00:00'\n"),
+#elif defined(__FreeBSD__)
+ SV("%c='jeu. 1 janv. 00:00:00 1970'\t%Ec='jeu. 1 janv. 00:00:00 1970'\n"),
+#else
+ SV("%c='jeu. 01 janv. 1970 00:00:00'\t%Ec='jeu. 01 janv. 1970 00:00:00'\n"),
+#endif
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ 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 UTC'\t%Ec='ven. 13 févr. 2009 23:31:30 UTC'\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 UTC'\t%Ec='13 février 2009 à 23:31:30 UTC'\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,
+ std::chrono::utc_seconds(1'234'567'890s + 24s)); // 23:31:30 UTC on 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/ 1 00:00:00 1970'\t%Ec='木 1/ 1 00:00:00 1970'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+ check(loc,
+ SV("%c='金 2/13 23:31:30 2009'\t%Ec='金 2/13 23:31:30 2009'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+#elif defined(_AIX) // defined(__APPLE__)|| defined(__FreeBSD__)
+ check(loc,
+ SV("%c='1970年01月 1日 00:00:00 UTC'\t%Ec='1970年01月 1日 00:00:00 UTC'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+ check(loc,
+ SV("%c='2009年02月13日 23:31:30 UTC'\t%Ec='2009年02月13日 23:31:30 UTC'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+#elif defined(_WIN32) // defined(__APPLE__)|| defined(__FreeBSD__)
+ check(loc,
+ SV("%c='1970/01/01 0:00:00'\t%Ec='1970/01/01 0:00:00'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+ check(loc,
+ SV("%c='2009/02/13 23:31:30'\t%Ec='2009/02/13 23:31:30'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
+#else // defined(__APPLE__)|| defined(__FreeBSD__)
+ check(loc,
+ SV("%c='1970年01月01日 00時00分00秒'\t%Ec='昭和45年01月01日 00時00分00秒'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ check(loc,
+ SV("%c='2009年02月13日 23時31分30秒'\t%Ec='平成21年02月13日 23時31分30秒'\n"),
+ lfmt,
+ std::chrono::utc_seconds(1'234'567'890s + 24s)); // 23:31:30 UTC on 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;
+
+ 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='UTC'\n"),
+ fmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ // Use the global locale (fr_FR)
+ check(SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='UTC'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ // Use supplied locale (ja_JP).
+ check(loc,
+ SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='UTC'\n"),
+ lfmt,
+ std::chrono::utc_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
+
+ std::locale::global(std::locale::classic());
+}
+
+template <class CharT>
+static void test_utc_transitions() {
+ using namespace std::literals::chrono_literals;
+
+ constexpr std::basic_string_view<CharT> fmt = SV("{:%F %T}");
+ check(SV("1972-06-30 23:59:59"), fmt, std::chrono::utc_seconds(78'796'799s));
+ check(SV("1972-06-30 23:59:60"), fmt, std::chrono::utc_seconds(78'796'800s));
+ check(SV("1972-07-01 00:00:00"), fmt, std::chrono::utc_seconds(78'796'801s));
+
+ check(SV("1972-12-31 23:59:59"), fmt, std::chrono::utc_seconds(94'694'400s));
+ check(SV("1972-12-31 23:59:60"), fmt, std::chrono::utc_seconds(94'694'401s));
+ check(SV("1973-01-01 00:00:00"), fmt, std::chrono::utc_seconds(94'694'402s));
+}
+
+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>();
+
+ test_utc_transitions<CharT>();
+}
+
+// In order to have the UTC seconds the number of leap seconds need to be
+// included in the UTC time. The number of leap seconds for times far in the
+// future are not yet known and may change in the future.
+template <class CharT>
+static void test() {
+ using namespace std::literals::chrono_literals;
+
+ 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")},
+ std::chrono::utc_seconds(0s));
+
+ check_exception("The format specifier expects a '%' or a '}'", SV("{:A"), std::chrono::utc_seconds(0s));
+ check_exception("The chrono specifiers contain a '{'", SV("{:%%{"), std::chrono::utc_seconds(0s));
+ check_exception("End of input while parsing a conversion specifier", SV("{:%"), std::chrono::utc_seconds(0s));
+ check_exception("End of input while parsing the modifier E", SV("{:%E"), std::chrono::utc_seconds(0s));
+ check_exception("End of input while parsing the modifier O", SV("{:%O"), std::chrono::utc_seconds(0s));
+
+ // Precision not allowed
+ check_exception("The format specifier expects a '%' or a '}'", SV("{:.3}"), std::chrono::utc_seconds(0s));
+}
+
+int main(int, char**) {
+ test<char>();
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test<wchar_t>();
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
index 94a1ea576bda0f..8355ca8f9ebd3a 100644
--- a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
@@ -149,7 +149,7 @@ void test_P1361() {
assert_is_formattable<std::chrono::microseconds, CharT>();
assert_is_formattable<std::chrono::sys_time<std::chrono::microseconds>, CharT>();
- //assert_is_formattable<std::chrono::utc_time<std::chrono::microseconds>, CharT>();
+ assert_is_formattable<std::chrono::utc_time<std::chrono::microseconds>, CharT>();
//assert_is_formattable<std::chrono::tai_time<std::chrono::microseconds>, CharT>();
//assert_is_formattable<std::chrono::gps_time<std::chrono::microseconds>, CharT>();
assert_is_formattable<std::chrono::file_time<std::chrono::microseconds>, CharT>();
>From 3fd4c66cf71e1d77c797fb4962ca95cb9ae3453f Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Mon, 8 Jul 2024 18:02:49 +0200
Subject: [PATCH 2/3] Review comments
---
.../time.clock.utc.members/to_sys.pass.cpp | 77 ++++++++++++++++++-
1 file changed, 75 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
index 7bac59a10ed29c..e599b4b411a75a 100644
--- a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
@@ -27,7 +27,7 @@
#include "concat_macros.h"
template <class Duration>
-static void test_leap_seconds(std::chrono::utc_time<Duration> time, std::chrono::sys_time< Duration> expected) {
+static void test_leap_seconds(std::chrono::utc_time<Duration> time, std::chrono::sys_time<Duration> expected) {
auto result = std::chrono::utc_clock::to_sys(time);
TEST_REQUIRE(
result == expected,
@@ -42,7 +42,7 @@ static std::chrono::sys_seconds get_sys_time(long long seconds_since_1900) {
return std::chrono::sys_seconds{std::chrono::seconds{seconds_since_1900} - __offset};
}
-// Tests the set of existing database entries at the time of writing. Since
+// Tests the set of existing database entries at the time of writing. Since
// the last leap second insertion is several years ago, it's expected all
// systems have the same information. (Adding new entries in the future does
// not affect this test.)
@@ -122,6 +122,78 @@ static void test_transitions() {
test_transition(get_sys_time(3692217600), 26s, true); // 1 Jan 2017
}
+// Tests the transition for clocks where the duration's rep is a floating-point type.
+static void test_transitions_floating_point() {
+ using namespace std::literals::chrono_literals;
+
+ // Based on test_transitions but uses a floating-point duration.
+ using F = float;
+
+ auto test_transition = [](std::chrono::sys_seconds sys, std::chrono::seconds elapsed, bool positive) {
+ // Note at the time of writing all leap seconds are positive so the else
+ // branch is never executed. The private test for this function tests
+ // negative leap seconds and uses the else branch.
+
+ std::chrono::utc_seconds utc = std::chrono::utc_seconds{sys.time_since_epoch()} + elapsed;
+
+ using D = std::chrono::duration<F>;
+ using S = std::chrono ::time_point<std::chrono::system_clock, D>;
+ using U = std::chrono ::time_point<std::chrono::utc_clock, D>;
+
+ S s{sys.time_since_epoch()};
+ bool is_leap_second = s.time_since_epoch().count() == sys.time_since_epoch().count();
+ assert(is_leap_second);
+
+ U u{utc.time_since_epoch()};
+ if (positive) {
+ test_leap_seconds(u - 1ns, s - 1ns);
+ test_leap_seconds(u, s - 1s);
+ test_leap_seconds(u + 0ns, s - 1ns);
+ test_leap_seconds(u + 1s - 1ns, s - 1ns);
+ test_leap_seconds(u + 1s, s);
+ test_leap_seconds(u + 1s + 0ns, s + 0ns);
+ test_leap_seconds(u + 1s + 1ns, s + 1ns);
+
+ test_leap_seconds(U{D{std::nextafter(u.time_since_epoch().count(), F{0})}},
+ S{D{std::nextafter(s.time_since_epoch().count(), F{0})}});
+ test_leap_seconds(u, S{D{s.time_since_epoch().count() - F{1}}});
+ test_leap_seconds(U{D{u.time_since_epoch().count() + F{1}}}, s);
+ test_leap_seconds(U{D{std::nextafter(u.time_since_epoch().count() + F{1}, std::numeric_limits<F>::max())}},
+ S{D{std::nextafter(s.time_since_epoch().count(), std::numeric_limits<F>::max())}});
+ }
+ };
+
+ // Transitions from the start of UTC.
+ test_transition(get_sys_time(2287785600), 0s, true); // 1 Jul 1972
+ test_transition(get_sys_time(2303683200), 1s, true); // 1 Jan 1973
+ test_transition(get_sys_time(2335219200), 2s, true); // 1 Jan 1974
+ test_transition(get_sys_time(2366755200), 3s, true); // 1 Jan 1975
+ test_transition(get_sys_time(2398291200), 4s, true); // 1 Jan 1976
+ test_transition(get_sys_time(2429913600), 5s, true); // 1 Jan 1977
+ test_transition(get_sys_time(2461449600), 6s, true); // 1 Jan 1978
+ test_transition(get_sys_time(2492985600), 7s, true); // 1 Jan 1979
+ test_transition(get_sys_time(2524521600), 8s, true); // 1 Jan 1980
+ test_transition(get_sys_time(2571782400), 9s, true); // 1 Jul 1981
+ test_transition(get_sys_time(2603318400), 10s, true); // 1 Jul 1982
+ test_transition(get_sys_time(2634854400), 11s, true); // 1 Jul 1983
+ test_transition(get_sys_time(2698012800), 12s, true); // 1 Jul 1985
+ test_transition(get_sys_time(2776982400), 13s, true); // 1 Jan 1988
+ test_transition(get_sys_time(2840140800), 14s, true); // 1 Jan 1990
+ test_transition(get_sys_time(2871676800), 15s, true); // 1 Jan 1991
+ test_transition(get_sys_time(2918937600), 16s, true); // 1 Jul 1992
+ test_transition(get_sys_time(2950473600), 17s, true); // 1 Jul 1993
+ test_transition(get_sys_time(2982009600), 18s, true); // 1 Jul 1994
+ test_transition(get_sys_time(3029443200), 19s, true); // 1 Jan 1996
+ test_transition(get_sys_time(3076704000), 20s, true); // 1 Jul 1997
+ test_transition(get_sys_time(3124137600), 21s, true); // 1 Jan 1999
+ test_transition(get_sys_time(3345062400), 22s, true); // 1 Jan 2006
+ test_transition(get_sys_time(3439756800), 23s, true); // 1 Jan 2009
+ test_transition(get_sys_time(3550089600), 24s, true); // 1 Jul 2012
+ test_transition(get_sys_time(3644697600), 25s, true); // 1 Jul 2015
+ test_transition(get_sys_time(3692217600), 26s, true); // 1 Jan 2017
+
+}
+
// Tests whether the return type is the expected type.
static void test_return_type() {
using namespace std::chrono;
@@ -143,5 +215,6 @@ static void test_return_type() {
int main(int, const char**) {
test_transitions();
+ test_transitions_floating_point();
test_return_type();
}
>From 4b630ead106e6dbacb2af966d680d1febcf578fd Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Tue, 10 Dec 2024 17:18:09 +0100
Subject: [PATCH 3/3] Address review comments.
---
libcxx/docs/Status/Cxx20.rst | 2 +-
libcxx/docs/Status/FormatPaper.csv | 2 +-
libcxx/include/__chrono/utc_clock.h | 4 +--
.../get_leap_second_info.pass.cpp | 33 ++++++++++++++++---
.../time.clock.utc.members/from_sys.pass.cpp | 2 ++
.../time.clock.utc.members/to_sys.pass.cpp | 2 ++
.../get_leap_second_info.pass.cpp | 6 ++--
.../leap_second_info.members.pass.cpp | 2 +-
.../time.clock.utc.members/from_sys.pass.cpp | 2 ++
.../time.clock.utc.members/now.pass.cpp | 7 ++++
.../time.clock.utc.members/to_sys.pass.cpp | 6 ++--
.../time.clock.utc/types.compile.pass.cpp | 2 +-
.../time.clock.utc/utc_time.ostream.pass.cpp | 2 +-
13 files changed, 56 insertions(+), 16 deletions(-)
diff --git a/libcxx/docs/Status/Cxx20.rst b/libcxx/docs/Status/Cxx20.rst
index 9f429c5b73b28a..fa7505fbc0db37 100644
--- a/libcxx/docs/Status/Cxx20.rst
+++ b/libcxx/docs/Status/Cxx20.rst
@@ -54,7 +54,7 @@ Paper Status
* ``Input parsers`` not done
* ``Stream output`` Obsolete due to `P1361R2 <https://wg21.link/P1361R2>`_ "Integration of chrono with text formatting"
* ``Time zone and leap seconds`` In Progress
- * ``UTC clock`` Clang 19
+ * ``UTC clock`` Clang 20
* ``TAI clock`` not done
* ``GPS clock`` not done
diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index bf733e264cde22..b51401e90ee8ee 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -2,7 +2,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`P1361 <https://wg21.link/P1361>`__ `P2372 <https://wg21.link/P2372>`__,"Formatting chrono"
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::duration<Rep, Period>``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_time<Duration>``",,Mark de Wever,|Complete|,17.0
-`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",,Mark de Wever,|Complete|,19.0
+`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",,Mark de Wever,|Complete|,20.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[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::file_time<Duration>``",,Mark de Wever,|Complete|,17.0
diff --git a/libcxx/include/__chrono/utc_clock.h b/libcxx/include/__chrono/utc_clock.h
index a3db972f26a4e2..3111991d87fa10 100644
--- a/libcxx/include/__chrono/utc_clock.h
+++ b/libcxx/include/__chrono/utc_clock.h
@@ -94,7 +94,7 @@ class utc_clock {
const tzdb& __tzdb = chrono::get_tzdb();
_Rp __result{__time.time_since_epoch()};
for (const auto& __leap_second : __tzdb.leap_seconds) {
- if (__time < __leap_second)
+ if (__leap_second > __time)
return __result;
__result += __leap_second.value();
@@ -111,7 +111,7 @@ struct leap_second_info {
template <class _Duration>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI leap_second_info get_leap_second_info(const utc_time<_Duration>& __time) {
const tzdb& __tzdb = chrono::get_tzdb();
- if (__tzdb.leap_seconds.empty())
+ if (__tzdb.leap_seconds.empty()) [[unlikely]]
return {false, chrono::seconds{0}};
sys_seconds __sys{chrono::floor<seconds>(__time).time_since_epoch()};
diff --git a/libcxx/test/libcxx/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp b/libcxx/test/libcxx/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
index df137de346b884..215cc980cdba26 100644
--- a/libcxx/test/libcxx/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
+++ b/libcxx/test/libcxx/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
@@ -17,7 +17,7 @@
// class utc_clock;
// template<class Duration>
-// leap_second_info get_leap_second_info(const utc_time<Duration>& ut);
+// std::chrono::leap_second_info get_leap_second_info(const utc_time<Duration>& ut);
#include <chrono>
#include <cassert>
@@ -44,9 +44,12 @@ std::string_view std::chrono::__libcpp_tzdb_directory() {
static void write(std::string_view input) {
static int version = 0;
- std::ofstream f{tzdata};
- f << "# version " << version++ << '\n';
- std::ofstream{leap_seconds}.write(input.data(), input.size());
+ {
+ std::ofstream f{tzdata};
+ f << "# version " << version++ << '\n';
+ std::ofstream{leap_seconds}.write(input.data(), input.size());
+ }
+ std::chrono::reload_tzdb();
}
template <class Duration>
@@ -67,9 +70,22 @@ static void test_leap_second_info(
"]\n"));
}
+static void test_no_leap_seconds_entries() {
+ using namespace std::literals::chrono_literals;
+
+ write("");
+
+ test_leap_second_info(
+ std::chrono::utc_seconds{std::chrono::sys_days{std::chrono::January / 1 / 1900}.time_since_epoch()}, false, 0s);
+ test_leap_second_info(
+ std::chrono::utc_seconds{std::chrono::sys_days{std::chrono::January / 1 / 2000}.time_since_epoch()}, false, 0s);
+ test_leap_second_info(
+ std::chrono::utc_seconds{std::chrono::sys_days{std::chrono::January / 1 / 3000}.time_since_epoch()}, false, 0s);
+}
+
// Note at the time of writing all leap seconds are positive. This test uses
// fake data to test the behaviour of negative leap seconds.
-int main(int, const char**) {
+static void test_negative_leap_seconds() {
using namespace std::literals::chrono_literals;
// Use small values for simplicity. The dates are seconds since 1.1.1900.
@@ -122,3 +138,10 @@ int main(int, const char**) {
test_transition(epoch + 300s + 2s, 2s, true);
test_transition(epoch + 360s + 3s, 3s, false);
}
+
+int main(int, const char**) {
+ test_no_leap_seconds_entries();
+ test_negative_leap_seconds();
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp b/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
index 8308ac95aed5bf..ba403c85ecca2e 100644
--- a/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
+++ b/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
@@ -103,4 +103,6 @@ int main(int, const char**) {
test_leap_seconds(epoch + 360s - 1ns, 3s);
test_leap_seconds(epoch + 360s, 2s);
test_leap_seconds(epoch + 360s + 1ns, 2s);
+
+ return 0;
}
diff --git a/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp b/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
index 7ab45aa6ae07d5..5856bfda0a8a65 100644
--- a/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
+++ b/libcxx/test/libcxx/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
@@ -112,4 +112,6 @@ int main(int, const char**) {
test_transition(sys_epoch + 240s, 1s, true);
test_transition(sys_epoch + 300s, 2s, true);
test_transition(sys_epoch + 360s, 3s, false);
+
+ return 0;
}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
index d6d1602da5a1ca..362cd8c1aab69d 100644
--- a/libcxx/test/std/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/get_leap_second_info.pass.cpp
@@ -47,9 +47,9 @@ static void test_leap_second_info(
static std::chrono::utc_seconds get_utc_time(long long seconds_since_1900) {
// The file leap-seconds.list stores dates since 1 January 1900, 00:00:00, we want
// seconds since 1 January 1970.
- constexpr auto __offset =
+ constexpr auto offset =
std::chrono::sys_days{std::chrono::January / 1 / 1970} - std::chrono::sys_days{std::chrono::January / 1 / 1900};
- return std::chrono::utc_seconds{std::chrono::seconds{seconds_since_1900} - __offset};
+ return std::chrono::utc_seconds{std::chrono::seconds{seconds_since_1900} - offset};
}
// Tests set of existing database entries at the time of writing.
@@ -123,4 +123,6 @@ int main(int, const char**) {
test_transition(get_utc_time(3550089600 + 24), 24s, true); // 1 Jul 2012
test_transition(get_utc_time(3644697600 + 25), 25s, true); // 1 Jul 2015
test_transition(get_utc_time(3692217600 + 26), 26s, true); // 1 Jan 2017
+
+ return 0;
}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/leap_second_info.members.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/leap_second_info.members.pass.cpp
index 79072254947ce7..f0f7b77d9313a0 100644
--- a/libcxx/test/std/time/time.clock/time.clock.utc/leap_second_info.members.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/leap_second_info.members.pass.cpp
@@ -30,7 +30,7 @@ int main(int, const char**) {
std::chrono::leap_second_info leap_second_info{.is_leap_second = false, .elapsed = std::chrono::seconds(0)};
- [[maybe_unused]] bool is_leap_second = leap_second_info.is_leap_second;
+ [[maybe_unused]] bool& is_leap_second = leap_second_info.is_leap_second;
[[maybe_unused]] std::chrono::seconds& elapsed = leap_second_info.elapsed;
return 0;
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
index 2dd061e230d7ce..c1209bb0025f72 100644
--- a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/from_sys.pass.cpp
@@ -210,4 +210,6 @@ int main(int, const char**) {
test_example_standard();
test_transitions();
test_return_type();
+
+ return 0;
}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/now.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/now.pass.cpp
index 9a90e618432ce9..58d4d848d8481b 100644
--- a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/now.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/now.pass.cpp
@@ -27,4 +27,11 @@ int main(int, const char**) {
assert(t >= clock::time_point::min());
assert(t <= clock::time_point::max());
+
+ auto t2 = clock::now();
+ assert(t2 - t >= std::chrono::seconds(0));
+ // This may fail if the tests takes a long time to complete.
+ assert(t2 - t < std::chrono::seconds(42));
+
+ return 0;
}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
index e599b4b411a75a..863f57b8d502c4 100644
--- a/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/time.clock.utc.members/to_sys.pass.cpp
@@ -37,9 +37,9 @@ static void test_leap_seconds(std::chrono::utc_time<Duration> time, std::chrono:
static std::chrono::sys_seconds get_sys_time(long long seconds_since_1900) {
// The file leap-seconds.list stores dates since 1 January 1900, 00:00:00, we want
// seconds since 1 January 1970.
- constexpr auto __offset =
+ constexpr auto offset =
std::chrono::sys_days{std::chrono::January / 1 / 1970} - std::chrono::sys_days{std::chrono::January / 1 / 1900};
- return std::chrono::sys_seconds{std::chrono::seconds{seconds_since_1900} - __offset};
+ return std::chrono::sys_seconds{std::chrono::seconds{seconds_since_1900} - offset};
}
// Tests the set of existing database entries at the time of writing. Since
@@ -217,4 +217,6 @@ int main(int, const char**) {
test_transitions();
test_transitions_floating_point();
test_return_type();
+
+ return 0;
}
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/types.compile.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/types.compile.pass.cpp
index 97985ea74f7ad7..e11aa83f32f694 100644
--- a/libcxx/test/std/time/time.clock/time.clock.utc/types.compile.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/types.compile.pass.cpp
@@ -41,7 +41,7 @@ using duration = std::chrono::utc_clock::duration;
using time_point = std::chrono::utc_clock::time_point;
constexpr bool is_steady = std::chrono::utc_clock::is_steady;
-// Tests the values. part of them are implementation defined.
+// Tests the values. Some of them are implementation-defined.
LIBCPP_STATIC_ASSERT(std::same_as<rep, std::chrono::system_clock::rep>);
static_assert(std::is_arithmetic_v<rep>);
static_assert(std::is_signed_v<rep>);
diff --git a/libcxx/test/std/time/time.clock/time.clock.utc/utc_time.ostream.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.utc/utc_time.ostream.pass.cpp
index 5813f478c2c32c..3dc1eb8ae20517 100644
--- a/libcxx/test/std/time/time.clock/time.clock.utc/utc_time.ostream.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.utc/utc_time.ostream.pass.cpp
@@ -21,7 +21,7 @@
// <chrono>
-// class utctem_clock;
+// using utc_time = ...;
// template<class charT, class traits, class Duration>
// basic_ostream<charT, traits>&
More information about the libcxx-commits
mailing list