//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// // TODO TZDB review the test based on review comments in // https://github.com/llvm/llvm-project/pull/85619 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23, c++26 // UNSUPPORTED: no-filesystem, no-localization, no-tzdb // XFAIL: libcpp-has-no-experimental-tzdb // XFAIL: availability-tzdb-missing // // class time_zone; // template // 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 transitions 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 #include #include #include #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(static_cast(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{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); assert_equal(expected, tz->get_info(std::chrono::sys_time{})); } 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 // constrained 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( "[1953-10-31 18:30:00, 1954-03-20 19:30:00) 08:00:00 0min HKT", tz->get_info(to_sys_seconds(1953y, std::chrono::October, 31d, 18h, 30min)), // 1953 1964 - O Su>=31 3:30 0 - tz->get_info(to_sys_seconds(1954y, std::chrono::March, 20d, 19h, 29min, 59s))); // 1954 1964 - Mar Su>=18 3:30 1 S assert_cycle( // 8 HK%sT "[1953-04-04 19:30:00, 1953-10-31 18:30:00) 09:00:00 60min HKST", tz->get_info(to_sys_seconds(1953y, std::chrono::April, 4d, 19h, 30min)), // 1949 1953 Ap Su>=1 3:30 1 S tz->get_info(to_sys_seconds(1953y, std::chrono::October, 31d, 18h, 29min, 59s)), // 1953 1964 - O Su>=31 3:30 0 - "[1953-10-31 18:30:00, 1954-03-20 19:30:00) 08:00:00 0min HKT", tz->get_info(to_sys_seconds(1953y, std::chrono::October, 31d, 18h, 30min)), // 1953 1964 - O Su>=31 3:30 0 - tz->get_info(to_sys_seconds(1954y, std::chrono::March, 20d, 19h, 29min, 59s))); // 1954 1964 - Mar Su>=18 3:30 1 S assert_cycle( // 8 HK%sT "[1972-04-15 19:30:00, 1972-10-21 18:30:00) 09:00:00 60min HKST", tz->get_info(to_sys_seconds(1972y, std::chrono::April, 19d, 19h, 30min)), // 1965 1976 - Ap Su>=16 3:30 1 S tz->get_info(to_sys_seconds(1972y, std::chrono::October, 21d, 18h, 29min, 59s)), // 1965 1976 - O Su>=16 3:30 0 - "[1972-10-21 18:30:00, 1973-04-21 19:30:00) 08:00:00 0min HKT", tz->get_info(to_sys_seconds(1972y, std::chrono::October, 21d, 18h, 30min)), // 1965 1976 - O Su>=16 3:30 0 - tz->get_info(to_sys_seconds(1973y, std::chrono::April, 21d, 19h, 29min, 59s))); // 1965 1976 - Ap Su>=16 3:30 1 S assert_range( // 8 HK%sT "[1973-04-21 19:30:00, 1973-10-20 18:30:00) 09:00:00 60min HKST", tz->get_info(to_sys_seconds(1973y, std::chrono::April, 21d, 19h, 30min)), // 1965 1976 - Ap Su>=16 3:30 1 S tz->get_info(to_sys_seconds(1973y, std::chrono::October, 20d, 18h, 29min, 59s))); // 1965 1976 - O Su>=16 3:30 0 - assert_range( // 8 HK%sT, test "1973 o - D 30 3:30 1 S" "[1973-10-20 18:30:00, 1973-12-29 19:30:00) 08:00:00 0min HKT", tz->get_info(to_sys_seconds(1973y, std::chrono::October, 20d, 18h, 30min)), // 1965 1976 - O Su>=16 3:30 tz->get_info(to_sys_seconds(1973y, std::chrono::December, 29d, 19h, 29min, 59s))); // 1973 o - D 30 3:30 1 S assert_range( // 8 HK%sT "[1973-12-29 19:30:00, 1974-10-19 18:30:00) 09:00:00 60min HKST", tz->get_info(to_sys_seconds(1973y, std::chrono::December, 29d, 19h, 30min)), // 1973 o - D 30 3:30 1 S tz->get_info(to_sys_seconds(1974y, std::chrono::October, 19d, 18h, 29min, 59s))); // 1965 1976 - O Su>=16 3:30 assert_range( // 8 HK%sT, between 1973 and 1979 no rule is active so falls back to default "[1976-04-17 19:30:00, 1976-10-16 18:30:00) 09:00:00 60min HKST", tz->get_info(to_sys_seconds(1976y, std::chrono::April, 17d, 19h, 30min)), // 1965 1976 - Ap Su>=16 3:30 1 S tz->get_info(to_sys_seconds(1976y, std::chrono::October, 16d, 18h, 29min, 59s))); // 1965 1976 - O Su>=16 3:30 0 - assert_range( // 8 HK%sT, between 1973 and 1979 no rule is active so falls back to default "[1976-10-16 18:30:00, 1979-05-12 19:30:00) 08:00:00 0min HKT", tz->get_info(to_sys_seconds(1976y, std::chrono::October, 16d, 18h, 30min)), // 1965 1976 - O Su>=16 3:30 0 - tz->get_info(to_sys_seconds(1979y, std::chrono::May, 12d, 19h, 29min, 59s))); // 1979 o - May 13 3:30 1 S assert_range( // 8 HK%sT "[1979-05-12 19:30:00, 1979-10-20 18:30:00) 09:00:00 60min HKST", tz->get_info(to_sys_seconds(1979y, std::chrono::May, 12d, 19h, 30min)), // 1979 o - May 13 3:30 1 S tz->get_info(to_sys_seconds(1979y, std::chrono::October, 20d, 18h, 29min, 59s))); // 1979 o - O 21 3:30 0 - assert_equal( std::chrono::sys_info( to_sys_seconds(1979y, std::chrono::October, 20d, 18h, 30min), std::chrono::sys_seconds::max(), 8h, std::chrono::minutes(0), "HKT"), tz->get_info(to_sys_seconds(1979y, std::chrono::October, 20d, 18h, 30min))); assert_equal( std::chrono::sys_info( to_sys_seconds(1979y, std::chrono::October, 20d, 18h, 30min), std::chrono::sys_seconds::max(), 8h, std::chrono::minutes(0), "HKT"), tz->get_info(std::chrono::sys_seconds::max() - std::chrono::seconds{1})); // max is not valid } static void test_europe_berlin() { // 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 Europe/Berlin 0:53:28 - LMT 1893 Ap // 1 c CE%sT 1945 May 24 2 // 1 So CE%sT 1946 // 1 DE CE%sT 1980 // 1 E CE%sT // // R c 1916 o - Ap 30 23 1 S // R c 1916 o - O 1 1 0 - // R c 1917 1918 - Ap M>=15 2s 1 S // R c 1917 1918 - S M>=15 2s 0 - // R c 1940 o - Ap 1 2s 1 S // R c 1942 o - N 2 2s 0 - // R c 1943 o - Mar 29 2s 1 S // R c 1943 o - O 4 2s 0 - // R c 1944 1945 - Ap M>=1 2s 1 S // R c 1944 o - O 2 2s 0 - // R c 1945 o - S 16 2s 0 - // R c 1977 1980 - Ap Su>=1 2s 1 S // R c 1977 o - S lastSu 2s 0 - // R c 1978 o - O 1 2s 0 - // R c 1979 1995 - S lastSu 2s 0 - // R c 1981 ma - Mar lastSu 2s 1 S // R c 1996 ma - O lastSu 2s 0 - // // R So 1945 o - May 24 2 2 M // R So 1945 o - S 24 3 1 S // R So 1945 o - N 18 2s 0 - // // R DE 1946 o - Ap 14 2s 1 S // R DE 1946 o - O 7 2s 0 - // R DE 1947 1949 - O Su>=1 2s 0 - // R DE 1947 o - Ap 6 3s 1 S // R DE 1947 o - May 11 2s 2 M // R DE 1947 o - Jun 29 3 1 S // R DE 1948 o - Ap 18 2s 1 S // R DE 1949 o - Ap 10 2s 1 S // // R E 1977 1980 - Ap Su>=1 1u 1 S // R E 1977 o - S lastSu 1u 0 - // R E 1978 o - O 1 1u 0 - // R E 1979 1995 - S lastSu 1u 0 - // R E 1981 ma - Mar lastSu 1u 1 S // R E 1996 ma - O lastSu 1u 0 - // // Note the European Union decided to stop the seasonal change in // 2021. In 2023 seasonal changes are still in effect. using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin"); assert_equal( std::chrono::sys_info( std::chrono::sys_seconds::min(), to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s), // 0:53:28 - LMT 1893 Ap 53min + 28s, 0min, "LMT"), tz->get_info(std::chrono::sys_seconds::min())); assert_equal( std::chrono::sys_info( std::chrono::sys_seconds::min(), to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s), // 0:53:28 - LMT 1893 Ap 53min + 28s, 0min, "LMT"), tz->get_info(to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 31s))); assert_range( // 1 CE%sT before 1916 o - Ap 30 23 1 S "[1893-03-31 23:06:32, 1916-04-30 22:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1893y, std::chrono::March, 31d, 23h, 6min, 32s)), tz->get_info(to_sys_seconds(1916y, std::chrono::April, 30d, 21h, 59min, 59s))); assert_cycle( // 1 CE%sT "[1916-04-30 22:00:00, 1916-09-30 23:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1916y, std::chrono::April, 30d, 22h)), // 1916 o - Ap 30 23 1 S tz->get_info(to_sys_seconds(1916y, std::chrono::September, 30d, 22h, 59min, 59s)), // o - O 1 1 0 - "[1916-09-30 23:00:00, 1917-04-16 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1916y, std::chrono::September, 30d, 23h)), // o - O 1 1 0 - tz->get_info(to_sys_seconds(1917y, std::chrono::April, 16d, 0h, 59min, 59s))); // 1917 1918 - Ap M>=15 2s 1 S assert_cycle( // 1 CE%sT "[1917-04-16 01:00:00, 1917-09-17 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1917y, std::chrono::April, 16d, 1h)), // 1917 1918 Ap M>=15 2s 1 S tz->get_info(to_sys_seconds(1917y, std::chrono::September, 17d, 0h, 59min, 59s)), // 1917 1918 S M>=15 2s 0 - "[1917-09-17 01:00:00, 1918-04-15 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1917y, std::chrono::September, 17d, 1h)), // 1917 1918 S M>=15 2s 0 - tz->get_info(to_sys_seconds(1918y, std::chrono::April, 15d, 0h, 59min, 59s))); // 1917 1918 Ap M>=15 2s 1 S assert_cycle( // 1 CE%sT (The cycle is more than 1 year) "[1918-04-15 01:00:00, 1918-09-16 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1918y, std::chrono::April, 15d, 1h)), // 1917 1918 Ap M>=15 2s 1 S tz->get_info(to_sys_seconds(1918y, std::chrono::September, 16d, 0h, 59min, 59s)), // 1917 1918 S M>=15 2s 0 - "[1918-09-16 01:00:00, 1940-04-01 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1918y, std::chrono::September, 16d, 1h)), // 1917 1918 S M>=15 2s 0 - tz->get_info(to_sys_seconds(1940y, std::chrono::April, 1d, 0h, 59min, 59s))); // 1940 o Ap 1 2s 1 S assert_cycle( // 1 CE%sT (The cycle is more than 1 year) "[1940-04-01 01:00:00, 1942-11-02 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1940y, std::chrono::April, 1d, 1h)), // 1940 o Ap 1 2s 1 S tz->get_info(to_sys_seconds(1942y, std::chrono::November, 2d, 0h, 59min, 59s)), // 1942 o N 2 2s 0 - "[1942-11-02 01:00:00, 1943-03-29 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1942y, std::chrono::November, 2d, 1h)), // 1942 o N 2 2s 0 - tz->get_info(to_sys_seconds(1943y, std::chrono::March, 29d, 0h, 59min, 59s))); // 1943 o Mar 29 2s 1 S assert_range( // Here the zone changes from c (C-Eur) to So (SovietZone). // The rule c ends on 1945-09-16, instead it ends at the zone change date/time // There is a tricky part in the time // "1 c CE%sT" has an offset of 1 at the moment the rule // ends there is a save of 60 minutes. This means the // local offset to UTC is 2 hours. The rule ends at // 1945-05-24 02:00:00 local time, which is // 1945-05-24 00:00:00 UTC. "[1945-04-02 01:00:00, 1945-05-24 00:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1945y, std::chrono::April, 2d, 1h)), // 1 CE%sT & 1945 Ap M>=1 2s 1 S tz->get_info(to_sys_seconds(1945y, std::chrono::May, 23d, 23h, 59min, 59s))); // 1 c CE%sT & 1945 May 24 2 assert_range( // -- "[1945-05-24 00:00:00, 1945-09-24 00:00:00) 03:00:00 120min CEMT", tz->get_info(to_sys_seconds(1945y, std::chrono::May, 24d)), // 1 c CE%sT & 1945 May 24 2 tz->get_info(to_sys_seconds(1945y, std::chrono::September, 23d, 23h, 59min, 59s))); // 1945 o S 24 3 1 S assert_range( // 1 c CE%sT 1945 May 24 2 "[1945-09-24 00:00:00, 1945-11-18 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1945y, std::chrono::September, 24d)), // 1945 o S 24 3 1 S tz->get_info(to_sys_seconds(1945y, std::chrono::November, 18d, 0h, 59min, 59s))); // 1945 o N 18 2s 0 - assert_range( // -- // Merges 2 continuations "[1945-11-18 01:00:00, 1946-04-14 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1945y, std::chrono::November, 18d, 1h)), // 1 c CE%sT & 1945 o N 18 2s 0 - tz->get_info(to_sys_seconds(1946y, std::chrono::April, 14d, 0h, 59min, 59s))); // 1 So CE%sT & 1946 o Ap 14 2s 1 S assert_range( // 1 DE CE%sT 1980 "[1946-04-14 01:00:00, 1946-10-07 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1946y, std::chrono::April, 14d, 1h)), // 1946 o Ap 14 2s 1 S tz->get_info(to_sys_seconds(1946y, std::chrono::October, 7d, 0h, 59min, 59s))); // 1946 o O 7 2s 0 - // Note 1947 is an interesting year with 4 rules // R DE 1947 1949 - O Su>=1 2s 0 - // R DE 1947 o - Ap 6 3s 1 S // R DE 1947 o - May 11 2s 2 M // R DE 1947 o - Jun 29 3 1 S assert_range( // 1 DE CE%sT 1980 "[1946-10-07 01:00:00, 1947-04-06 02:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1946y, std::chrono::October, 7d, 1h)), // 1946 o O 7 2s 0 - tz->get_info(to_sys_seconds(1947y, std::chrono::April, 6d, 1h, 59min, 59s))); // 1947 o Ap 6 3s 1 S assert_range( // 1 DE CE%sT 1980 "[1947-04-06 02:00:00, 1947-05-11 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1947y, std::chrono::April, 6d, 2h)), // 1947 o Ap 6 3s 1 S tz->get_info(to_sys_seconds(1947y, std::chrono::May, 11d, 0h, 59min, 59s))); // 1947 o May 11 2s 2 M assert_range( // 1 DE CE%sT 1980 "[1947-05-11 01:00:00, 1947-06-29 00:00:00) 03:00:00 120min CEMT", tz->get_info(to_sys_seconds(1947y, std::chrono::May, 11d, 1h)), // 1947 o May 11 2s 2 M tz->get_info(to_sys_seconds(1947y, std::chrono::June, 28d, 23h, 59min, 59s))); // 1947 o Jun 29 3 1 S assert_cycle( // 1 DE CE%sT 1980 "[1947-06-29 00:00:00, 1947-10-05 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1947y, std::chrono::June, 29d)), // 1947 o Jun 29 3 1 S tz->get_info(to_sys_seconds(1947y, std::chrono::October, 5d, 0h, 59min, 59s)), // 1947 1949 O Su>=1 2s 0 - "[1947-10-05 01:00:00, 1948-04-18 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1947y, std::chrono::October, 5d, 1h)), // 1947 1949 O Su>=1 2s 0 - tz->get_info(to_sys_seconds(1948y, std::chrono::April, 18d, 0h, 59min, 59s))); // 1948 o Ap 18 2s 1 S assert_cycle( // 1 DE CE%sT 1980 "[1948-04-18 01:00:00, 1948-10-03 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(1948y, std::chrono::April, 18d, 1h)), // 1948 o Ap 18 2s 1 S tz->get_info(to_sys_seconds(1948y, std::chrono::October, 3d, 0h, 59min, 59s)), // 1947 1949 O Su>=1 2s 0 - "[1948-10-03 01:00:00, 1949-04-10 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1948y, std::chrono::October, 3d, 1h)), // 1947 1949 O Su>=1 2s 0 - tz->get_info(to_sys_seconds(1949y, std::chrono::April, 10d, 0h, 59min, 59s))); // 1949 o Ap 10 2s 1 S assert_cycle( // Note the end time is in a different continuation. "[1949-04-10 01:00:00, 1949-10-02 01:00:00) 02:00:00 60min CEST", // 1 DE CE%sT 1980 tz->get_info(to_sys_seconds(1949y, std::chrono::April, 10d, 1h)), // 1949 o Ap 10 2s 1 S tz->get_info(to_sys_seconds(1949y, std::chrono::October, 2d, 0h, 59min, 59s)), // 1947 1949 O Su>=1 2s 0 - "[1949-10-02 01:00:00, 1980-04-06 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(1949y, std::chrono::October, 2d, 1h)), // 1947 1949 O Su>=1 2s 0 - tz->get_info( // 1 E CE%sT to_sys_seconds(1980y, std::chrono::April, 6d, 0h, 59min, 59s))); // 1977 1980 Ap Su>=1 1u 1 S assert_cycle( // 1 E CE%sT "[2020-03-29 01:00:00, 2020-10-25 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(2020y, std::chrono::March, 29d, 1h)), // 1981 ma Mar lastSu 1u 1 S tz->get_info(to_sys_seconds(2020y, std::chrono::October, 25d, 0h, 59min, 59s)), // 1996 ma O lastSu 1u 0 - "[2020-10-25 01:00:00, 2021-03-28 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(2020y, std::chrono::October, 25d, 1h)), // 1996 ma O lastSu 1u 0 - tz->get_info(to_sys_seconds(2021y, std::chrono::March, 28d, 0h, 59min, 59s))); // 1981 ma Mar lastSu 1u 1 S assert_cycle( // 1 E CE%sT "[2021-03-28 01:00:00, 2021-10-31 01:00:00) 02:00:00 60min CEST", tz->get_info(to_sys_seconds(2021y, std::chrono::March, 28d, 1h)), // 1981 ma Mar lastSu 1u 1 S tz->get_info(to_sys_seconds(2021y, std::chrono::October, 31d, 0h, 59min, 59s)), // 1996 ma O lastSu 1u 0 - "[2021-10-31 01:00:00, 2022-03-27 01:00:00) 01:00:00 0min CET", tz->get_info(to_sys_seconds(2021y, std::chrono::October, 31d, 1h)), // 1996 ma O lastSu 1u 0 - tz->get_info(to_sys_seconds(2022y, std::chrono::March, 27d, 0h, 59min, 59s))); // 1981 ma Mar lastSu 1u 1 S } static void test_america_st_johns() { // A more typical entry, // Uses letters both when DST is active and not and has multiple // letters. Uses negative offsets. // Switches several times between their own and Canadian rules // Switches the stdoff from -3:30:52 to -3:30 while observing the same rule // Z America/St_Johns -3:30:52 - LMT 1884 // -3:30:52 j N%sT 1918 // -3:30:52 C N%sT 1919 // -3:30:52 j N%sT 1935 Mar 30 // -3:30 j N%sT 1942 May 11 // -3:30 C N%sT 1946 // -3:30 j N%sT 2011 N // -3:30 C N%sT // // R j 1917 o - Ap 8 2 1 D // R j 1917 o - S 17 2 0 S // R j 1919 o - May 5 23 1 D // R j 1919 o - Au 12 23 0 S // R j 1920 1935 - May Su>=1 23 1 D // R j 1920 1935 - O lastSu 23 0 S // R j 1936 1941 - May M>=9 0 1 D // R j 1936 1941 - O M>=2 0 0 S // R j 1946 1950 - May Su>=8 2 1 D // R j 1946 1950 - O Su>=2 2 0 S // R j 1951 1986 - Ap lastSu 2 1 D // R j 1951 1959 - S lastSu 2 0 S // R j 1960 1986 - O lastSu 2 0 S // R j 1987 o - Ap Su>=1 0:1 1 D // R j 1987 2006 - O lastSu 0:1 0 S // R j 1988 o - Ap Su>=1 0:1 2 DD // R j 1989 2006 - Ap Su>=1 0:1 1 D // R j 2007 2011 - Mar Su>=8 0:1 1 D // R j 2007 2010 - N Su>=1 0:1 0 S // // R C 1918 o - Ap 14 2 1 D // R C 1918 o - O 27 2 0 S // R C 1942 o - F 9 2 1 W // R C 1945 o - Au 14 23u 1 P // R C 1945 o - S 30 2 0 S // R C 1974 1986 - Ap lastSu 2 1 D // R C 1974 2006 - O lastSu 2 0 S // R C 1987 2006 - Ap Su>=1 2 1 D // R C 2007 ma - Mar Su>=8 2 1 D // R C 2007 ma - N Su>=1 2 0 S using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/St_Johns"); assert_equal( // -- std::chrono::sys_info( std::chrono::sys_seconds::min(), to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s), // -3:30:52 - LMT 1884 -(3h + 30min + 52s), 0min, "LMT"), tz->get_info(std::chrono::sys_seconds::min())); assert_equal( // -- std::chrono::sys_info( std::chrono::sys_seconds::min(), to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s), // -3:30:52 - LMT 1884 -(3h + 30min + 52s), 0min, "LMT"), tz->get_info(to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 51s))); assert_range( // -3:30:52 j N%sT 1918 "[1884-01-01 03:30:52, 1917-04-08 05:30:52) -03:30:52 0min NST", tz->get_info(to_sys_seconds(1884y, std::chrono::January, 1d, 3h, 30min, 52s)), // no rule active tz->get_info(to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 51s))); // 1917 o Ap 8 2 1 D assert_range( // -3:30:52 j N%sT 1918 "[1917-04-08 05:30:52, 1917-09-17 04:30:52) -02:30:52 60min NDT", tz->get_info(to_sys_seconds(1917y, std::chrono::April, 8d, 5h, 30min, 52s)), // 1917 o Ap 8 2 1 D tz->get_info(to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 51s))); // 1917 o S 17 2 0 S assert_range("[1917-09-17 04:30:52, 1918-04-14 05:30:52) -03:30:52 0min NST", tz->get_info( // -3:30:52 j N%sT 1918 to_sys_seconds(1917y, std::chrono::September, 17d, 4h, 30min, 52s)), // 1917 o S 17 2 0 S tz->get_info( // -3:30:52 C N%sT 1919 to_sys_seconds(1918y, std::chrono::April, 14d, 5h, 30min, 51s))); // 1918 o Ap 14 2 1 D assert_range( // -3:30:52 C N%sT 1919 "[1918-04-14 05:30:52, 1918-10-27 04:30:52) -02:30:52 60min NDT", tz->get_info(to_sys_seconds(1918y, std::chrono::April, 14d, 5h, 30min, 52s)), // 1918 o Ap 14 2 1 D tz->get_info(to_sys_seconds(1918y, std::chrono::October, 27d, 4h, 30min, 51s))); // 1918 o O 27 2 0 S assert_range("[1918-10-27 04:30:52, 1919-05-06 02:30:52) -03:30:52 0min NST", tz->get_info( // -3:30:52 C N%sT 1919 to_sys_seconds(1918y, std::chrono::October, 27d, 4h, 30min, 52s)), // 1918 o O 27 2 0 S tz->get_info( // -3:30:52 j N%sT 1935 Mar 30 to_sys_seconds(1919y, std::chrono::May, 6d, 2h, 30min, 51s))); // 1919 o May 5 23 1 D assert_range( // -3:30:52 j N%sT 1935 Mar 30 "[1934-10-29 01:30:52, 1935-03-30 03:30:52) -03:30:52 0min NST", tz->get_info(to_sys_seconds(1934y, std::chrono::October, 29d, 1h, 30min, 52s)), // 1920 1935 O lastSu 23 0 S tz->get_info(to_sys_seconds(1935y, std::chrono::March, 30d, 3h, 30min, 51s))); // 1920 1935 May Su>=1 23 1 D assert_range( // -3:30 j N%sT 1942 May 11 // Changed the stdoff while the same rule remains active. "[1935-03-30 03:30:52, 1935-05-06 02:30:00) -03:30:00 0min NST", tz->get_info(to_sys_seconds(1935y, std::chrono::March, 30d, 3h, 30min, 52s)), // 1920 1935 O lastSu 23 0 S tz->get_info(to_sys_seconds(1935y, std::chrono::May, 6d, 2h, 29min, 59s))); // 1920 1935 May Su>=1 23 1 D assert_range( // -3:30 j N%sT 1942 May 11 "[1935-05-06 02:30:00, 1935-10-28 01:30:00) -02:30:00 60min NDT", tz->get_info(to_sys_seconds(1935y, std::chrono::May, 6d, 2h, 30min, 0s)), // 1920 1935 May Su>=1 23 1 D tz->get_info(to_sys_seconds(1935y, std::chrono::October, 28d, 1h, 29min, 59s))); // 1920 1935 O lastSu 23 0 S assert_range( // -3:30 j N%sT 1942 May 11 "[1941-10-06 02:30:00, 1942-05-11 03:30:00) -03:30:00 0min NST", tz->get_info(to_sys_seconds(1941y, std::chrono::October, 6d, 2h, 30min, 0s)), // 1936 1941 O M>=2 0 0 S tz->get_info(to_sys_seconds(1942y, std::chrono::May, 11d, 3h, 29min, 59s))); // 1946 1950 May Su>=8 2 1 D assert_range( // -3:30 C N%sT 1946 "[1942-05-11 03:30:00, 1945-08-14 23:00:00) -02:30:00 60min NWT", tz->get_info(to_sys_seconds(1942y, std::chrono::May, 11d, 3h, 30min, 0s)), // 1942 o F 9 2 1 W tz->get_info(to_sys_seconds(1945y, std::chrono::August, 14d, 22h, 59min, 59s))); // 1945 o Au 14 23u 1 P assert_range( // -3:30 C N%sT 1946 "[1945-08-14 23:00:00, 1945-09-30 04:30:00) -02:30:00 60min NPT", tz->get_info(to_sys_seconds(1945y, std::chrono::August, 14d, 23h, 0min, 0s)), // 1945 o Au 14 23u 1 P tz->get_info(to_sys_seconds(1945y, std::chrono::September, 30d, 4h, 29min, 59s))); // 1945 o S 30 2 0 S assert_range( "[1945-09-30 04:30:00, 1946-05-12 05:30:00) -03:30:00 0min NST", tz->get_info( to_sys_seconds(1945y, std::chrono::September, 30d, 4h, 30min, 0s)), // -3:30 C N%sT 1946 & 945 o S 30 2 0 S tz->get_info(to_sys_seconds( 1946y, std::chrono::May, 12d, 5h, 29min, 59s))); // -3:30 j N%sT 2011 N & 1946 1950 May Su>=8 2 1 D assert_range( // -3:30 j N%sT 2011 N "[1988-04-03 03:31:00, 1988-10-30 01:31:00) -01:30:00 120min NDDT", tz->get_info(to_sys_seconds(1988y, std::chrono::April, 3d, 3h, 31min, 0s)), // 1988 o Ap Su>=1 0:1 2 DD tz->get_info(to_sys_seconds(1988y, std::chrono::October, 30d, 1h, 30min, 59s))); // 1987 2006 O lastSu 0:1 0 S assert_range("[2011-03-13 03:31:00, 2011-11-06 04:30:00) -02:30:00 60min NDT", tz->get_info( // -3:30 j N%sT 2011 N to_sys_seconds(2011y, std::chrono::March, 13d, 3h, 31min, 0s)), // 2007 2011 Mar Su>=8 0:1 1 D tz->get_info( // -3:30 C N%sT to_sys_seconds(2011y, std::chrono::November, 6d, 04h, 29min, 59s))); // 2007 ma N Su>=1 2 0 S } static void test_get_at_standard_time_universal() { // Z Asia/Barnaul 5:35 - LMT 1919 D 10 // ... // 7 R +07/+08 1995 May 28 // 6 R +06/+07 2011 Mar 27 2s // ... // // ... // R R 1985 2010 - Mar lastSu 2s 1 S // R R 1996 2010 - O lastSu 2s 0 - using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Asia/Barnaul"); assert_equal( std::chrono::sys_info( to_sys_seconds(2010y, std::chrono::October, 30d, 20h), to_sys_seconds(2011y, std::chrono::March, 26d, 20h), 6h, 0min, "+06"), tz->get_info(to_sys_seconds(2010y, std::chrono::October, 31d, 10h))); } static void test_get_at_standard_time_standard() { // Z Africa/Bissau -1:2:20 - LMT 1912 Ja 1 1u using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Bissau"); assert_equal( std::chrono::sys_info( std::chrono::sys_seconds::min(), to_sys_seconds(1912y, std::chrono::January, 1d, 1h), -(1h + 2min + 20s), 0min, "LMT"), tz->get_info(std::chrono::sys_seconds::min())); } static void test_get_at_save_universal() { // Z America/Tijuana -7:48:4 - LMT 1922 Ja 1 0:11:56 // -7 - MST 1924 // -8 - PST 1927 Jun 10 23 // -7 - MST 1930 N 15 // -8 - PST 1931 Ap // -8 1 PDT 1931 S 30 // -8 - PST 1942 Ap 24 // -8 1 PWT 1945 Au 14 23u // ... using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Tijuana"); assert_equal( std::chrono::sys_info( to_sys_seconds(1942y, std::chrono::April, 24d, 8h), to_sys_seconds(1945y, std::chrono::August, 14d, 23h), -7h, 60min, "PWT"), tz->get_info(to_sys_seconds(1942y, std::chrono::April, 24d, 8h))); } static void test_get_at_rule_standard() { // Z Antarctica/Macquarie 0 - -00 1899 N // 10 - AEST 1916 O 1 2 // 10 1 AEDT 1917 F // 10 AU AE%sT 1919 Ap 1 0s // ... // // R AU 1917 o - Ja 1 2s 1 D // R AU 1917 o - Mar lastSu 2s 0 S // R AU 1942 o - Ja 1 2s 1 D // ... using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Antarctica/Macquarie"); // Another rule where the S propagates? assert_equal( std::chrono::sys_info( to_sys_seconds(1916y, std::chrono::September, 30d, 16h), to_sys_seconds(1917y, std::chrono::March, 24d, 16h), 11h, 60min, "AEDT"), tz->get_info(to_sys_seconds(1916y, std::chrono::September, 30d, 16h))); } static void test_get_at_rule_universal() { // Z America/Nuuk -3:26:56 - LMT 1916 Jul 28 // -3 - -03 1980 Ap 6 2 // -3 E -03/-02 2023 O 29 1u // -2 E -02/-01 // // R E 1977 1980 - Ap Su>=1 1u 1 S // R E 1977 o - S lastSu 1u 0 - // R E 1978 o - O 1 1u 0 - // R E 1979 1995 - S lastSu 1u 0 - // R E 1981 ma - Mar lastSu 1u 1 S // R E 1996 ma - O lastSu 1u 0 - using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Nuuk"); assert_equal( std::chrono::sys_info( to_sys_seconds(1980y, std::chrono::April, 6d, 5h), to_sys_seconds(1980y, std::chrono::September, 28d, 1h), -2h, 60min, "-02"), tz->get_info(to_sys_seconds(1980y, std::chrono::April, 6d, 5h))); } static void test_format_with_alternatives_west() { // Z America/Nuuk -3:26:56 - LMT 1916 Jul 28 // -3 - -03 1980 Ap 6 2 // -3 E -03/-02 2023 O 29 1u // -2 E -02/-01 // // ... // R E 1981 ma - Mar lastSu 1u 1 S // R E 1996 ma - O lastSu 1u 0 - using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Nuuk"); assert_cycle( // -3 E -03/-02 "[2019-10-27 01:00:00, 2020-03-29 01:00:00) -03:00:00 0min -03", tz->get_info(to_sys_seconds(2019y, std::chrono::October, 27d, 1h)), // 1981 ma Mar lastSu 1u 1 S tz->get_info(to_sys_seconds(2020y, std::chrono::March, 29d, 0h, 59min, 59s)), // 1996 ma O lastSu 1u 0 - "[2020-03-29 01:00:00, 2020-10-25 01:00:00) -02:00:00 60min -02", tz->get_info(to_sys_seconds(2020y, std::chrono::March, 29d, 1h)), // 1996 ma O lastSu 1u 0 - tz->get_info(to_sys_seconds(2020y, std::chrono::October, 25d, 0h, 59min, 59s))); // 1981 ma Mar lastSu 1u 1 S } static void test_format_with_alternatives_east() { // Z Asia/Barnaul 5:35 - LMT 1919 D 10 // ... // 6 R +06/+07 2011 Mar 27 2s // ... // // ... // R R 1985 2010 - Mar lastSu 2s 1 S // R R 1996 2010 - O lastSu 2s 0 - using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Asia/Barnaul"); assert_cycle( // 6 R +06/+07 2011 Mar 27 2s "[2000-03-25 20:00:00, 2000-10-28 20:00:00) 07:00:00 60min +07", tz->get_info(to_sys_seconds(2000y, std::chrono::March, 25d, 20h)), // 1985 2010 Mar lastSu 2s 1 S tz->get_info(to_sys_seconds(2000y, std::chrono::October, 28d, 19h, 59min, 59s)), // 1996 2010 O lastSu 2s 0 - "[2000-10-28 20:00:00, 2001-03-24 20:00:00) 06:00:00 0min +06", tz->get_info(to_sys_seconds(2000y, std::chrono::October, 28d, 20h)), // 1996 2010 O lastSu 2s 0 - tz->get_info(to_sys_seconds(2001y, std::chrono::March, 24d, 19h, 59min, 59s))); // 1985 2010 Mar lastSu 2s 1 S } static void test_africa_algiers() { using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Algiers"); assert_equal( std::chrono::sys_info( to_sys_seconds(1977y, std::chrono::October, 20d, 23h), to_sys_seconds(1978y, std::chrono::March, 24d), 1h, std::chrono::minutes(0), "CET"), tz->get_info(to_sys_seconds(1977y, std::chrono::October, 20d, 23h))); assert_range("[1977-05-06 00:00:00, 1977-10-20 23:00:00) 01:00:00 60min WEST", // 0 d WE%sT 1977 O 21 tz->get_info(to_sys_seconds(1977y, std::chrono::May, 6d)), tz->get_info(to_sys_seconds(1977y, std::chrono::October, 20d, 22h, 59min, 59s))); assert_range("[1977-10-20 23:00:00, 1978-03-24 00:00:00) 01:00:00 0min CET", // 1 d CE%sT 1979 O 26 tz->get_info(to_sys_seconds(1977y, std::chrono::October, 20d, 23h)), tz->get_info(to_sys_seconds(1978y, std::chrono::March, 23d, 23h, 59min, 59s))); } static void test_africa_casablanca() { // Z Africa/Casablanca -0:30:20 - LMT 1913 O 26 // 0 M +00/+01 1984 Mar 16 // 1 - +01 1986 // 0 M +00/+01 2018 O 28 3 // 1 M +01/+00 // // ... // R M 2013 2018 - O lastSu 3 0 - // R M 2014 2018 - Mar lastSu 2 1 - // R M 2014 o - Jun 28 3 0 - // R M 2014 o - Au 2 2 1 - // R M 2015 o - Jun 14 3 0 - // R M 2015 o - Jul 19 2 1 - // R M 2016 o - Jun 5 3 0 - // R M 2016 o - Jul 10 2 1 - // R M 2017 o - May 21 3 0 - // R M 2017 o - Jul 2 2 1 - // R M 2018 o - May 13 3 0 - // R M 2018 o - Jun 17 2 1 - // R M 2019 o - May 5 3 -1 - // R M 2019 o - Jun 9 2 0 - // R M 2020 o - Ap 19 3 -1 - // ... using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Casablanca"); assert_range("[2018-06-17 02:00:00, 2018-10-28 02:00:00) 01:00:00 60min +01", tz->get_info(to_sys_seconds(2018y, std::chrono::June, 17d, 2h)), tz->get_info(to_sys_seconds(2018y, std::chrono::October, 28d, 1h, 59min, 59s))); assert_range("[2018-10-28 02:00:00, 2019-05-05 02:00:00) 01:00:00 0min +01", tz->get_info( // 1 M +01/+00 & R M 2018 o - Jun 17 2 1 - to_sys_seconds(2018y, std::chrono::October, 28d, 2h)), tz->get_info( // 1 M +01/+00 & R M 2019 o - May 5 3 -1 - to_sys_seconds(2019y, std::chrono::May, 5d, 1h, 59min, 59s))); // 1 M +01/+00 // Note the SAVE contains a negative value assert_range("[2019-05-05 02:00:00, 2019-06-09 02:00:00) 00:00:00 -60min +00", tz->get_info(to_sys_seconds(2019y, std::chrono::May, 5d, 2h)), // R M 2019 o - May 5 3 -1 - tz->get_info(to_sys_seconds(2019y, std::chrono::June, 9d, 1h, 59min, 59s))); // R M 2019 o - Jun 9 2 0 - assert_range("[2019-06-09 02:00:00, 2020-04-19 02:00:00) 01:00:00 0min +01", tz->get_info( // 1 M +01/+00 & R M 2019 o - Jun 9 2 0 - to_sys_seconds(2019y, std::chrono::June, 9d, 2h)), tz->get_info( // 1 M +01/+00 & R M 2020 o - Ap 19 3 -1 - to_sys_seconds(2020y, std::chrono::April, 19d, 1h, 59min, 59s))); // } static void test_africa_ceuta() { // Z Africa/Ceuta -0:21:16 - LMT 1900 D 31 23:38:44 // 0 - WET 1918 May 6 23 // 0 1 WEST 1918 O 7 23 // 0 - WET 1924 // 0 s WE%sT 1929 // 0 - WET 1967 // 0 Sp WE%sT 1984 Mar 16 // 1 - CET 1986 // 1 E CE%sT // // ... // R s 1926 o - Ap 17 23 1 S // R s 1926 1929 - O Sa>=1 24s 0 - // R s 1927 o - Ap 9 23 1 S // R s 1928 o - Ap 15 0 1 S // R s 1929 o - Ap 20 23 1 S // R s 1937 o - Jun 16 23 1 S // ... // // R Sp 1967 o - Jun 3 12 1 S // R Sp 1967 o - O 1 0 0 - // R Sp 1974 o - Jun 24 0 1 S // R Sp 1974 o - S 1 0 0 - // R Sp 1976 1977 - May 1 0 1 S // R Sp 1976 o - Au 1 0 0 - // R Sp 1977 o - S 28 0 0 - // R Sp 1978 o - Jun 1 0 1 S // R Sp 1978 o - Au 4 0 0 - using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Ceuta"); assert_range( "[1928-10-07 00:00:00, 1967-06-03 12:00:00) 00:00:00 0min WET", tz->get_info(to_sys_seconds(1928y, std::chrono::October, 7d)), // 0 s WE%sT 1929 & 1926 1929 O Sa>=1 24s 0 - tz->get_info( // No transitions in "0 - WET 1967" to_sys_seconds(1967y, std::chrono::June, 3d, 11h, 59min, 59s))); // 0 - WET 1967 & 1967 o Jun 3 12 1 S } static void test_africa_freetown() { // Z Africa/Freetown -0:53 - LMT 1882 // -0:53 - FMT 1913 Jul // -1 SL %s 1939 S 5 // -1 - -01 1941 D 6 24 // 0 - GMT // // R SL 1932 o - D 1 0 0:20 -0040 // R SL 1933 1938 - Mar 31 24 0 -01 // R SL 1933 1939 - Au 31 24 0:20 -0040 // R SL 1939 o - May 31 24 0 -01 using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Freetown"); // When a continuation has a named rule, the transition time determined by // the active rule can be wrong. The next continuation may set the clock to an // earlier time. This is tested for San Luis. This tests the rule is not used // when the rule is not a named rule. // // Fixes: // Expected output [1882-01-01 00:53:00, 1913-07-01 00:53:00) -00:53:00 0min FMT // Actual output [1882-01-01 00:53:00, 1913-07-01 00:46:00) -00:53:00 0min FMT assert_range("[1882-01-01 00:53:00, 1913-07-01 00:53:00) -00:53:00 0min FMT", tz->get_info(to_sys_seconds(1882y, std::chrono::January, 1d, 0h, 53min)), // -0:53 - FMT 1913 Jul tz->get_info( // -1 SL %s 1939 S 5 & before first rule to_sys_seconds(1913y, std::chrono::July, 1d, 0h, 52min, 59s))); // Tests whether the "-1 SL %s 1939 S 5" until gets the proper local time // adjustment. assert_range("[1939-09-01 01:00:00, 1939-09-05 00:40:00) -00:40:00 20min -0040", tz->get_info( // -1 SL %s 1939 S 5 & R SL 1933 1939 - Au 31 24 0:20 -0040 to_sys_seconds(1939y, std::chrono::September, 1d, 1h)), tz->get_info( // -1 - -01 1941 D 6 24 to_sys_seconds(1939y, std::chrono::September, 5d, 0h, 39min, 59s))); } static void test_africa_windhoek() { // Tests the LETTER/S used before the first rule per // https://data.iana.org/time-zones/tz-how-to.html // If switching to a named rule before any transition has happened, // assume standard time (SAVE zero), and use the LETTER data from // the earliest transition with a SAVE of zero. // Z Africa/Windhoek 1:8:24 - LMT 1892 F 8 // 1:30 - +0130 1903 Mar // 2 - SAST 1942 S 20 2 // 2 1 SAST 1943 Mar 21 2 // 2 - SAST 1990 Mar 21 // 2 NA %s // // R NA 1994 o - Mar 21 0 -1 WAT // R NA 1994 2017 - S Su>=1 2 0 CAT // R NA 1995 2017 - Ap Su>=1 2 -1 WAT using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Africa/Windhoek"); assert_range( // 2 - EET 2012 N 10 2 "[1990-03-20 22:00:00, 1994-03-20 22:00:00) 02:00:00 0min CAT", tz->get_info(to_sys_seconds(1990y, std::chrono::March, 20d, 22h)), tz->get_info(to_sys_seconds(1994y, std::chrono::March, 20d, 21h, 59min, 59s))); } static void test_america_adak() { // Z America/Adak 12:13:22 - LMT 1867 O 19 12:44:35 // ... // -11 u B%sT 1983 O 30 2 // -10 u AH%sT 1983 N 30 // -10 u H%sT // // ... // R u 1945 o - S 30 2 0 S // R u 1967 2006 - O lastSu 2 0 S // R u 1967 1973 - Ap lastSu 2 1 D // R u 1974 o - Ja 6 2 1 D // R u 1975 o - F lastSu 2 1 D // R u 1976 1986 - Ap lastSu 2 1 D // R u 1987 2006 - Ap Su>=1 2 1 D // ... using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Adak"); assert_range( // 2 - EET 2012 N 10 2 "[1983-10-30 12:00:00, 1983-11-30 10:00:00) -10:00:00 0min AHST", tz->get_info(to_sys_seconds(1983y, std::chrono::October, 30d, 12h)), // -11 u B%sT 1983 O 30 2 tz->get_info(to_sys_seconds(1983y, std::chrono::November, 30d, 9h, 59min, 59s))); // -10 u AH%sT 1983 N 30 } static void test_america_auncion() { // R y 2013 ma - Mar Su>=22 0 0 - // Z America/Asuncion -3:50:40 - LMT 1890 // -3:50:40 - AMT 1931 O 10 // -4 - -04 1972 O // -3 - -03 1974 Ap // -4 y -04/-03 // // R y 1975 1988 - O 1 0 1 - // R y 1975 1978 - Mar 1 0 0 - // R y 1979 1991 - Ap 1 0 0 - // ... using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Asuncion"); assert_range("[1974-04-01 03:00:00, 1975-10-01 04:00:00) -04:00:00 0min -04", tz->get_info(to_sys_seconds(1974y, std::chrono::April, 1d, 3h)), tz->get_info(to_sys_seconds(1975y, std::chrono::October, 1d, 3h, 59min, 59s))); assert_range("[1975-10-01 04:00:00, 1976-03-01 03:00:00) -03:00:00 60min -03", tz->get_info(to_sys_seconds(1975y, std::chrono::October, 1d, 4h)), tz->get_info(to_sys_seconds(1976y, std::chrono::March, 1d, 2h, 59min, 59s))); } static void test_america_ciudad_juarez() { // Z America/Ciudad_Juarez -7:5:56 - LMT 1922 Ja 1 7u // -7 - MST 1927 Jun 10 23 // -6 - CST 1930 N 15 // -7 m MST 1932 Ap // -6 - CST 1996 // -6 m C%sT 1998 // ... // // R m 1939 o - F 5 0 1 D // R m 1939 o - Jun 25 0 0 S // R m 1940 o - D 9 0 1 D // R m 1941 o - Ap 1 0 0 S // R m 1943 o - D 16 0 1 W // R m 1944 o - May 1 0 0 S // R m 1950 o - F 12 0 1 D // R m 1950 o - Jul 30 0 0 S // R m 1996 2000 - Ap Su>=1 2 1 D // R m 1996 2000 - O lastSu 2 0 S // ... using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Ciudad_Juarez"); // 1996 has a similar issue, instead of __time the __until end before // the first rule in 1939. Between the two usages of RULE Mexico // a different continuation RULE is active assert_range("[1996-04-07 08:00:00, 1996-10-27 07:00:00) -05:00:00 60min CDT", tz->get_info(to_sys_seconds(1996y, std::chrono::April, 7d, 8h)), tz->get_info(to_sys_seconds(1996y, std::chrono::October, 27d, 6h, 59min, 59s))); } static void test_america_argentina_buenos_aires() { // Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31 // -4:16:48 - CMT 1920 May // -4 - -04 1930 D // -4 A -04/-03 1969 O 5 // -3 A -03/-02 1999 O 3 // -4 A -04/-03 2000 Mar 3 // -3 A -03/-02 // // ... // R A 1989 1992 - O Su>=15 0 1 - // R A 1999 o - O Su>=1 0 1 - // R A 2000 o - Mar 3 0 0 - // R A 2007 o - D 30 0 1 - // ... // The 1999 switch uses the same rule, but with a different stdoff. // R A 1999 o - O Su>=1 0 1 - // stdoff -3 -> 1999-10-03 03:00:00 // stdoff -4 -> 1999-10-03 04:00:00 // This generates an invalid entry and this is evaluated as a transition. // Looking at the zdump like output in libc++ this generates jumps in // the UTC time using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Argentina/Buenos_Aires"); assert_range("[1999-10-03 03:00:00, 2000-03-03 03:00:00) -03:00:00 60min -03", tz->get_info(to_sys_seconds(1999y, std::chrono::October, 3d, 3h)), tz->get_info(to_sys_seconds(2000y, std::chrono::March, 3d, 2h, 59min, 59s))); assert_range("[2000-03-03 03:00:00, 2007-12-30 03:00:00) -03:00:00 0min -03", tz->get_info(to_sys_seconds(2000y, std::chrono::March, 3d, 3h)), tz->get_info(to_sys_seconds(2007y, std::chrono::December, 30d, 2h, 59min, 59s))); } static void test_america_argentina_la_rioja() { // Z America/Argentina/La_Rioja -4:27:24 - LMT 1894 O 31 // ... // -4 A -04/-03 1969 O 5 // -3 A -03/-02 1991 Mar // -4 - -04 1991 May 7 // -3 A -03/-02 1999 O 3 // ... // // ... // R A 1988 o - D 1 0 1 - // R A 1989 1993 - Mar Su>=1 0 0 - // R A 1989 1992 - O Su>=15 0 1 - // R A 1999 o - O Su>=1 0 1 - // ... using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Argentina/La_Rioja"); assert_range("[1990-10-21 03:00:00, 1991-03-01 02:00:00) -02:00:00 60min -02", tz->get_info(to_sys_seconds(1990y, std::chrono::October, 21d, 3h)), tz->get_info(to_sys_seconds(1991y, std::chrono::March, 1d, 1h, 59min, 59s))); } static void test_america_argentina_san_luis() { // Z America/Argentina/San_Luis -4:25:24 - LMT 1894 O 31 // ... // -4 A -04/-03 1969 O 5 // -3 A -03/-02 1990 // -3 1 -02 1990 Mar 14 // -4 - -04 1990 O 15 // -4 1 -03 1991 Mar // -4 - -04 1991 Jun // -3 - -03 1999 O 3 // -4 1 -03 2000 Mar 3 // -4 - -04 2004 Jul 25 // -3 A -03/-02 2008 Ja 21 // -4 Sa -04/-03 2009 O 11 // -3 - -03 // // ... // R A 1988 o - D 1 0 1 - // R A 1989 1993 - Mar Su>=1 0 0 - // R A 1989 1992 - O Su>=15 0 1 - // R A 1999 o - O Su>=1 0 1 - // R A 2000 o - Mar 3 0 0 - // R A 2007 o - D 30 0 1 - // R A 2008 2009 - Mar Su>=15 0 0 - // R A 2008 o - O Su>=15 0 1 - // // R Sa 2008 2009 - Mar Su>=8 0 0 - // R Sa 2007 2008 - O Su>=8 0 1 - using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Argentina/San_Luis"); assert_range("[1989-10-15 03:00:00, 1990-03-14 02:00:00) -02:00:00 60min -02", tz->get_info( // -3 A -03/-02 1990 & R A 1989 1992 - O Su>=15 0 1 - to_sys_seconds(1989y, std::chrono::October, 15d, 3h)), tz->get_info( // UNTIL -3 1 -02 1990 Mar 14 to_sys_seconds(1990y, std::chrono::March, 14d, 1h, 59min, 59s))); assert_range("[2008-01-21 02:00:00, 2008-03-09 03:00:00) -03:00:00 60min -03", tz->get_info(to_sys_seconds(2008y, std::chrono::January, 21d, 2h)), tz->get_info(to_sys_seconds(2008y, std::chrono::March, 9d, 2h, 59min, 59s))); } static void test_america_indiana_knox() { // Z America/Indiana/Knox -5:46:30 - LMT 1883 N 18 12:13:30 // -6 u C%sT 1947 // -6 St C%sT 1962 Ap 29 2 // -5 - EST 1963 O 27 2 // -6 u C%sT 1991 O 27 2 // -5 - EST 2006 Ap 2 2 // -6 u C%sT // // ... // R u 1976 1986 - Ap lastSu 2 1 D // R u 1987 2006 - Ap Su>=1 2 1 D // R u 2007 ma - Mar Su>=8 2 1 D // R u 2007 ma - N Su>=1 2 0 S using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Indiana/Knox"); // The continuations // -5 - EST // -6 u C%sT // have different offsets. The start time of the first active rule in // RULE u should use the offset at the end of -5 - EST. assert_range("[2006-04-02 07:00:00, 2006-10-29 07:00:00) -05:00:00 60min CDT", tz->get_info(to_sys_seconds(2006y, std::chrono::April, 2d, 7h)), tz->get_info(to_sys_seconds(2006y, std::chrono::October, 29d, 6h, 59min, 59s))); } static void test_america_punta_arenas() { // Z America/Punta_Arenas -4:43:40 - LMT 1890 // ... // -4 - -04 1919 Jul // -4:42:45 - SMT 1927 S // -5 x -05/-04 1932 S // ... // // R x 1927 1931 - S 1 0 1 - // R x 1928 1932 - Ap 1 0 0 - // ... using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("America/Punta_Arenas"); assert_equal( std::chrono::sys_info( to_sys_seconds(1927y, std::chrono::September, 1d, 4h, 42min, 45s), to_sys_seconds(1928y, std::chrono::April, 1d, 4h), -4h, 60min, "-04"), tz->get_info(to_sys_seconds(1927y, std::chrono::September, 1d, 4h, 42min, 45s))); assert_equal( std::chrono::sys_info( to_sys_seconds(1927y, std::chrono::September, 1d, 4h, 42min, 45s), to_sys_seconds(1928y, std::chrono::April, 1d, 4h), -4h, 60min, "-04"), tz->get_info(to_sys_seconds(1928y, std::chrono::April, 1d, 3h, 59min, 59s))); } static void test_europ_ljubljana() { // Z Europe/Ljubljana 0:58:4 - LMT 1884 // 1 - CET 1941 Ap 18 23 // 1 c CE%sT 1945 May 8 2s // 1 1 CEST 1945 S 16 2s // 1 - CET 1982 N 27 // 1 E CE%sT // // ... // R c 1943 o - O 4 2s 0 - // R c 1944 1945 - Ap M>=1 2s 1 S // R c 1944 o - O 2 2s 0 - // R c 1945 o - S 16 2s 0 - // R c 1977 1980 - Ap Su>=1 2s 1 S // ... using namespace std::literals::chrono_literals; const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Ljubljana"); assert_equal( std::chrono::sys_info( to_sys_seconds(1945y, std::chrono::April, 2d, 1h), to_sys_seconds(1945y, std::chrono::September, 16d, 1h), 2h, 60min, "CEST"), tz->get_info(to_sys_seconds(1945y, std::chrono::April, 2d, 1h))); assert_equal( std::chrono::sys_info( to_sys_seconds(1945y, std::chrono::April, 2d, 1h), to_sys_seconds(1945y, std::chrono::September, 16d, 1h), 2h, 60min, "CEST"), tz->get_info(to_sys_seconds(1945y, std::chrono::September, 16d, 0h, 59min, 59s))); } int main(int, const char**) { // Basic tests test_gmt(); test_durations(); test_antarctica_syowa(); test_asia_hong_kong(); test_europe_berlin(); test_america_st_johns(); // Small tests for not-yet tested conditions test_get_at_standard_time_universal(); test_get_at_standard_time_standard(); test_get_at_save_universal(); test_get_at_rule_standard(); test_get_at_rule_universal(); test_format_with_alternatives_west(); test_format_with_alternatives_east(); // Tests based on bugs found test_africa_algiers(); test_africa_casablanca(); test_africa_ceuta(); test_africa_freetown(); test_africa_windhoek(); test_america_adak(); test_america_argentina_buenos_aires(); test_america_argentina_la_rioja(); test_america_argentina_san_luis(); test_america_auncion(); test_america_ciudad_juarez(); test_america_indiana_knox(); // Reverse search bugs test_america_punta_arenas(); test_europ_ljubljana(); return 0; }