[libc++][chrono] implements UTC clock. (#90393)

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
This commit is contained in:
Mark de Wever 2025-01-24 18:56:02 +01:00 committed by GitHub
parent bd8a818128
commit 0cd794d486
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 2653 additions and 5 deletions

View File

@ -238,7 +238,7 @@
"`LWG3313 <https://wg21.link/LWG3313>`__","``join_view::iterator::operator--``\ is incorrectly constrained","2020-02 (Prague)","|Complete|","14",""
"`LWG3314 <https://wg21.link/LWG3314>`__","Is stream insertion behavior locale dependent when ``Period::type``\ is ``micro``\ ?","2020-02 (Prague)","|Complete|","16",""
"`LWG3315 <https://wg21.link/LWG3315>`__","LWG3315: Correct Allocator Default Behavior","2020-02 (Prague)","|Complete|","",""
"`LWG3316 <https://wg21.link/LWG3316>`__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","2020-02 (Prague)","","",""
"`LWG3316 <https://wg21.link/LWG3316>`__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","2020-02 (Prague)","|Nothing To Do|","",""
"`LWG3317 <https://wg21.link/LWG3317>`__","Incorrect ``operator<<``\ for floating-point durations","2020-02 (Prague)","|Complete|","16",""
"`LWG3318 <https://wg21.link/LWG3318>`__","Clarify whether clocks can represent time before their epoch","2020-02 (Prague)","","",""
"`LWG3319 <https://wg21.link/LWG3319>`__","Properly reference specification of IANA time zone database","2020-02 (Prague)","|Nothing To Do|","",""

1 Issue # Issue Name Meeting Status First released version Notes
238 `LWG3313 <https://wg21.link/LWG3313>`__ ``join_view::iterator::operator--``\ is incorrectly constrained 2020-02 (Prague) |Complete| 14
239 `LWG3314 <https://wg21.link/LWG3314>`__ Is stream insertion behavior locale dependent when ``Period::type``\ is ``micro``\ ? 2020-02 (Prague) |Complete| 16
240 `LWG3315 <https://wg21.link/LWG3315>`__ LWG3315: Correct Allocator Default Behavior 2020-02 (Prague) |Complete|
241 `LWG3316 <https://wg21.link/LWG3316>`__ Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ 2020-02 (Prague) |Nothing To Do|
242 `LWG3317 <https://wg21.link/LWG3317>`__ Incorrect ``operator<<``\ for floating-point durations 2020-02 (Prague) |Complete| 16
243 `LWG3318 <https://wg21.link/LWG3318>`__ Clarify whether clocks can represent time before their epoch 2020-02 (Prague)
244 `LWG3319 <https://wg21.link/LWG3319>`__ Properly reference specification of IANA time zone database 2020-02 (Prague) |Nothing To Do|

View File

@ -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
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_time<Duration>``",,Mark de Wever,|Complete|,17
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,|Complete|,20
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",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

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -275,6 +275,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

View File

@ -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>
@ -98,6 +99,22 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const chrono::sys_time<_Duration> __tp
return __result;
}
# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_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_EXPERIMENTAL_TZDB)
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_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>
@ -110,6 +127,12 @@ _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);
# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else if constexpr (same_as<typename _ChronoT::clock, chrono::utc_clock>)
return std::__convert_to_tm<_Tm>(__value);
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
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>)

View File

@ -32,6 +32,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>
@ -719,6 +720,23 @@ public:
}
};
# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
template <class _Duration, __fmt_char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS formatter<chrono::utc_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
public:
using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
}
};
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
template <class _Duration, __fmt_char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
public:

View File

