diff options
author | Mark de Wever <koraq@xs4all.nl> | 2024-04-15 19:56:41 +0200 |
---|---|---|
committer | Mark de Wever <koraq@xs4all.nl> | 2024-04-28 20:57:35 +0200 |
commit | 77edef4dd99a16f6ac2668b098e3659ee52a134a (patch) | |
tree | 63e23ba43611ccef0353b27634fef50db5de9370 | |
parent | d094cded6d023bbffdca0af983ef64e00289c797 (diff) |
[libc++][chrono] implements UTC clock.upstream/users/mordante/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
26 files changed, 2459 insertions, 6 deletions
diff --git a/libcxx/benchmarks/CMakeLists.txt b/libcxx/benchmarks/CMakeLists.txt index 527a2acf2d3b..ea7ef6902f72 100644 --- a/libcxx/benchmarks/CMakeLists.txt +++ b/libcxx/benchmarks/CMakeLists.txt @@ -229,6 +229,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 000000000000..255b1dde52aa --- /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 23289dc6e596..baa1dc6d6138 100644 --- a/libcxx/docs/Status/Cxx20.rst +++ b/libcxx/docs/Status/Cxx20.rst @@ -55,9 +55,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 .. [#note-P0718] P0718: Implemented deprecation of ``shared_ptr`` atomic access APIs only. diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv index fed1c7e73624..556da4fc72f8 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 f29f1f7ca748..bf733e264cde 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 1296c536bc88..4aff14870305 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -285,6 +285,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 881a4970822d..7f9aea00c012 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 e9b81c3de8a7..6a6f60341c13 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 @@ public: } }; +# 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 ecf07a320c8b..4862a8ae326a 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 000000000000..a3db972f26a4 --- /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 4d8398af1a10..7ce527f120e9 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: @@ -828,6 +863,8 @@ 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> struct formatter<chrono::local_time<Duration>, charT>; // C++20 @@ -942,6 +979,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> # endif #endif diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 8727ab88f16c..11fae841673e 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -1161,6 +1161,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 1265e21dc54e..d5636b0e7c27 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 fea1e4417cc1..755c72b76085 100644 --- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp +++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp @@ -69,4 +69,18 @@ void test() { leap.date(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} leap.value(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} } + + { // [time.clock.utc] + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::chrono::utc_clock::now(); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::chrono::utc_clock::to_sys(std::chrono::utc_seconds{}); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::chrono::utc_clock::from_sys(std::chrono::sys_seconds{}); + + // expected-warning@+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 000000000000..df137de346b8 --- /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 000000000000..8308ac95aed5 --- /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 000000000000..7ab45aa6ae07 --- /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 000000000000..d6d1602da5a1 --- /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 000000000000..79072254947c --- /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 000000000000..2dd061e230d7 --- /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 000000000000..9a90e618432c --- /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 000000000000..7bac59a10ed2 --- /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 000000000000..97985ea74f7a --- /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 000000000000..5813f478c2c3 --- /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 000000000000..82ac50f4ea2a --- /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 94a1ea576bda..8355ca8f9ebd 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>(); |