[llvm-branch-commits] [libcxx] [libc++][chrono] Adds the sys_info class. (PR #85619)
Louis Dionne via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Mar 26 11:00:44 PDT 2024
================
@@ -0,0 +1,1374 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 time_zone;
+
+// template <class _Duration>
+// sys_info get_info(const sys_time<_Duration>& time) const;
+
+// This test uses the system provided database. This makes the test portable,
+// but may cause failures when the database information changes. Historic data
+// may change if new facts are uncovered, future data may change when regions
+// change their time zone or daylight saving time. Most tests will not look in
+// the future to attempt to avoid issues. All tests list the data on which they
+// are based, this makes debugging easier upon failure; including to see whether
+// the provided data has not been changed
+//
+//
+// The data in the tests can be validated by using the zdump tool. For
+// example
+// zdump -v Asia/Hong_Kong
+// show all transistions in the Hong Kong time zone. Or
+// zdump -c1970,1980 -v Asia/Hong_Kong
+// shows all transitions in Hong Kong between 1970 and 1980.
+
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <format>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+/***** ***** HELPERS ***** *****/
+
+[[nodiscard]] static std::chrono::sys_seconds to_sys_seconds(
+ std::chrono::year year,
+ std::chrono::month month,
+ std::chrono::day day,
+ std::chrono::hours h = std::chrono::hours(0),
+ std::chrono::minutes m = std::chrono::minutes{0},
+ std::chrono::seconds s = std::chrono::seconds{0}) {
+ std::chrono::year_month_day result{year, month, day};
+
+ return std::chrono::time_point_cast<std::chrono::seconds>(static_cast<std::chrono::sys_days>(result)) + h + m + s;
+}
+
+static void assert_equal(const std::chrono::sys_info& lhs, const std::chrono::sys_info& rhs) {
+ TEST_REQUIRE(lhs.begin == rhs.begin,
+ TEST_WRITE_CONCATENATED("\nBegin:\nExpected output ", lhs.begin, "\nActual output ", rhs.begin, '\n'));
+ TEST_REQUIRE(lhs.end == rhs.end,
+ TEST_WRITE_CONCATENATED("\nEnd:\nExpected output ", lhs.end, "\nActual output ", rhs.end, '\n'));
+ TEST_REQUIRE(
+ lhs.offset == rhs.offset,
+ TEST_WRITE_CONCATENATED("\nOffset:\nExpected output ", lhs.offset, "\nActual output ", rhs.offset, '\n'));
+ TEST_REQUIRE(lhs.save == rhs.save,
+ TEST_WRITE_CONCATENATED("\nSave:\nExpected output ", lhs.save, "\nActual output ", rhs.save, '\n'));
+ TEST_REQUIRE(
+ lhs.abbrev == rhs.abbrev,
+ TEST_WRITE_CONCATENATED("\nAbbrev:\nExpected output ", lhs.abbrev, "\nActual output ", rhs.abbrev, '\n'));
+}
+
+static void assert_equal(std::string_view expected, const std::chrono::sys_info& value) {
+ // Note the output of operator<< is implementation defined, use this
+ // format to keep the test portable.
+ std::string result = std::format(
+ "[{}, {}) {:%T} {:%Q%q} {}",
+ value.begin,
+ value.end,
+ std::chrono::hh_mm_ss{value.offset},
+ value.save,
+ value.abbrev);
+
+ TEST_REQUIRE(expected == result,
+ TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output ", result, '\n'));
+}
+
+static void
+assert_range(std::string_view expected, const std::chrono::sys_info& begin, const std::chrono::sys_info& end) {
+ assert_equal(expected, begin);
+ assert_equal(expected, end);
+}
+
+static void assert_cycle(
+ std::string_view expected_1,
+ const std::chrono::sys_info& begin_1,
+ const std::chrono::sys_info& end_1,
+ std::string_view expected_2,
+ const std::chrono::sys_info& begin_2,
+ const std::chrono::sys_info& end_2
+
+) {
+ assert_range(expected_1, begin_1, end_1);
+ assert_range(expected_2, begin_2, end_2);
+}
+
+/***** ***** TESTS ***** *****/
+
+static void test_gmt() {
+ // Simple zone always valid, no rule entries, lookup using a link.
+ // L Etc/GMT GMT
+ // Z Etc/GMT 0 - GMT
+
+ const std::chrono::time_zone* tz = std::chrono::locate_zone("GMT");
+
+ assert_equal(
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(),
+ std::chrono::sys_seconds::max(),
+ std::chrono::seconds(0),
+ std::chrono::minutes(0),
+ "GMT"),
+ tz->get_info(std::chrono::sys_seconds::min()));
+ assert_equal(
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(),
+ std::chrono::sys_seconds::max(),
+ std::chrono::seconds(0),
+ std::chrono::minutes(0),
+ "GMT"),
+ tz->get_info(std::chrono::sys_seconds(std::chrono::seconds{0})));
+
+ assert_equal(
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(),
+ std::chrono::sys_seconds::max(),
+ std::chrono::seconds(0),
+ std::chrono::minutes(0),
+ "GMT"),
+ tz->get_info(std::chrono::sys_seconds::max() - std::chrono::seconds{1})); // max is not valid
+}
+
+static void test_durations() {
+ // Doesn't test a location, instead tests whether different duration
+ // specializations work.
+ const std::chrono::time_zone* tz = std::chrono::locate_zone("GMT");
+
+ // Using the GMT zone means every call gives the same result.
+ std::chrono::sys_info expected(
+ std::chrono::sys_seconds::min(),
+ std::chrono::sys_seconds::max(),
+ std::chrono::seconds(0),
+ std::chrono::minutes(0),
+ "GMT");
+
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::nanoseconds>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::microseconds>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::milliseconds>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::seconds>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::minutes>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::minutes>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::hours>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::days>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::weeks>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::months>{}));
+ assert_equal(expected, tz->get_info(std::chrono::sys_time<std::chrono::years>{}));
+}
+
+static void test_indian_kerguelen() {
+ // One change, no rules, no dst changes.
+
+ // Z Indian/Kerguelen 0 - -00 1950
+ // 5 - +05
+
+ const std::chrono::time_zone* tz = std::chrono::locate_zone("Indian/Kerguelen");
+
+ std::chrono::sys_seconds transition =
+ to_sys_seconds(std::chrono::year(1950), std::chrono::January, std::chrono::day(1));
+
+ assert_equal(
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(), //
+ transition, //
+ std::chrono::seconds(0), //
+ std::chrono::minutes(0), //
+ "-00"), //
+ tz->get_info(std::chrono::sys_seconds::min()));
+
+ assert_equal(
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(), //
+ transition, //
+ std::chrono::seconds(0), //
+ std::chrono::minutes(0), //
+ "-00"), //
+ tz->get_info(transition - std::chrono::seconds{1}));
+
+ assert_equal(
+ std::chrono::sys_info(
+ transition, //
+ std::chrono::sys_seconds::max(), //
+ std::chrono::hours(5), //
+ std::chrono::minutes(0), //
+ "+05"), //
+ tz->get_info(transition));
+}
+
+static void test_antarctica_syowa() {
+ // One change, no rules, no dst changes
+ // This change uses an ON field with a day number
+ //
+ // There don't seem to be rule-less zones that use last day or a
+ // contrained day
+
+ // Z Antarctica/Syowa 0 - -00 1957 Ja 29
+ // 3 - +03
+
+ const std::chrono::time_zone* tz = std::chrono::locate_zone("Antarctica/Syowa");
+
+ std::chrono::sys_seconds transition =
+ to_sys_seconds(std::chrono::year(1957), std::chrono::January, std::chrono::day(29));
+
+ assert_equal(
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(), //
+ transition, //
+ std::chrono::seconds(0), //
+ std::chrono::minutes(0), //
+ "-00"), //
+ tz->get_info(std::chrono::sys_seconds::min()));
+
+ assert_equal(
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(), //
+ transition, //
+ std::chrono::seconds(0), //
+ std::chrono::minutes(0), //
+ "-00"), //
+ tz->get_info(transition - std::chrono::seconds(1)));
+
+ assert_equal(
+ std::chrono::sys_info(
+ transition, //
+ std::chrono::sys_seconds::max(), //
+ std::chrono::hours(3), //
+ std::chrono::minutes(0), //
+ "+03"), //
+ tz->get_info(transition));
+}
+
+static void test_asia_hong_kong() {
+ // A more typical entry, first some hard-coded entires and then at the
+ // end a rules based entry. This rule is valid for its entire period
+ //
+ // Z Asia/Hong_Kong 7:36:42 - LMT 1904 O 30 0:36:42
+ // 8 - HKT 1941 Jun 15 3
+ // 8 1 HKST 1941 O 1 4
+ // 8 0:30 HKWT 1941 D 25
+ // 9 - JST 1945 N 18 2
+ // 8 HK HK%sT
+ //
+ // R HK 1946 o - Ap 21 0 1 S
+ // R HK 1946 o - D 1 3:30s 0 -
+ // R HK 1947 o - Ap 13 3:30s 1 S
+ // R HK 1947 o - N 30 3:30s 0 -
+ // R HK 1948 o - May 2 3:30s 1 S
+ // R HK 1948 1952 - O Su>=28 3:30s 0 -
+ // R HK 1949 1953 - Ap Su>=1 3:30 1 S
+ // R HK 1953 1964 - O Su>=31 3:30 0 -
+ // R HK 1954 1964 - Mar Su>=18 3:30 1 S
+ // R HK 1965 1976 - Ap Su>=16 3:30 1 S
+ // R HK 1965 1976 - O Su>=16 3:30 0 -
+ // R HK 1973 o - D 30 3:30 1 S
+ // R HK 1979 o - May 13 3:30 1 S
+ // R HK 1979 o - O 21 3:30 0 -
+
+ using namespace std::literals::chrono_literals;
+ const std::chrono::time_zone* tz = std::chrono::locate_zone("Asia/Hong_Kong");
+
+ assert_equal(
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(),
+ to_sys_seconds(1904y, std::chrono::October, 29d, 17h), // 7:36:42 - LMT 1904 O 30 0:36:42
+ 7h + 36min + 42s,
+ 0min,
+ "LMT"),
+ tz->get_info(std::chrono::sys_seconds::min()));
+
+ assert_equal(
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(),
+ to_sys_seconds(1904y, std::chrono::October, 29d, 17h), // 7:36:42 - LMT 1904 O 30 0:36:42
+ 7h + 36min + 42s,
+ 0min,
+ "LMT"),
+ tz->get_info(to_sys_seconds(1904y, std::chrono::October, 29d, 16h, 59min, 59s)));
+
+ assert_range("[1904-10-29 17:00:00, 1941-06-14 19:00:00) 08:00:00 0min HKT", // 8 - HKT 1941 Jun 15 3
+ tz->get_info(to_sys_seconds(1904y, std::chrono::October, 29d, 17h)),
+ tz->get_info(to_sys_seconds(1941y, std::chrono::June, 14d, 18h, 59min, 59s)));
+
+ assert_range("[1941-06-14 19:00:00, 1941-09-30 19:00:00) 09:00:00 60min HKST", // 8 1 HKST 1941 O 1 4
+ tz->get_info(to_sys_seconds(1941y, std::chrono::June, 14d, 19h)),
+ tz->get_info(to_sys_seconds(1941y, std::chrono::September, 30d, 18h, 59min, 59s)));
+
+ assert_range("[1941-09-30 19:00:00, 1941-12-24 15:30:00) 08:30:00 30min HKWT", // 8 0:30 HKWT 1941 D 25
+ tz->get_info(to_sys_seconds(1941y, std::chrono::September, 30d, 19h)),
+ tz->get_info(to_sys_seconds(1941y, std::chrono::December, 24d, 15h, 29min, 59s)));
+
+ assert_range("[1941-12-24 15:30:00, 1945-11-17 17:00:00) 09:00:00 0min JST", // 9 - JST 1945 N 18 2
+ tz->get_info(to_sys_seconds(1941y, std::chrono::December, 24d, 15h, 30min)),
+ tz->get_info(to_sys_seconds(1945y, std::chrono::November, 17d, 16h, 59min, 59s)));
+
+ assert_range("[1945-11-17 17:00:00, 1946-04-20 16:00:00) 08:00:00 0min HKT", // 8 HK%sT
+ tz->get_info(to_sys_seconds(1945y, std::chrono::November, 17d, 17h)),
+ tz->get_info(to_sys_seconds(1946y, std::chrono::April, 20d, 15h, 59min, 59s)));
+
+ assert_cycle( // 8 HK%sT
+ "[1946-04-20 16:00:00, 1946-11-30 19:30:00) 09:00:00 60min HKST",
+ tz->get_info(to_sys_seconds(1946y, std::chrono::April, 20d, 16h)), // 1946 o Ap 21 0 1 S
+ tz->get_info(to_sys_seconds(1946y, std::chrono::November, 30d, 19h, 29min, 59s)), // 1946 o D 1 3:30s 0 -
+ "[1946-11-30 19:30:00, 1947-04-12 19:30:00) 08:00:00 0min HKT",
+ tz->get_info(to_sys_seconds(1946y, std::chrono::November, 30d, 19h, 30min)), // 1946 o D 1 3:30s 0 -
+ tz->get_info(to_sys_seconds(1947y, std::chrono::April, 12d, 19h, 29min, 59s))); // 1947 o Ap 13 3:30s 1 S
+
+ assert_cycle( // 8 HK%sT
+ "[1947-04-12 19:30:00, 1947-11-29 19:30:00) 09:00:00 60min HKST",
+ tz->get_info(to_sys_seconds(1947y, std::chrono::April, 12d, 19h, 30min)), // 1947 o Ap 13 3:30s 1 S
+ tz->get_info(to_sys_seconds(1947y, std::chrono::November, 29d, 19h, 29min, 59s)), // 1947 o N 30 3:30s 0 -
+ "[1947-11-29 19:30:00, 1948-05-01 19:30:00) 08:00:00 0min HKT",
+ tz->get_info(to_sys_seconds(1947y, std::chrono::November, 29d, 19h, 30min)), // 1947 o N 30 3:30s 0 -
+ tz->get_info(to_sys_seconds(1948y, std::chrono::May, 1d, 19h, 29min, 59s))); // 1948 o May 2 3:30s 1 S
+
+ assert_cycle( // 8 HK%sT
+ "[1948-05-01 19:30:00, 1948-10-30 19:30:00) 09:00:00 60min HKST",
+ tz->get_info(to_sys_seconds(1948y, std::chrono::May, 1d, 19h, 30min)), // 1948 o May 2 3:30s 1 S
+ tz->get_info(to_sys_seconds(1948y, std::chrono::October, 30d, 19h, 29min, 59s)), // 1948 1952 O Su>=28 3:30s 0 -
+ "[1948-10-30 19:30:00, 1949-04-02 19:30:00) 08:00:00 0min HKT",
+ tz->get_info(to_sys_seconds(1948y, std::chrono::October, 30d, 19h, 30min)), // 1948 1952 O Su>=28 3:30s 0 -
+ tz->get_info(to_sys_seconds(1949y, std::chrono::April, 2d, 19h, 29min, 59s))); // 1949 1953 Ap Su>=1 3:30 1 S
+
+ assert_cycle( // 8 HK%sT
+ "[1949-04-02 19:30:00, 1949-10-29 19:30:00) 09:00:00 60min HKST",
+ tz->get_info(to_sys_seconds(1949y, std::chrono::April, 2d, 19h, 30min)), // 1949 1953 Ap Su>=1 3:30 1 S
+ tz->get_info(to_sys_seconds(1949y, std::chrono::October, 29d, 19h, 29min, 59s)), // 1948 1952 O Su>=28 3:30s 0
+ "[1949-10-29 19:30:00, 1950-04-01 19:30:00) 08:00:00 0min HKT",
+ tz->get_info(to_sys_seconds(1949y, std::chrono::October, 29d, 19h, 30min)), // 1948 1952 O Su>=28 3:30s 0
+ tz->get_info(to_sys_seconds(1950y, std::chrono::April, 1d, 19h, 29min, 59s))); // 1949 1953 Ap Su>=1 3:30 1 S
+
+ assert_range(
----------------
ldionne wrote:
Wow. We discussed how you came up with these tests and I understand it now. I will not go through all the test cases in detail though since that would probably not add a lot of value.
https://github.com/llvm/llvm-project/pull/85619
More information about the llvm-branch-commits
mailing list