@ -26,6 +26,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>
@ -61,6 +62,18 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_days& __dp) {
return __os << year_month_day{__dp};
}
# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
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_EXPERIMENTAL_TZDB)
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
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) {

View File

@ -0,0 +1,163 @@
// -*- 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_EXPERIMENTAL_TZDB)
# include <__chrono/duration.h>
# include <__chrono/leap_second.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 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_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 (__leap_second > __time)
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()) [[unlikely]]
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)
// A time point may only be a leap second during a positive leap second
// insertion, since time points that occur during a (theoretical)
// negative leap second don't exist.
return {__leap_second.value() > 0s, __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 = chrono::get_leap_second_info(__time);
// [time.clock.utc.members]/2
// Returns: A sys_time t, such that from_sys(t) == u if such a mapping
// exists. Otherwise u represents a time_point during a positive leap
// second insertion, the conversion counts that leap second as not
// inserted, and the last representable value of sys_time prior to the
// insertion of the leap second is returned.
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 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM &&
// _LIBCPP_HAS_LOCALIZATION
_LIBCPP_END_NAMESPACE_STD
#endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
#endif // _LIBCPP___CHRONO_UTC_CLOCK_H

View File

@ -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:
@ -861,6 +896,8 @@ strong_ordering operator<=>(const time_zone_link& x, const time_zone_link& y);
namespace std {
template<class Duration, class charT>
struct formatter<chrono::sys_time<Duration>, charT>; // C++20
template<class Duration, class charT>
struct formatter<chrono::utc_time<Duration>, charT>; // C++20
template<class Duration, class charT>
struct formatter<chrono::filetime<Duration>, charT>; // C++20
template<class Duration, class charT>
@ -981,6 +1018,7 @@ constexpr chrono::year operator ""y(unsigned lo
# include <__chrono/time_zone_link.h>
# include <__chrono/tzdb.h>
# include <__chrono/tzdb_list.h>
# include <__chrono/utc_clock.h>
# include <__chrono/zoned_time.h>
# endif

View File

@ -980,6 +980,10 @@ module std [system] {
export std.string // public data member of type std::string
export std.vector // public data members of type std::vector
}
module utc_clock {
header "__chrono/utc_clock.h"
export std.chrono.time_point
}
module weekday { header "__chrono/weekday.h" }
module year_month_day { header "__chrono/year_month_day.h" }
module year_month_weekday { header "__chrono/year_month_weekday.h" }

View File

@ -84,7 +84,9 @@ export namespace std {
using std::chrono::sys_seconds;
using std::chrono::sys_time;
#if 0
#if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
# ifdef _LIBCPP_ENABLE_EXPERIMENTAL
// [time.clock.utc], class utc_clock
using std::chrono::utc_clock;
@ -94,6 +96,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;
@ -105,7 +109,10 @@ export namespace std {
using std::chrono::gps_seconds;
using std::chrono::gps_time;
#endif
# endif
# endif // _LIBCPP_ENABLE_EXPERIMENTAL
#endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
// [time.clock.file], type file_clock
using std::chrono::file_clock;

View File

@ -0,0 +1,60 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
// XFAIL: libcpp-has-no-experimental-tzdb
// XFAIL: availability-tzdb-missing
#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();
}

View File

@ -75,6 +75,20 @@ void test(std::chrono::time_zone tz, std::chrono::time_zone_link link, std::chro
t::locate_zone(""); // 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{});
}
{
std::chrono::zoned_time<std::chrono::seconds> zt;

View File

@ -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-experimental-tzdb
// XFAIL: availability-tzdb-missing
// <chrono>
//
// class utc_clock;
// template<class Duration>
// std::chrono::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());
}
std::chrono::reload_tzdb();
}
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 void test_no_leap_seconds_entries() {
using namespace std::literals::chrono_literals;
write("");
test_leap_second_info(
std::chrono::utc_seconds{std::chrono::sys_days{std::chrono::January / 1 / 1900}.time_since_epoch()}, false, 0s);
test_leap_second_info(
std::chrono::utc_seconds{std::chrono::sys_days{std::chrono::January / 1 / 2000}.time_since_epoch()}, false, 0s);
test_leap_second_info(
std::chrono::utc_seconds{std::chrono::sys_days{std::chrono::January / 1 / 3000}.time_since_epoch()}, false, 0s);
}
// Note at the time of writing all leap seconds are positive. This test uses
// fake data to test the behaviour of negative leap seconds.
static void test_negative_leap_seconds() {
using namespace std::literals::chrono_literals;
// Use small values for simplicity. The dates are seconds since 1.1.1900.
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);
}
int main(int, const char**) {
test_no_leap_seconds_entries();
test_negative_leap_seconds();
return 0;
}

View File

@ -0,0 +1,108 @@
//===----------------------------------------------------------------------===//
//
// 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-experimental-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);
return 0;
}

View File

@ -0,0 +1,117 @@
//===----------------------------------------------------------------------===//
//
// 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-experimental-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);
return 0;
}

