[libcxx-commits] [libcxx] [libc++][chrono] implements UTC clock. (PR #90393)
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Dec 10 08:17:45 PST 2024
================
@@ -0,0 +1,220 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 the transition for clocks where the duration's rep is a floating-point type.
+static void test_transitions_floating_point() {
+ using namespace std::literals::chrono_literals;
+
+ // Based on test_transitions but uses a floating-point duration.
+ using F = float;
+
+ auto test_transition = [](std::chrono::sys_seconds sys, std::chrono::seconds elapsed, bool positive) {
+ // Note at the time of writing all leap seconds are positive so the else
+ // branch is never executed. The private test for this function tests
+ // negative leap seconds and uses the else branch.
+
+ std::chrono::utc_seconds utc = std::chrono::utc_seconds{sys.time_since_epoch()} + elapsed;
+
+ using D = std::chrono::duration<F>;
+ using S = std::chrono ::time_point<std::chrono::system_clock, D>;
+ using U = std::chrono ::time_point<std::chrono::utc_clock, D>;
+
+ S s{sys.time_since_epoch()};
+ bool is_leap_second = s.time_since_epoch().count() == sys.time_since_epoch().count();
+ assert(is_leap_second);
+
+ U u{utc.time_since_epoch()};
+ if (positive) {
+ test_leap_seconds(u - 1ns, s - 1ns);
+ test_leap_seconds(u, s - 1s);
+ test_leap_seconds(u + 0ns, s - 1ns);
+ test_leap_seconds(u + 1s - 1ns, s - 1ns);
+ test_leap_seconds(u + 1s, s);
+ test_leap_seconds(u + 1s + 0ns, s + 0ns);
+ test_leap_seconds(u + 1s + 1ns, s + 1ns);
+
+ test_leap_seconds(U{D{std::nextafter(u.time_since_epoch().count(), F{0})}},
+ S{D{std::nextafter(s.time_since_epoch().count(), F{0})}});
+ test_leap_seconds(u, S{D{s.time_since_epoch().count() - F{1}}});
+ test_leap_seconds(U{D{u.time_since_epoch().count() + F{1}}}, s);
+ test_leap_seconds(U{D{std::nextafter(u.time_since_epoch().count() + F{1}, std::numeric_limits<F>::max())}},
+ S{D{std::nextafter(s.time_since_epoch().count(), std::numeric_limits<F>::max())}});
+ }
+ };
+
+ // Transitions from the start of UTC.
+ test_transition(get_sys_time(2287785600), 0s, true); // 1 Jul 1972
+ test_transition(get_sys_time(2303683200), 1s, true); // 1 Jan 1973
+ test_transition(get_sys_time(2335219200), 2s, true); // 1 Jan 1974
+ test_transition(get_sys_time(2366755200), 3s, true); // 1 Jan 1975
+ test_transition(get_sys_time(2398291200), 4s, true); // 1 Jan 1976
+ test_transition(get_sys_time(2429913600), 5s, true); // 1 Jan 1977
+ test_transition(get_sys_time(2461449600), 6s, true); // 1 Jan 1978
+ test_transition(get_sys_time(2492985600), 7s, true); // 1 Jan 1979
+ test_transition(get_sys_time(2524521600), 8s, true); // 1 Jan 1980
+ test_transition(get_sys_time(2571782400), 9s, true); // 1 Jul 1981
+ test_transition(get_sys_time(2603318400), 10s, true); // 1 Jul 1982
+ test_transition(get_sys_time(2634854400), 11s, true); // 1 Jul 1983
+ test_transition(get_sys_time(2698012800), 12s, true); // 1 Jul 1985
+ test_transition(get_sys_time(2776982400), 13s, true); // 1 Jan 1988
+ test_transition(get_sys_time(2840140800), 14s, true); // 1 Jan 1990
+ test_transition(get_sys_time(2871676800), 15s, true); // 1 Jan 1991
+ test_transition(get_sys_time(2918937600), 16s, true); // 1 Jul 1992
+ test_transition(get_sys_time(2950473600), 17s, true); // 1 Jul 1993
+ test_transition(get_sys_time(2982009600), 18s, true); // 1 Jul 1994
+ test_transition(get_sys_time(3029443200), 19s, true); // 1 Jan 1996
+ test_transition(get_sys_time(3076704000), 20s, true); // 1 Jul 1997
+ test_transition(get_sys_time(3124137600), 21s, true); // 1 Jan 1999
+ test_transition(get_sys_time(3345062400), 22s, true); // 1 Jan 2006
+ test_transition(get_sys_time(3439756800), 23s, true); // 1 Jan 2009
+ test_transition(get_sys_time(3550089600), 24s, true); // 1 Jul 2012
+ test_transition(get_sys_time(3644697600), 25s, true); // 1 Jul 2015
+ test_transition(get_sys_time(3692217600), 26s, true); // 1 Jan 2017
+
+}
+
+// Tests whether the return type is the expected type.
+static void test_return_type() {
+ using namespace std::chrono;
+ 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_transitions_floating_point();
+ test_return_type();
----------------
mordante wrote:
done here and at other places
https://github.com/llvm/llvm-project/pull/90393
More information about the libcxx-commits
mailing list