View File

@ -0,0 +1,128 @@
//===----------------------------------------------------------------------===//
//
// 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-experimental-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
return 0;
}

View File

@ -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-experimental-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;
}

View File

@ -0,0 +1,245 @@
//===----------------------------------------------------------------------===//
//
// 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-experimental-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() {
namespace cr = std::chrono;
using namespace std::literals::chrono_literals;
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::nanoseconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::nanoseconds>{0ns});
}
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::microseconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::microseconds>{0us});
}
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::milliseconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::milliseconds>{0ms});
}
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::seconds>{cr::seconds{0}});
}
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::minutes>{cr::minutes{0}});
}
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::hours>{cr::hours{0}});
}
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::days>{cr::days{0}});
}
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::weeks>{cr::weeks{0}});
}
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::months>{cr::months{0}});
}
{
[[maybe_unused]] std::same_as<cr::utc_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::from_sys(cr::sys_time<cr::years>{cr::years{0}});
}
}
int main(int, const char**) {
test_example_standard();
test_transitions();
test_return_type();
return 0;
}

View File

@ -0,0 +1,38 @@
//===----------------------------------------------------------------------===//
//
// 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-experimental-tzdb
// XFAIL: availability-tzdb-missing
// <chrono>
//
// class utc_clock;
// static time_point now();
#include <chrono>
#include <concepts>
#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());
auto t2 = clock::now();
assert(t2 - t >= std::chrono::seconds(0));
// This may fail if the tests takes a long time to complete.
assert(t2 - t < std::chrono::seconds(42));
return 0;
}

View File

@ -0,0 +1,252 @@
//===----------------------------------------------------------------------===//
//
// 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-experimental-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 <cmath>
#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 the transition for clocks where the duration's rep is a floating-point type.
static void test_transitions_floating_point() {
using namespace std::literals::chrono_literals;
// Based on test_transitions but uses a floating-point duration.
using F = float;
auto test_transition = [](std::chrono::sys_seconds sys, std::chrono::seconds elapsed, bool positive) {
// Note at the time of writing all leap seconds are positive so the else
// branch is never executed. The private test for this function tests
// negative leap seconds and uses the else branch.
std::chrono::utc_seconds utc = std::chrono::utc_seconds{sys.time_since_epoch()} + elapsed;
using D = std::chrono::duration<F>;
using S = std::chrono ::time_point<std::chrono::system_clock, D>;
using U = std::chrono ::time_point<std::chrono::utc_clock, D>;
S s{sys.time_since_epoch()};
bool is_leap_second = s.time_since_epoch().count() == sys.time_since_epoch().count();
assert(is_leap_second);
U u{utc.time_since_epoch()};
if (positive) {
test_leap_seconds(u - 1ns, s - 1ns);
test_leap_seconds(u, s - 1s);
test_leap_seconds(u + 0ns, s - 1ns);
test_leap_seconds(u + 1s - 1ns, s - 1ns);
test_leap_seconds(u + 1s, s);
test_leap_seconds(u + 1s + 0ns, s + 0ns);
test_leap_seconds(u + 1s + 1ns, s + 1ns);
test_leap_seconds(U{D{std::nextafter(u.time_since_epoch().count(), F{0})}},
S{D{std::nextafter(s.time_since_epoch().count(), F{0})}});
test_leap_seconds(u, S{D{s.time_since_epoch().count() - F{1}}});
test_leap_seconds(U{D{u.time_since_epoch().count() + F{1}}}, s);
test_leap_seconds(U{D{std::nextafter(u.time_since_epoch().count() + F{1}, std::numeric_limits<F>::max())}},
S{D{std::nextafter(s.time_since_epoch().count(), std::numeric_limits<F>::max())}});
}
};
// Transitions from the start of UTC.
test_transition(get_sys_time(2287785600), 0s, true); // 1 Jul 1972
test_transition(get_sys_time(2303683200), 1s, true); // 1 Jan 1973
test_transition(get_sys_time(2335219200), 2s, true); // 1 Jan 1974
test_transition(get_sys_time(2366755200), 3s, true); // 1 Jan 1975
test_transition(get_sys_time(2398291200), 4s, true); // 1 Jan 1976
test_transition(get_sys_time(2429913600), 5s, true); // 1 Jan 1977
test_transition(get_sys_time(2461449600), 6s, true); // 1 Jan 1978
test_transition(get_sys_time(2492985600), 7s, true); // 1 Jan 1979
test_transition(get_sys_time(2524521600), 8s, true); // 1 Jan 1980
test_transition(get_sys_time(2571782400), 9s, true); // 1 Jul 1981
test_transition(get_sys_time(2603318400), 10s, true); // 1 Jul 1982
test_transition(get_sys_time(2634854400), 11s, true); // 1 Jul 1983
test_transition(get_sys_time(2698012800), 12s, true); // 1 Jul 1985
test_transition(get_sys_time(2776982400), 13s, true); // 1 Jan 1988
test_transition(get_sys_time(2840140800), 14s, true); // 1 Jan 1990
test_transition(get_sys_time(2871676800), 15s, true); // 1 Jan 1991
test_transition(get_sys_time(2918937600), 16s, true); // 1 Jul 1992
test_transition(get_sys_time(2950473600), 17s, true); // 1 Jul 1993
test_transition(get_sys_time(2982009600), 18s, true); // 1 Jul 1994
test_transition(get_sys_time(3029443200), 19s, true); // 1 Jan 1996
test_transition(get_sys_time(3076704000), 20s, true); // 1 Jul 1997
test_transition(get_sys_time(3124137600), 21s, true); // 1 Jan 1999
test_transition(get_sys_time(3345062400), 22s, true); // 1 Jan 2006
test_transition(get_sys_time(3439756800), 23s, true); // 1 Jan 2009
test_transition(get_sys_time(3550089600), 24s, true); // 1 Jul 2012
test_transition(get_sys_time(3644697600), 25s, true); // 1 Jul 2015
test_transition(get_sys_time(3692217600), 26s, true); // 1 Jan 2017
}
// Tests whether the return type is the expected type.
static void test_return_type() {
namespace cr = std::chrono;
using namespace std::literals::chrono_literals;
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::nanoseconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::nanoseconds>{0ns});
}
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::microseconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::microseconds>{0us});
}
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::milliseconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::milliseconds>{0ms});
}
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::seconds>{cr::seconds{0}});
}
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::minutes>{cr::minutes{0}});
}
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::hours>{cr::hours{0}});
}
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::days>{cr::days{0}});
}
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::weeks>{cr::weeks{0}});
}
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::months>{cr::months{0}});
}
{
[[maybe_unused]] std::same_as<cr::sys_time<cr::seconds>> decltype(auto) _ =
cr::utc_clock::to_sys(cr::utc_time<cr::years>{cr::years{0}});
}
}
int main(int, const char**) {
test_transitions();
test_transitions_floating_point();
test_return_type();
return 0;
}

View File

@ -0,0 +1,60 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
// XFAIL: libcpp-has-no-experimental-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 <ratio>
#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. Some of them are implementation-defined.
LIBCPP_STATIC_ASSERT(std::same_as<rep, std::chrono::system_clock::rep>);
static_assert(std::is_arithmetic_v<rep>);
static_assert(std::is_signed_v<rep>);
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>>);

View File

@ -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-experimental-tzdb
// XFAIL: availability-tzdb-missing
// REQUIRES: locale.fr_FR.UTF-8
// REQUIRES: locale.ja_JP.UTF-8
// <chrono>
// using utc_time = ...;
// 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;
}

File diff suppressed because it is too large Load Diff

View File

@ -150,9 +150,15 @@ 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>();
# if !defined(TEST_HAS_NO_EXPERIMENTAL_TZDB) && !defined(TEST_HAS_NO_TIME_ZONE_DATABASE) && \
!defined(TEST_HAS_NO_FILESYSTEM)
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>();
# endif // !defined(TEST_HAS_NO_EXPERIMENTAL_TZDB) && !defined(TEST_HAS_NO_TIME_ZONE_DATABASE) &&
// !defined(TEST_HAS_NO_FILESYSTEM)
assert_is_formattable<std::chrono::file_time<std::chrono::microseconds>, CharT>();
assert_is_formattable<std::chrono::local_time<std::chrono::microseconds>, CharT>();