diff options
author | orivej <orivej@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
commit | 718c552901d703c502ccbefdfc3c9028d608b947 (patch) | |
tree | 46534a98bbefcd7b1f3faa5b52c138ab27db75b7 /contrib/libs/cctz/test | |
parent | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (diff) | |
download | ydb-718c552901d703c502ccbefdfc3c9028d608b947.tar.gz |
Restoring authorship annotation for <orivej@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/cctz/test')
-rw-r--r-- | contrib/libs/cctz/test/civil_time_test.cc | 72 | ||||
-rw-r--r-- | contrib/libs/cctz/test/time_zone_format_test.cc | 3176 | ||||
-rw-r--r-- | contrib/libs/cctz/test/time_zone_lookup_test.cc | 694 | ||||
-rw-r--r-- | contrib/libs/cctz/test/ya.make | 20 |
4 files changed, 1981 insertions, 1981 deletions
diff --git a/contrib/libs/cctz/test/civil_time_test.cc b/contrib/libs/cctz/test/civil_time_test.cc index 5ec0ca8d38..0b13cc1b7a 100644 --- a/contrib/libs/cctz/test/civil_time_test.cc +++ b/contrib/libs/cctz/test/civil_time_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// https://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "cctz/civil_time.h" +#include "cctz/civil_time.h" #include <iomanip> #include <limits> @@ -20,7 +20,7 @@ #include <string> #include <type_traits> -#include "gtest/gtest.h" +#include "gtest/gtest.h" namespace cctz { @@ -35,7 +35,7 @@ std::string Format(const T& t) { } // namespace -#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) +#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) // Construction constexpr tests TEST(CivilTime, Normal) { @@ -223,7 +223,7 @@ TEST(CivilTime, Subtraction) { static_assert(cs2.second() == 22, "Subtraction.second"); } -TEST(CivilTime, Difference) { +TEST(CivilTime, Difference) { constexpr civil_day cd1(2016, 1, 28); constexpr civil_day cd2(2015, 1, 28); constexpr int diff = cd1 - cd2; @@ -231,16 +231,16 @@ TEST(CivilTime, Difference) { } // NOTE: Run this with --copt=-ftrapv to detect overflow problems. -TEST(CivilTime, ConstructionWithHugeYear) { - constexpr civil_hour h(-9223372036854775807, 1, 1, -1); - static_assert(h.year() == -9223372036854775807 - 1, - "ConstructionWithHugeYear"); - static_assert(h.month() == 12, "ConstructionWithHugeYear"); - static_assert(h.day() == 31, "ConstructionWithHugeYear"); - static_assert(h.hour() == 23, "ConstructionWithHugeYear"); -} - -// NOTE: Run this with --copt=-ftrapv to detect overflow problems. +TEST(CivilTime, ConstructionWithHugeYear) { + constexpr civil_hour h(-9223372036854775807, 1, 1, -1); + static_assert(h.year() == -9223372036854775807 - 1, + "ConstructionWithHugeYear"); + static_assert(h.month() == 12, "ConstructionWithHugeYear"); + static_assert(h.day() == 31, "ConstructionWithHugeYear"); + static_assert(h.hour() == 23, "ConstructionWithHugeYear"); +} + +// NOTE: Run this with --copt=-ftrapv to detect overflow problems. TEST(CivilTime, DifferenceWithHugeYear) { { constexpr civil_day d1(9223372036854775807, 1, 1); @@ -327,7 +327,7 @@ TEST(CivilTime, YearDay) { constexpr int yd = get_yearday(cd); static_assert(yd == 28, "YearDay"); } -#endif // __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) +#endif // __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) // The remaining tests do not use constexpr. @@ -628,7 +628,7 @@ TEST(CivilTime, Relational) { TEST_RELATIONAL(civil_second(2014, 1, 1, 1, 1, 0), civil_second(2014, 1, 1, 1, 1, 1)); - // Tests the relational operators of two different civil-time types. + // Tests the relational operators of two different civil-time types. TEST_RELATIONAL(civil_day(2014, 1, 1), civil_minute(2014, 1, 1, 1, 1)); TEST_RELATIONAL(civil_day(2014, 1, 1), civil_month(2014, 2)); @@ -768,7 +768,7 @@ TEST(CivilTime, ArithmeticLimits) { EXPECT_EQ("0", Format(year)); } -TEST(CivilTime, ArithmeticDifference) { +TEST(CivilTime, ArithmeticDifference) { civil_second second(2015, 1, 2, 3, 4, 5); EXPECT_EQ(0, second - second); EXPECT_EQ(10, (second + 10) - second); @@ -829,8 +829,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(4, ss.hour()); EXPECT_EQ(5, ss.minute()); EXPECT_EQ(6, ss.second()); - EXPECT_EQ(weekday::tuesday, get_weekday(ss)); - EXPECT_EQ(34, get_yearday(ss)); + EXPECT_EQ(weekday::tuesday, get_weekday(ss)); + EXPECT_EQ(34, get_yearday(ss)); civil_minute mm(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, mm.year()); @@ -839,8 +839,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(4, mm.hour()); EXPECT_EQ(5, mm.minute()); EXPECT_EQ(0, mm.second()); - EXPECT_EQ(weekday::tuesday, get_weekday(mm)); - EXPECT_EQ(34, get_yearday(mm)); + EXPECT_EQ(weekday::tuesday, get_weekday(mm)); + EXPECT_EQ(34, get_yearday(mm)); civil_hour hh(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, hh.year()); @@ -849,8 +849,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(4, hh.hour()); EXPECT_EQ(0, hh.minute()); EXPECT_EQ(0, hh.second()); - EXPECT_EQ(weekday::tuesday, get_weekday(hh)); - EXPECT_EQ(34, get_yearday(hh)); + EXPECT_EQ(weekday::tuesday, get_weekday(hh)); + EXPECT_EQ(34, get_yearday(hh)); civil_day d(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, d.year()); @@ -869,8 +869,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(0, m.hour()); EXPECT_EQ(0, m.minute()); EXPECT_EQ(0, m.second()); - EXPECT_EQ(weekday::sunday, get_weekday(m)); - EXPECT_EQ(32, get_yearday(m)); + EXPECT_EQ(weekday::sunday, get_weekday(m)); + EXPECT_EQ(32, get_yearday(m)); civil_year y(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, y.year()); @@ -879,8 +879,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(0, y.hour()); EXPECT_EQ(0, y.minute()); EXPECT_EQ(0, y.second()); - EXPECT_EQ(weekday::thursday, get_weekday(y)); - EXPECT_EQ(1, get_yearday(y)); + EXPECT_EQ(weekday::thursday, get_weekday(y)); + EXPECT_EQ(1, get_yearday(y)); } TEST(CivilTime, OutputStream) { @@ -966,37 +966,37 @@ TEST(CivilTime, NextPrevWeekday) { // Thursday -> Thursday civil_day d = next_weekday(thursday, weekday::thursday); - EXPECT_EQ(7, d - thursday) << Format(d); + EXPECT_EQ(7, d - thursday) << Format(d); EXPECT_EQ(d - 14, prev_weekday(thursday, weekday::thursday)); // Thursday -> Friday d = next_weekday(thursday, weekday::friday); - EXPECT_EQ(1, d - thursday) << Format(d); + EXPECT_EQ(1, d - thursday) << Format(d); EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::friday)); // Thursday -> Saturday d = next_weekday(thursday, weekday::saturday); - EXPECT_EQ(2, d - thursday) << Format(d); + EXPECT_EQ(2, d - thursday) << Format(d); EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::saturday)); // Thursday -> Sunday d = next_weekday(thursday, weekday::sunday); - EXPECT_EQ(3, d - thursday) << Format(d); + EXPECT_EQ(3, d - thursday) << Format(d); EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::sunday)); // Thursday -> Monday d = next_weekday(thursday, weekday::monday); - EXPECT_EQ(4, d - thursday) << Format(d); + EXPECT_EQ(4, d - thursday) << Format(d); EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::monday)); // Thursday -> Tuesday d = next_weekday(thursday, weekday::tuesday); - EXPECT_EQ(5, d - thursday) << Format(d); + EXPECT_EQ(5, d - thursday) << Format(d); EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::tuesday)); // Thursday -> Wednesday d = next_weekday(thursday, weekday::wednesday); - EXPECT_EQ(6, d - thursday) << Format(d); + EXPECT_EQ(6, d - thursday) << Format(d); EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::wednesday)); } @@ -1053,7 +1053,7 @@ TEST(CivilTime, LeapYears) { TEST(CivilTime, FirstThursdayInMonth) { const civil_day nov1(2014, 11, 1); - const civil_day thursday = next_weekday(nov1 - 1, weekday::thursday); + const civil_day thursday = next_weekday(nov1 - 1, weekday::thursday); EXPECT_EQ("2014-11-06", Format(thursday)); // Bonus: Date of Thanksgiving in the United States diff --git a/contrib/libs/cctz/test/time_zone_format_test.cc b/contrib/libs/cctz/test/time_zone_format_test.cc index 0d07dad5c0..724836fa14 100644 --- a/contrib/libs/cctz/test/time_zone_format_test.cc +++ b/contrib/libs/cctz/test/time_zone_format_test.cc @@ -1,1588 +1,1588 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "cctz/time_zone.h" - -#include <chrono> -#include <iomanip> -#include <sstream> -#include <string> - -#include "cctz/civil_time.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace chrono = std::chrono; - -namespace cctz { - -namespace { - -// This helper is a macro so that failed expectations show up with the -// correct line numbers. -#define ExpectTime(tp, tz, y, m, d, hh, mm, ss, off, isdst, zone) \ - do { \ - time_zone::absolute_lookup al = tz.lookup(tp); \ - EXPECT_EQ(y, al.cs.year()); \ - EXPECT_EQ(m, al.cs.month()); \ - EXPECT_EQ(d, al.cs.day()); \ - EXPECT_EQ(hh, al.cs.hour()); \ - EXPECT_EQ(mm, al.cs.minute()); \ - EXPECT_EQ(ss, al.cs.second()); \ - EXPECT_EQ(off, al.offset); \ - EXPECT_TRUE(isdst == al.is_dst); \ - EXPECT_STREQ(zone, al.abbr); \ - } while (0) - -const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez"; -const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez"; - -const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z"; -const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; - -// A helper that tests the given format specifier by itself, and with leading -// and trailing characters. For example: TestFormatSpecifier(tp, "%a", "Thu"). -template <typename D> -void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt, - const std::string& ans) { - EXPECT_EQ(ans, format(fmt, tp, tz)) << fmt; - EXPECT_EQ("xxx " + ans, format("xxx " + fmt, tp, tz)); - EXPECT_EQ(ans + " yyy", format(fmt + " yyy", tp, tz)); - EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz)); -} - -} // namespace - -// -// Testing format() -// - -TEST(Format, TimePointResolution) { - const char kFmt[] = "%H:%M:%E*S"; - const time_zone utc = utc_time_zone(); - const time_point<chrono::nanoseconds> t0 = - chrono::system_clock::from_time_t(1420167845) + - chrono::milliseconds(123) + chrono::microseconds(456) + - chrono::nanoseconds(789); - EXPECT_EQ( - "03:04:05.123456789", - format(kFmt, chrono::time_point_cast<chrono::nanoseconds>(t0), utc)); - EXPECT_EQ( - "03:04:05.123456", - format(kFmt, chrono::time_point_cast<chrono::microseconds>(t0), utc)); - EXPECT_EQ( - "03:04:05.123", - format(kFmt, chrono::time_point_cast<chrono::milliseconds>(t0), utc)); - EXPECT_EQ("03:04:05", - format(kFmt, chrono::time_point_cast<chrono::seconds>(t0), utc)); - EXPECT_EQ("03:04:05", - format(kFmt, chrono::time_point_cast<cctz::seconds>(t0), utc)); - EXPECT_EQ("03:04:00", - format(kFmt, chrono::time_point_cast<chrono::minutes>(t0), utc)); - EXPECT_EQ("03:00:00", - format(kFmt, chrono::time_point_cast<chrono::hours>(t0), utc)); -} - -TEST(Format, TimePointExtendedResolution) { - const char kFmt[] = "%H:%M:%E*S"; - const time_zone utc = utc_time_zone(); - const time_point<cctz::seconds> tp = - chrono::time_point_cast<cctz::seconds>( - chrono::system_clock::from_time_t(0)) + - chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56); - - EXPECT_EQ( - "12:34:56.123456789012345", - detail::format(kFmt, tp, detail::femtoseconds(123456789012345), utc)); - EXPECT_EQ( - "12:34:56.012345678901234", - detail::format(kFmt, tp, detail::femtoseconds(12345678901234), utc)); - EXPECT_EQ( - "12:34:56.001234567890123", - detail::format(kFmt, tp, detail::femtoseconds(1234567890123), utc)); - EXPECT_EQ( - "12:34:56.000123456789012", - detail::format(kFmt, tp, detail::femtoseconds(123456789012), utc)); - - EXPECT_EQ("12:34:56.000000000000123", - detail::format(kFmt, tp, detail::femtoseconds(123), utc)); - EXPECT_EQ("12:34:56.000000000000012", - detail::format(kFmt, tp, detail::femtoseconds(12), utc)); - EXPECT_EQ("12:34:56.000000000000001", - detail::format(kFmt, tp, detail::femtoseconds(1), utc)); -} - -TEST(Format, Basics) { - time_zone tz = utc_time_zone(); - time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); - - // Starts with a couple basic edge cases. - EXPECT_EQ("", format("", tp, tz)); - EXPECT_EQ(" ", format(" ", tp, tz)); - EXPECT_EQ(" ", format(" ", tp, tz)); - EXPECT_EQ("xxx", format("xxx", tp, tz)); - std::string big(128, 'x'); - EXPECT_EQ(big, format(big, tp, tz)); - // Cause the 1024-byte buffer to grow. - std::string bigger(100000, 'x'); - EXPECT_EQ(bigger, format(bigger, tp, tz)); - - tp += chrono::hours(13) + chrono::minutes(4) + chrono::seconds(5); - tp += chrono::milliseconds(6) + chrono::microseconds(7) + - chrono::nanoseconds(8); - EXPECT_EQ("1970-01-01", format("%Y-%m-%d", tp, tz)); - EXPECT_EQ("13:04:05", format("%H:%M:%S", tp, tz)); - EXPECT_EQ("13:04:05.006", format("%H:%M:%E3S", tp, tz)); - EXPECT_EQ("13:04:05.006007", format("%H:%M:%E6S", tp, tz)); - EXPECT_EQ("13:04:05.006007008", format("%H:%M:%E9S", tp, tz)); -} - -TEST(Format, PosixConversions) { - const time_zone tz = utc_time_zone(); - auto tp = chrono::system_clock::from_time_t(0); - - TestFormatSpecifier(tp, tz, "%d", "01"); - TestFormatSpecifier(tp, tz, "%e", " 1"); // extension but internal support - TestFormatSpecifier(tp, tz, "%H", "00"); - TestFormatSpecifier(tp, tz, "%I", "12"); - TestFormatSpecifier(tp, tz, "%j", "001"); - TestFormatSpecifier(tp, tz, "%m", "01"); - TestFormatSpecifier(tp, tz, "%M", "00"); - TestFormatSpecifier(tp, tz, "%S", "00"); - TestFormatSpecifier(tp, tz, "%U", "00"); - TestFormatSpecifier(tp, tz, "%w", "4"); // 4=Thursday - TestFormatSpecifier(tp, tz, "%W", "00"); - TestFormatSpecifier(tp, tz, "%y", "70"); - TestFormatSpecifier(tp, tz, "%Y", "1970"); - TestFormatSpecifier(tp, tz, "%z", "+0000"); - TestFormatSpecifier(tp, tz, "%Z", "UTC"); - TestFormatSpecifier(tp, tz, "%%", "%"); - -#if defined(__linux__) - // SU/C99/TZ extensions - TestFormatSpecifier(tp, tz, "%C", "19"); - TestFormatSpecifier(tp, tz, "%D", "01/01/70"); - TestFormatSpecifier(tp, tz, "%F", "1970-01-01"); - TestFormatSpecifier(tp, tz, "%g", "70"); - TestFormatSpecifier(tp, tz, "%G", "1970"); - TestFormatSpecifier(tp, tz, "%k", " 0"); - TestFormatSpecifier(tp, tz, "%l", "12"); - TestFormatSpecifier(tp, tz, "%n", "\n"); - TestFormatSpecifier(tp, tz, "%R", "00:00"); - TestFormatSpecifier(tp, tz, "%t", "\t"); - TestFormatSpecifier(tp, tz, "%T", "00:00:00"); - TestFormatSpecifier(tp, tz, "%u", "4"); // 4=Thursday - TestFormatSpecifier(tp, tz, "%V", "01"); - TestFormatSpecifier(tp, tz, "%s", "0"); -#endif -} - -TEST(Format, LocaleSpecific) { - const time_zone tz = utc_time_zone(); - auto tp = chrono::system_clock::from_time_t(0); - - TestFormatSpecifier(tp, tz, "%a", "Thu"); - TestFormatSpecifier(tp, tz, "%A", "Thursday"); - TestFormatSpecifier(tp, tz, "%b", "Jan"); - TestFormatSpecifier(tp, tz, "%B", "January"); - - // %c should at least produce the numeric year and time-of-day. - const std::string s = format("%c", tp, utc_time_zone()); - EXPECT_THAT(s, testing::HasSubstr("1970")); - EXPECT_THAT(s, testing::HasSubstr("00:00:00")); - - TestFormatSpecifier(tp, tz, "%p", "AM"); - TestFormatSpecifier(tp, tz, "%x", "01/01/70"); - TestFormatSpecifier(tp, tz, "%X", "00:00:00"); - -#if defined(__linux__) - // SU/C99/TZ extensions - TestFormatSpecifier(tp, tz, "%h", "Jan"); // Same as %b - TestFormatSpecifier(tp, tz, "%P", "am"); - TestFormatSpecifier(tp, tz, "%r", "12:00:00 AM"); - - // Modified conversion specifiers %E_ - TestFormatSpecifier(tp, tz, "%Ec", "Thu Jan 1 00:00:00 1970"); - TestFormatSpecifier(tp, tz, "%EC", "19"); - TestFormatSpecifier(tp, tz, "%Ex", "01/01/70"); - TestFormatSpecifier(tp, tz, "%EX", "00:00:00"); - TestFormatSpecifier(tp, tz, "%Ey", "70"); - TestFormatSpecifier(tp, tz, "%EY", "1970"); - - // Modified conversion specifiers %O_ - TestFormatSpecifier(tp, tz, "%Od", "01"); - TestFormatSpecifier(tp, tz, "%Oe", " 1"); - TestFormatSpecifier(tp, tz, "%OH", "00"); - TestFormatSpecifier(tp, tz, "%OI", "12"); - TestFormatSpecifier(tp, tz, "%Om", "01"); - TestFormatSpecifier(tp, tz, "%OM", "00"); - TestFormatSpecifier(tp, tz, "%OS", "00"); - TestFormatSpecifier(tp, tz, "%Ou", "4"); // 4=Thursday - TestFormatSpecifier(tp, tz, "%OU", "00"); - TestFormatSpecifier(tp, tz, "%OV", "01"); - TestFormatSpecifier(tp, tz, "%Ow", "4"); // 4=Thursday - TestFormatSpecifier(tp, tz, "%OW", "00"); - TestFormatSpecifier(tp, tz, "%Oy", "70"); -#endif -} - -TEST(Format, Escaping) { - const time_zone tz = utc_time_zone(); - auto tp = chrono::system_clock::from_time_t(0); - - TestFormatSpecifier(tp, tz, "%%", "%"); - TestFormatSpecifier(tp, tz, "%%a", "%a"); - TestFormatSpecifier(tp, tz, "%%b", "%b"); - TestFormatSpecifier(tp, tz, "%%Ea", "%Ea"); - TestFormatSpecifier(tp, tz, "%%Es", "%Es"); - TestFormatSpecifier(tp, tz, "%%E3S", "%E3S"); - TestFormatSpecifier(tp, tz, "%%OS", "%OS"); - TestFormatSpecifier(tp, tz, "%%O3S", "%O3S"); - - // Multiple levels of escaping. - TestFormatSpecifier(tp, tz, "%%%Y", "%1970"); - TestFormatSpecifier(tp, tz, "%%%E3S", "%00.000"); - TestFormatSpecifier(tp, tz, "%%%%E3S", "%%E3S"); -} - -TEST(Format, ExtendedSeconds) { - const time_zone tz = utc_time_zone(); - - // No subseconds. - time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); - tp += chrono::seconds(5); - EXPECT_EQ("05", format("%E*S", tp, tz)); - EXPECT_EQ("05", format("%E0S", tp, tz)); - EXPECT_EQ("05.0", format("%E1S", tp, tz)); - EXPECT_EQ("05.00", format("%E2S", tp, tz)); - EXPECT_EQ("05.000", format("%E3S", tp, tz)); - EXPECT_EQ("05.0000", format("%E4S", tp, tz)); - EXPECT_EQ("05.00000", format("%E5S", tp, tz)); - EXPECT_EQ("05.000000", format("%E6S", tp, tz)); - EXPECT_EQ("05.0000000", format("%E7S", tp, tz)); - EXPECT_EQ("05.00000000", format("%E8S", tp, tz)); - EXPECT_EQ("05.000000000", format("%E9S", tp, tz)); - EXPECT_EQ("05.0000000000", format("%E10S", tp, tz)); - EXPECT_EQ("05.00000000000", format("%E11S", tp, tz)); - EXPECT_EQ("05.000000000000", format("%E12S", tp, tz)); - EXPECT_EQ("05.0000000000000", format("%E13S", tp, tz)); - EXPECT_EQ("05.00000000000000", format("%E14S", tp, tz)); - EXPECT_EQ("05.000000000000000", format("%E15S", tp, tz)); - - // With subseconds. - tp += chrono::milliseconds(6) + chrono::microseconds(7) + - chrono::nanoseconds(8); - EXPECT_EQ("05.006007008", format("%E*S", tp, tz)); - EXPECT_EQ("05", format("%E0S", tp, tz)); - EXPECT_EQ("05.0", format("%E1S", tp, tz)); - EXPECT_EQ("05.00", format("%E2S", tp, tz)); - EXPECT_EQ("05.006", format("%E3S", tp, tz)); - EXPECT_EQ("05.0060", format("%E4S", tp, tz)); - EXPECT_EQ("05.00600", format("%E5S", tp, tz)); - EXPECT_EQ("05.006007", format("%E6S", tp, tz)); - EXPECT_EQ("05.0060070", format("%E7S", tp, tz)); - EXPECT_EQ("05.00600700", format("%E8S", tp, tz)); - EXPECT_EQ("05.006007008", format("%E9S", tp, tz)); - EXPECT_EQ("05.0060070080", format("%E10S", tp, tz)); - EXPECT_EQ("05.00600700800", format("%E11S", tp, tz)); - EXPECT_EQ("05.006007008000", format("%E12S", tp, tz)); - EXPECT_EQ("05.0060070080000", format("%E13S", tp, tz)); - EXPECT_EQ("05.00600700800000", format("%E14S", tp, tz)); - EXPECT_EQ("05.006007008000000", format("%E15S", tp, tz)); - - // Times before the Unix epoch. - tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1); - EXPECT_EQ("1969-12-31 23:59:59.999999", - format("%Y-%m-%d %H:%M:%E*S", tp, tz)); - - // Here is a "%E*S" case we got wrong for a while. While the first - // instant below is correctly rendered as "...:07.333304", the second - // one used to appear as "...:07.33330499999999999". - tp = chrono::system_clock::from_time_t(0) + - chrono::microseconds(1395024427333304); - EXPECT_EQ("2014-03-17 02:47:07.333304", - format("%Y-%m-%d %H:%M:%E*S", tp, tz)); - tp += chrono::microseconds(1); - EXPECT_EQ("2014-03-17 02:47:07.333305", - format("%Y-%m-%d %H:%M:%E*S", tp, tz)); -} - -TEST(Format, ExtendedSubeconds) { - const time_zone tz = utc_time_zone(); - - // No subseconds. - time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); - tp += chrono::seconds(5); - EXPECT_EQ("0", format("%E*f", tp, tz)); - EXPECT_EQ("", format("%E0f", tp, tz)); - EXPECT_EQ("0", format("%E1f", tp, tz)); - EXPECT_EQ("00", format("%E2f", tp, tz)); - EXPECT_EQ("000", format("%E3f", tp, tz)); - EXPECT_EQ("0000", format("%E4f", tp, tz)); - EXPECT_EQ("00000", format("%E5f", tp, tz)); - EXPECT_EQ("000000", format("%E6f", tp, tz)); - EXPECT_EQ("0000000", format("%E7f", tp, tz)); - EXPECT_EQ("00000000", format("%E8f", tp, tz)); - EXPECT_EQ("000000000", format("%E9f", tp, tz)); - EXPECT_EQ("0000000000", format("%E10f", tp, tz)); - EXPECT_EQ("00000000000", format("%E11f", tp, tz)); - EXPECT_EQ("000000000000", format("%E12f", tp, tz)); - EXPECT_EQ("0000000000000", format("%E13f", tp, tz)); - EXPECT_EQ("00000000000000", format("%E14f", tp, tz)); - EXPECT_EQ("000000000000000", format("%E15f", tp, tz)); - - // With subseconds. - tp += chrono::milliseconds(6) + chrono::microseconds(7) + - chrono::nanoseconds(8); - EXPECT_EQ("006007008", format("%E*f", tp, tz)); - EXPECT_EQ("", format("%E0f", tp, tz)); - EXPECT_EQ("0", format("%E1f", tp, tz)); - EXPECT_EQ("00", format("%E2f", tp, tz)); - EXPECT_EQ("006", format("%E3f", tp, tz)); - EXPECT_EQ("0060", format("%E4f", tp, tz)); - EXPECT_EQ("00600", format("%E5f", tp, tz)); - EXPECT_EQ("006007", format("%E6f", tp, tz)); - EXPECT_EQ("0060070", format("%E7f", tp, tz)); - EXPECT_EQ("00600700", format("%E8f", tp, tz)); - EXPECT_EQ("006007008", format("%E9f", tp, tz)); - EXPECT_EQ("0060070080", format("%E10f", tp, tz)); - EXPECT_EQ("00600700800", format("%E11f", tp, tz)); - EXPECT_EQ("006007008000", format("%E12f", tp, tz)); - EXPECT_EQ("0060070080000", format("%E13f", tp, tz)); - EXPECT_EQ("00600700800000", format("%E14f", tp, tz)); - EXPECT_EQ("006007008000000", format("%E15f", tp, tz)); - - // Times before the Unix epoch. - tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1); - EXPECT_EQ("1969-12-31 23:59:59.999999", - format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); - - // Here is a "%E*S" case we got wrong for a while. While the first - // instant below is correctly rendered as "...:07.333304", the second - // one used to appear as "...:07.33330499999999999". - tp = chrono::system_clock::from_time_t(0) + - chrono::microseconds(1395024427333304); - EXPECT_EQ("2014-03-17 02:47:07.333304", - format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); - tp += chrono::microseconds(1); - EXPECT_EQ("2014-03-17 02:47:07.333305", - format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); -} - -TEST(Format, CompareExtendSecondsVsSubseconds) { - const time_zone tz = utc_time_zone(); - - // This test case illustrates the differences/similarities between: - // fmt_A: %E<prec>S - // fmt_B: %S.%E<prec>f - auto fmt_A = [](const std::string& prec) { return "%E" + prec + "S"; }; - auto fmt_B = [](const std::string& prec) { return "%S.%E" + prec + "f"; }; - - // No subseconds: - time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); - tp += chrono::seconds(5); - // ... %E*S and %S.%E*f are different. - EXPECT_EQ("05", format(fmt_A("*"), tp, tz)); - EXPECT_EQ("05.0", format(fmt_B("*"), tp, tz)); - // ... %E0S and %S.%E0f are different. - EXPECT_EQ("05", format(fmt_A("0"), tp, tz)); - EXPECT_EQ("05.", format(fmt_B("0"), tp, tz)); - // ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15]. - for (int prec = 1; prec <= 15; ++prec) { - const std::string a = format(fmt_A(std::to_string(prec)), tp, tz); - const std::string b = format(fmt_B(std::to_string(prec)), tp, tz); - EXPECT_EQ(a, b) << "prec=" << prec; - } - - // With subseconds: - // ... %E*S and %S.%E*f are the same. - tp += chrono::milliseconds(6) + chrono::microseconds(7) + - chrono::nanoseconds(8); - EXPECT_EQ("05.006007008", format(fmt_A("*"), tp, tz)); - EXPECT_EQ("05.006007008", format(fmt_B("*"), tp, tz)); - // ... %E0S and %S.%E0f are different. - EXPECT_EQ("05", format(fmt_A("0"), tp, tz)); - EXPECT_EQ("05.", format(fmt_B("0"), tp, tz)); - // ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15]. - for (int prec = 1; prec <= 15; ++prec) { - const std::string a = format(fmt_A(std::to_string(prec)), tp, tz); - const std::string b = format(fmt_B(std::to_string(prec)), tp, tz); - EXPECT_EQ(a, b) << "prec=" << prec; - } -} - -TEST(Format, ExtendedOffset) { - const auto tp = chrono::system_clock::from_time_t(0); - - auto tz = fixed_time_zone(cctz::seconds::zero()); - TestFormatSpecifier(tp, tz, "%z", "+0000"); - TestFormatSpecifier(tp, tz, "%:z", "+00:00"); - TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); - - tz = fixed_time_zone(chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%z", "+0000"); - TestFormatSpecifier(tp, tz, "%:z", "+00:00"); - TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); - - tz = fixed_time_zone(-chrono::seconds(56)); // NOTE: +00:00 - TestFormatSpecifier(tp, tz, "%z", "+0000"); - TestFormatSpecifier(tp, tz, "%:z", "+00:00"); - TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); - - tz = fixed_time_zone(chrono::minutes(34)); - TestFormatSpecifier(tp, tz, "%z", "+0034"); - TestFormatSpecifier(tp, tz, "%:z", "+00:34"); - TestFormatSpecifier(tp, tz, "%Ez", "+00:34"); - - tz = fixed_time_zone(-chrono::minutes(34)); - TestFormatSpecifier(tp, tz, "%z", "-0034"); - TestFormatSpecifier(tp, tz, "%:z", "-00:34"); - TestFormatSpecifier(tp, tz, "%Ez", "-00:34"); - - tz = fixed_time_zone(chrono::minutes(34) + chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%z", "+0034"); - TestFormatSpecifier(tp, tz, "%:z", "+00:34"); - TestFormatSpecifier(tp, tz, "%Ez", "+00:34"); - - tz = fixed_time_zone(-chrono::minutes(34) - chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%z", "-0034"); - TestFormatSpecifier(tp, tz, "%:z", "-00:34"); - TestFormatSpecifier(tp, tz, "%Ez", "-00:34"); - - tz = fixed_time_zone(chrono::hours(12)); - TestFormatSpecifier(tp, tz, "%z", "+1200"); - TestFormatSpecifier(tp, tz, "%:z", "+12:00"); - TestFormatSpecifier(tp, tz, "%Ez", "+12:00"); - - tz = fixed_time_zone(-chrono::hours(12)); - TestFormatSpecifier(tp, tz, "%z", "-1200"); - TestFormatSpecifier(tp, tz, "%:z", "-12:00"); - TestFormatSpecifier(tp, tz, "%Ez", "-12:00"); - - tz = fixed_time_zone(chrono::hours(12) + chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%z", "+1200"); - TestFormatSpecifier(tp, tz, "%:z", "+12:00"); - TestFormatSpecifier(tp, tz, "%Ez", "+12:00"); - - tz = fixed_time_zone(-chrono::hours(12) - chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%z", "-1200"); - TestFormatSpecifier(tp, tz, "%:z", "-12:00"); - TestFormatSpecifier(tp, tz, "%Ez", "-12:00"); - - tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34)); - TestFormatSpecifier(tp, tz, "%z", "+1234"); - TestFormatSpecifier(tp, tz, "%:z", "+12:34"); - TestFormatSpecifier(tp, tz, "%Ez", "+12:34"); - - tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34)); - TestFormatSpecifier(tp, tz, "%z", "-1234"); - TestFormatSpecifier(tp, tz, "%:z", "-12:34"); - TestFormatSpecifier(tp, tz, "%Ez", "-12:34"); - - tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34) + - chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%z", "+1234"); - TestFormatSpecifier(tp, tz, "%:z", "+12:34"); - TestFormatSpecifier(tp, tz, "%Ez", "+12:34"); - - tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34) - - chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%z", "-1234"); - TestFormatSpecifier(tp, tz, "%:z", "-12:34"); - TestFormatSpecifier(tp, tz, "%Ez", "-12:34"); -} - -TEST(Format, ExtendedSecondOffset) { - const auto tp = chrono::system_clock::from_time_t(0); - - auto tz = fixed_time_zone(cctz::seconds::zero()); - TestFormatSpecifier(tp, tz, "%E*z", "+00:00:00"); - TestFormatSpecifier(tp, tz, "%::z", "+00:00:00"); - TestFormatSpecifier(tp, tz, "%:::z", "+00"); - - tz = fixed_time_zone(chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%E*z", "+00:00:56"); - TestFormatSpecifier(tp, tz, "%::z", "+00:00:56"); - TestFormatSpecifier(tp, tz, "%:::z", "+00:00:56"); - - tz = fixed_time_zone(-chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%E*z", "-00:00:56"); - TestFormatSpecifier(tp, tz, "%::z", "-00:00:56"); - TestFormatSpecifier(tp, tz, "%:::z", "-00:00:56"); - - tz = fixed_time_zone(chrono::minutes(34)); - TestFormatSpecifier(tp, tz, "%E*z", "+00:34:00"); - TestFormatSpecifier(tp, tz, "%::z", "+00:34:00"); - TestFormatSpecifier(tp, tz, "%:::z", "+00:34"); - - tz = fixed_time_zone(-chrono::minutes(34)); - TestFormatSpecifier(tp, tz, "%E*z", "-00:34:00"); - TestFormatSpecifier(tp, tz, "%::z", "-00:34:00"); - TestFormatSpecifier(tp, tz, "%:::z", "-00:34"); - - tz = fixed_time_zone(chrono::minutes(34) + chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%E*z", "+00:34:56"); - TestFormatSpecifier(tp, tz, "%::z", "+00:34:56"); - TestFormatSpecifier(tp, tz, "%:::z", "+00:34:56"); - - tz = fixed_time_zone(-chrono::minutes(34) - chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%E*z", "-00:34:56"); - TestFormatSpecifier(tp, tz, "%::z", "-00:34:56"); - TestFormatSpecifier(tp, tz, "%:::z", "-00:34:56"); - - tz = fixed_time_zone(chrono::hours(12)); - TestFormatSpecifier(tp, tz, "%E*z", "+12:00:00"); - TestFormatSpecifier(tp, tz, "%::z", "+12:00:00"); - TestFormatSpecifier(tp, tz, "%:::z", "+12"); - - tz = fixed_time_zone(-chrono::hours(12)); - TestFormatSpecifier(tp, tz, "%E*z", "-12:00:00"); - TestFormatSpecifier(tp, tz, "%::z", "-12:00:00"); - TestFormatSpecifier(tp, tz, "%:::z", "-12"); - - tz = fixed_time_zone(chrono::hours(12) + chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%E*z", "+12:00:56"); - TestFormatSpecifier(tp, tz, "%::z", "+12:00:56"); - TestFormatSpecifier(tp, tz, "%:::z", "+12:00:56"); - - tz = fixed_time_zone(-chrono::hours(12) - chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%E*z", "-12:00:56"); - TestFormatSpecifier(tp, tz, "%::z", "-12:00:56"); - TestFormatSpecifier(tp, tz, "%:::z", "-12:00:56"); - - tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34)); - TestFormatSpecifier(tp, tz, "%E*z", "+12:34:00"); - TestFormatSpecifier(tp, tz, "%::z", "+12:34:00"); - TestFormatSpecifier(tp, tz, "%:::z", "+12:34"); - - tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34)); - TestFormatSpecifier(tp, tz, "%E*z", "-12:34:00"); - TestFormatSpecifier(tp, tz, "%::z", "-12:34:00"); - TestFormatSpecifier(tp, tz, "%:::z", "-12:34"); - - tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34) + - chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%E*z", "+12:34:56"); - TestFormatSpecifier(tp, tz, "%::z", "+12:34:56"); - TestFormatSpecifier(tp, tz, "%:::z", "+12:34:56"); - - tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34) - - chrono::seconds(56)); - TestFormatSpecifier(tp, tz, "%E*z", "-12:34:56"); - TestFormatSpecifier(tp, tz, "%::z", "-12:34:56"); - TestFormatSpecifier(tp, tz, "%:::z", "-12:34:56"); -} - -TEST(Format, ExtendedYears) { - const time_zone utc = utc_time_zone(); - const char e4y_fmt[] = "%E4Y%m%d"; // no separators - - // %E4Y zero-pads the year to produce at least 4 chars, including the sign. - auto tp = convert(civil_second(-999, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-9991127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(-99, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-0991127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(-9, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-0091127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(-1, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-0011127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(0, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("00001127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(1, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("00011127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(9, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("00091127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(99, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("00991127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(999, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("09991127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(9999, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("99991127", format(e4y_fmt, tp, utc)); - - // When the year is outside [-999:9999], more than 4 chars are produced. - tp = convert(civil_second(-1000, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-10001127", format(e4y_fmt, tp, utc)); - tp = convert(civil_second(10000, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("100001127", format(e4y_fmt, tp, utc)); -} - -TEST(Format, RFC3339Format) { - time_zone tz; - EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); - - time_point<chrono::nanoseconds> tp = - convert(civil_second(1977, 6, 28, 9, 8, 7), tz); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); - - tp += chrono::milliseconds(100); - EXPECT_EQ("1977-06-28T09:08:07.1-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); - - tp += chrono::milliseconds(20); - EXPECT_EQ("1977-06-28T09:08:07.12-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); - - tp += chrono::milliseconds(3); - EXPECT_EQ("1977-06-28T09:08:07.123-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); - - tp += chrono::microseconds(400); - EXPECT_EQ("1977-06-28T09:08:07.1234-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); - - tp += chrono::microseconds(50); - EXPECT_EQ("1977-06-28T09:08:07.12345-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); - - tp += chrono::microseconds(6); - EXPECT_EQ("1977-06-28T09:08:07.123456-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); - - tp += chrono::nanoseconds(700); - EXPECT_EQ("1977-06-28T09:08:07.1234567-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); - - tp += chrono::nanoseconds(80); - EXPECT_EQ("1977-06-28T09:08:07.12345678-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); - - tp += chrono::nanoseconds(9); - EXPECT_EQ("1977-06-28T09:08:07.123456789-07:00", - format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); -} - -TEST(Format, RFC1123Format) { // locale specific - time_zone tz; - EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); - - auto tp = convert(civil_second(1977, 6, 28, 9, 8, 7), tz); - EXPECT_EQ("Tue, 28 Jun 1977 09:08:07 -0700", format(RFC1123_full, tp, tz)); - EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz)); -} - -TEST(Format, Week) { - const time_zone utc = utc_time_zone(); - - auto tp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc); - EXPECT_EQ("2017-01-7", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2017-00-0", format("%Y-%W-%w", tp, utc)); - - tp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc); - EXPECT_EQ("2017-53-7", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2017-52-0", format("%Y-%W-%w", tp, utc)); - - tp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc); - EXPECT_EQ("2018-00-1", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2018-01-1", format("%Y-%W-%w", tp, utc)); - - tp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc); - EXPECT_EQ("2018-52-1", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2018-53-1", format("%Y-%W-%w", tp, utc)); - - tp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc); - EXPECT_EQ("2019-00-2", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2019-00-2", format("%Y-%W-%w", tp, utc)); - - tp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); - EXPECT_EQ("2019-52-2", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2019-52-2", format("%Y-%W-%w", tp, utc)); -} - -// -// Testing parse() -// - -TEST(Parse, TimePointResolution) { - const char kFmt[] = "%H:%M:%E*S"; - const time_zone utc = utc_time_zone(); - - time_point<chrono::nanoseconds> tp_ns; - EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_ns)); - EXPECT_EQ("03:04:05.123456789", format(kFmt, tp_ns, utc)); - EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ns)); - EXPECT_EQ("03:04:05.123456", format(kFmt, tp_ns, utc)); - - time_point<chrono::microseconds> tp_us; - EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_us)); - EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc)); - EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_us)); - EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc)); - EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_us)); - EXPECT_EQ("03:04:05.123", format(kFmt, tp_us, utc)); - - time_point<chrono::milliseconds> tp_ms; - EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ms)); - EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc)); - EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_ms)); - EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc)); - EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_ms)); - EXPECT_EQ("03:04:05", format(kFmt, tp_ms, utc)); - - time_point<chrono::seconds> tp_s; - EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_s)); - EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc)); - EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_s)); - EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc)); - - time_point<chrono::minutes> tp_m; - EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_m)); - EXPECT_EQ("03:04:00", format(kFmt, tp_m, utc)); - - time_point<chrono::hours> tp_h; - EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_h)); - EXPECT_EQ("03:00:00", format(kFmt, tp_h, utc)); -} - -TEST(Parse, TimePointExtendedResolution) { - const char kFmt[] = "%H:%M:%E*S"; - const time_zone utc = utc_time_zone(); - - time_point<cctz::seconds> tp; - detail::femtoseconds fs; - EXPECT_TRUE(detail::parse(kFmt, "12:34:56.123456789012345", utc, &tp, &fs)); - EXPECT_EQ("12:34:56.123456789012345", detail::format(kFmt, tp, fs, utc)); - EXPECT_TRUE(detail::parse(kFmt, "12:34:56.012345678901234", utc, &tp, &fs)); - EXPECT_EQ("12:34:56.012345678901234", detail::format(kFmt, tp, fs, utc)); - EXPECT_TRUE(detail::parse(kFmt, "12:34:56.001234567890123", utc, &tp, &fs)); - EXPECT_EQ("12:34:56.001234567890123", detail::format(kFmt, tp, fs, utc)); - EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000123", utc, &tp, &fs)); - EXPECT_EQ("12:34:56.000000000000123", detail::format(kFmt, tp, fs, utc)); - EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000012", utc, &tp, &fs)); - EXPECT_EQ("12:34:56.000000000000012", detail::format(kFmt, tp, fs, utc)); - EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000001", utc, &tp, &fs)); - EXPECT_EQ("12:34:56.000000000000001", detail::format(kFmt, tp, fs, utc)); -} - -TEST(Parse, Basics) { - time_zone tz = utc_time_zone(); - time_point<chrono::nanoseconds> tp = - chrono::system_clock::from_time_t(1234567890); - - // Simple edge cases. - EXPECT_TRUE(parse("", "", tz, &tp)); - EXPECT_EQ(chrono::system_clock::from_time_t(0), tp); // everything defaulted - EXPECT_TRUE(parse(" ", " ", tz, &tp)); - EXPECT_TRUE(parse(" ", " ", tz, &tp)); - EXPECT_TRUE(parse("x", "x", tz, &tp)); - EXPECT_TRUE(parse("xxx", "xxx", tz, &tp)); - - EXPECT_TRUE( - parse("%Y-%m-%d %H:%M:%S %z", "2013-06-28 19:08:09 -0800", tz, &tp)); - ExpectTime(tp, tz, 2013, 6, 29, 3, 8, 9, 0, false, "UTC"); -} - -TEST(Parse, WithTimeZone) { - time_zone tz; - EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); - time_point<chrono::nanoseconds> tp; - - // We can parse a string without a UTC offset if we supply a timezone. - EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &tp)); - ExpectTime(tp, tz, 2013, 6, 28, 19, 8, 9, -7 * 60 * 60, true, "PDT"); - - // But the timezone is ignored when a UTC offset is present. - EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S %z", "2013-06-28 19:08:09 +0800", - utc_time_zone(), &tp)); - ExpectTime(tp, tz, 2013, 6, 28, 19 - 8 - 7, 8, 9, -7 * 60 * 60, true, "PDT"); - - // Check a skipped time (a Spring DST transition). parse() uses the - // pre-transition offset. - EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-03-13 02:15:00", tz, &tp)); - ExpectTime(tp, tz, 2011, 3, 13, 3, 15, 0, -7 * 60 * 60, true, "PDT"); - - // Check a repeated time (a Fall DST transition). parse() uses the - // pre-transition offset. - EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-11-06 01:15:00", tz, &tp)); - ExpectTime(tp, tz, 2011, 11, 6, 1, 15, 0, -7 * 60 * 60, true, "PDT"); -} - -TEST(Parse, LeapSecond) { - time_zone tz; - EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); - time_point<chrono::nanoseconds> tp; - - // ":59" -> ":59" - EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59-08:00", tz, &tp)); - ExpectTime(tp, tz, 2013, 6, 28, 8, 8, 59, -7 * 60 * 60, true, "PDT"); - - // ":59.5" -> ":59.5" - EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59.5-08:00", tz, &tp)); - ExpectTime(tp, tz, 2013, 6, 28, 8, 8, 59, -7 * 60 * 60, true, "PDT"); - - // ":60" -> ":00" - EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:60-08:00", tz, &tp)); - ExpectTime(tp, tz, 2013, 6, 28, 8, 9, 0, -7 * 60 * 60, true, "PDT"); - - // ":60.5" -> ":00.0" - EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:60.5-08:00", tz, &tp)); - ExpectTime(tp, tz, 2013, 6, 28, 8, 9, 0, -7 * 60 * 60, true, "PDT"); - - // ":61" -> error - EXPECT_FALSE(parse(RFC3339_full, "2013-06-28T07:08:61-08:00", tz, &tp)); -} - -TEST(Parse, ErrorCases) { - const time_zone tz = utc_time_zone(); - auto tp = chrono::system_clock::from_time_t(0); - - // Illegal trailing data. - EXPECT_FALSE(parse("%S", "123", tz, &tp)); - - // Can't parse an illegal format specifier. - EXPECT_FALSE(parse("%Q", "x", tz, &tp)); - - // Fails because of trailing, unparsed data "blah". - EXPECT_FALSE(parse("%m-%d", "2-3 blah", tz, &tp)); - - // Trailing whitespace is allowed. - EXPECT_TRUE(parse("%m-%d", "2-3 ", tz, &tp)); - EXPECT_EQ(2, convert(tp, utc_time_zone()).month()); - EXPECT_EQ(3, convert(tp, utc_time_zone()).day()); - - // Feb 31 requires normalization. - EXPECT_FALSE(parse("%m-%d", "2-31", tz, &tp)); - - // Check that we cannot have spaces in UTC offsets. - EXPECT_TRUE(parse("%z", "-0203", tz, &tp)); - EXPECT_FALSE(parse("%z", "- 2 3", tz, &tp)); - EXPECT_TRUE(parse("%Ez", "-02:03", tz, &tp)); - EXPECT_FALSE(parse("%Ez", "- 2: 3", tz, &tp)); - - // Check that we reject other malformed UTC offsets. - EXPECT_FALSE(parse("%Ez", "+-08:00", tz, &tp)); - EXPECT_FALSE(parse("%Ez", "-+08:00", tz, &tp)); - - // Check that we do not accept "-0" in fields that allow zero. - EXPECT_FALSE(parse("%Y", "-0", tz, &tp)); - EXPECT_FALSE(parse("%E4Y", "-0", tz, &tp)); - EXPECT_FALSE(parse("%H", "-0", tz, &tp)); - EXPECT_FALSE(parse("%M", "-0", tz, &tp)); - EXPECT_FALSE(parse("%S", "-0", tz, &tp)); - EXPECT_FALSE(parse("%z", "+-000", tz, &tp)); - EXPECT_FALSE(parse("%Ez", "+-0:00", tz, &tp)); - EXPECT_FALSE(parse("%z", "-00-0", tz, &tp)); - EXPECT_FALSE(parse("%Ez", "-00:-0", tz, &tp)); -} - -TEST(Parse, PosixConversions) { - time_zone tz = utc_time_zone(); - auto tp = chrono::system_clock::from_time_t(0); - const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz); - - tp = reset; - EXPECT_TRUE(parse("%d", "15", tz, &tp)); - EXPECT_EQ(15, convert(tp, tz).day()); - - // %e is an extension, but is supported internally. - tp = reset; - EXPECT_TRUE(parse("%e", "15", tz, &tp)); - EXPECT_EQ(15, convert(tp, tz).day()); // Equivalent to %d - - tp = reset; - EXPECT_TRUE(parse("%H", "17", tz, &tp)); - EXPECT_EQ(17, convert(tp, tz).hour()); - - tp = reset; - EXPECT_TRUE(parse("%I", "5", tz, &tp)); - EXPECT_EQ(5, convert(tp, tz).hour()); - - // %j is parsed but ignored. - EXPECT_TRUE(parse("%j", "32", tz, &tp)); - - tp = reset; - EXPECT_TRUE(parse("%m", "11", tz, &tp)); - EXPECT_EQ(11, convert(tp, tz).month()); - - tp = reset; - EXPECT_TRUE(parse("%M", "33", tz, &tp)); - EXPECT_EQ(33, convert(tp, tz).minute()); - - tp = reset; - EXPECT_TRUE(parse("%S", "55", tz, &tp)); - EXPECT_EQ(55, convert(tp, tz).second()); - - // %U is parsed but ignored. - EXPECT_TRUE(parse("%U", "15", tz, &tp)); - - // %w is parsed but ignored. - EXPECT_TRUE(parse("%w", "2", tz, &tp)); - - // %W is parsed but ignored. - EXPECT_TRUE(parse("%W", "22", tz, &tp)); - - tp = reset; - EXPECT_TRUE(parse("%y", "04", tz, &tp)); - EXPECT_EQ(2004, convert(tp, tz).year()); - - tp = reset; - EXPECT_TRUE(parse("%Y", "2004", tz, &tp)); - EXPECT_EQ(2004, convert(tp, tz).year()); - - EXPECT_TRUE(parse("%%", "%", tz, &tp)); - -#if defined(__linux__) - // SU/C99/TZ extensions - - // Because we handle each (non-internal) specifier in a separate call - // to strptime(), there is no way to group %C and %y together. So we - // just skip the %C/%y case. -#if 0 - tp = reset; - EXPECT_TRUE(parse("%C %y", "20 04", tz, &tp)); - EXPECT_EQ(2004, convert(tp, tz).year()); -#endif - - tp = reset; - EXPECT_TRUE(parse("%D", "02/03/04", tz, &tp)); - EXPECT_EQ(2, convert(tp, tz).month()); - EXPECT_EQ(3, convert(tp, tz).day()); - EXPECT_EQ(2004, convert(tp, tz).year()); - - EXPECT_TRUE(parse("%n", "\n", tz, &tp)); - - tp = reset; - EXPECT_TRUE(parse("%R", "03:44", tz, &tp)); - EXPECT_EQ(3, convert(tp, tz).hour()); - EXPECT_EQ(44, convert(tp, tz).minute()); - - EXPECT_TRUE(parse("%t", "\t\v\f\n\r ", tz, &tp)); - - tp = reset; - EXPECT_TRUE(parse("%T", "03:44:55", tz, &tp)); - EXPECT_EQ(3, convert(tp, tz).hour()); - EXPECT_EQ(44, convert(tp, tz).minute()); - EXPECT_EQ(55, convert(tp, tz).second()); - - tp = reset; - EXPECT_TRUE(parse("%s", "1234567890", tz, &tp)); - EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp); - - // %s conversion, like %z/%Ez, pays no heed to the optional zone. - time_zone lax; - EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax)); - tp = reset; - EXPECT_TRUE(parse("%s", "1234567890", lax, &tp)); - EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp); - - // This is most important when the time has the same YMDhms - // breakdown in the zone as some other time. For example, ... - // 1414917000 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PDT) - // 1414920600 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PST) - tp = reset; - EXPECT_TRUE(parse("%s", "1414917000", lax, &tp)); - EXPECT_EQ(chrono::system_clock::from_time_t(1414917000), tp); - tp = reset; - EXPECT_TRUE(parse("%s", "1414920600", lax, &tp)); - EXPECT_EQ(chrono::system_clock::from_time_t(1414920600), tp); -#endif -} - -TEST(Parse, LocaleSpecific) { - time_zone tz = utc_time_zone(); - auto tp = chrono::system_clock::from_time_t(0); - const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz); - - // %a is parsed but ignored. - EXPECT_TRUE(parse("%a", "Mon", tz, &tp)); - - // %A is parsed but ignored. - EXPECT_TRUE(parse("%A", "Monday", tz, &tp)); - - tp = reset; - EXPECT_TRUE(parse("%b", "Feb", tz, &tp)); - EXPECT_EQ(2, convert(tp, tz).month()); - - tp = reset; - EXPECT_TRUE(parse("%B", "February", tz, &tp)); - EXPECT_EQ(2, convert(tp, tz).month()); - - // %p is parsed but ignored if it's alone. But it's used with %I. - EXPECT_TRUE(parse("%p", "AM", tz, &tp)); - tp = reset; - EXPECT_TRUE(parse("%I %p", "5 PM", tz, &tp)); - EXPECT_EQ(17, convert(tp, tz).hour()); - - tp = reset; - EXPECT_TRUE(parse("%x", "02/03/04", tz, &tp)); - if (convert(tp, tz).month() == 2) { - EXPECT_EQ(3, convert(tp, tz).day()); - } else { - EXPECT_EQ(2, convert(tp, tz).day()); - EXPECT_EQ(3, convert(tp, tz).month()); - } - EXPECT_EQ(2004, convert(tp, tz).year()); - - tp = reset; - EXPECT_TRUE(parse("%X", "15:44:55", tz, &tp)); - EXPECT_EQ(15, convert(tp, tz).hour()); - EXPECT_EQ(44, convert(tp, tz).minute()); - EXPECT_EQ(55, convert(tp, tz).second()); - -#if defined(__linux__) - // SU/C99/TZ extensions - - tp = reset; - EXPECT_TRUE(parse("%h", "Feb", tz, &tp)); - EXPECT_EQ(2, convert(tp, tz).month()); // Equivalent to %b - - tp = reset; - EXPECT_TRUE(parse("%l %p", "5 PM", tz, &tp)); - EXPECT_EQ(17, convert(tp, tz).hour()); - - tp = reset; - EXPECT_TRUE(parse("%r", "03:44:55 PM", tz, &tp)); - EXPECT_EQ(15, convert(tp, tz).hour()); - EXPECT_EQ(44, convert(tp, tz).minute()); - EXPECT_EQ(55, convert(tp, tz).second()); - - tp = reset; - EXPECT_TRUE(parse("%Ec", "Tue Nov 19 05:06:07 2013", tz, &tp)); - EXPECT_EQ(convert(civil_second(2013, 11, 19, 5, 6, 7), tz), tp); - - // Modified conversion specifiers %E_ - - tp = reset; - EXPECT_TRUE(parse("%Ex", "02/03/04", tz, &tp)); - EXPECT_EQ(2, convert(tp, tz).month()); - EXPECT_EQ(3, convert(tp, tz).day()); - EXPECT_EQ(2004, convert(tp, tz).year()); - - tp = reset; - EXPECT_TRUE(parse("%EX", "15:44:55", tz, &tp)); - EXPECT_EQ(15, convert(tp, tz).hour()); - EXPECT_EQ(44, convert(tp, tz).minute()); - EXPECT_EQ(55, convert(tp, tz).second()); - - // %Ey, the year offset from %EC, doesn't really make sense alone as there - // is no way to represent it in tm_year (%EC is not simply the century). - // Yet, because we handle each (non-internal) specifier in a separate call - // to strptime(), there is no way to group %EC and %Ey either. So we just - // skip the %EC and %Ey cases. - - tp = reset; - EXPECT_TRUE(parse("%EY", "2004", tz, &tp)); - EXPECT_EQ(2004, convert(tp, tz).year()); - - // Modified conversion specifiers %O_ - - tp = reset; - EXPECT_TRUE(parse("%Od", "15", tz, &tp)); - EXPECT_EQ(15, convert(tp, tz).day()); - - tp = reset; - EXPECT_TRUE(parse("%Oe", "15", tz, &tp)); - EXPECT_EQ(15, convert(tp, tz).day()); // Equivalent to %d - - tp = reset; - EXPECT_TRUE(parse("%OH", "17", tz, &tp)); - EXPECT_EQ(17, convert(tp, tz).hour()); - - tp = reset; - EXPECT_TRUE(parse("%OI", "5", tz, &tp)); - EXPECT_EQ(5, convert(tp, tz).hour()); - - tp = reset; - EXPECT_TRUE(parse("%Om", "11", tz, &tp)); - EXPECT_EQ(11, convert(tp, tz).month()); - - tp = reset; - EXPECT_TRUE(parse("%OM", "33", tz, &tp)); - EXPECT_EQ(33, convert(tp, tz).minute()); - - tp = reset; - EXPECT_TRUE(parse("%OS", "55", tz, &tp)); - EXPECT_EQ(55, convert(tp, tz).second()); - - // %OU is parsed but ignored. - EXPECT_TRUE(parse("%OU", "15", tz, &tp)); - - // %Ow is parsed but ignored. - EXPECT_TRUE(parse("%Ow", "2", tz, &tp)); - - // %OW is parsed but ignored. - EXPECT_TRUE(parse("%OW", "22", tz, &tp)); - - tp = reset; - EXPECT_TRUE(parse("%Oy", "04", tz, &tp)); - EXPECT_EQ(2004, convert(tp, tz).year()); -#endif -} - -TEST(Parse, ExtendedSeconds) { - const time_zone tz = utc_time_zone(); - const time_point<chrono::nanoseconds> unix_epoch = - chrono::system_clock::from_time_t(0); - - // All %E<prec>S cases are treated the same as %E*S on input. - auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", - "8", "9", "10", "11", "12", "13", "14", "15"}; - for (const std::string& prec : precisions) { - const std::string fmt = "%E" + prec + "S"; - SCOPED_TRACE(fmt); - time_point<chrono::nanoseconds> tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "5", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "05", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "05.0", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "05.00", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "05.6", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "05.60", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "05.600", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "05.67", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "05.670", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "05.678", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(678), tp); - } - - // Here is a "%E*S" case we got wrong for a while. The fractional - // part of the first instant is less than 2^31 and was correctly - // parsed, while the second (and any subsecond field >=2^31) failed. - time_point<chrono::nanoseconds> tp = unix_epoch; - EXPECT_TRUE(parse("%E*S", "0.2147483647", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); - tp = unix_epoch; - EXPECT_TRUE(parse("%E*S", "0.2147483648", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); - - // We should also be able to specify long strings of digits far - // beyond the current resolution and have them convert the same way. - tp = unix_epoch; - EXPECT_TRUE(parse( - "%E*S", "0.214748364801234567890123456789012345678901234567890123456789", - tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); -} - -TEST(Parse, ExtendedSecondsScan) { - const time_zone tz = utc_time_zone(); - time_point<chrono::nanoseconds> tp; - for (int ms = 0; ms < 1000; ms += 111) { - for (int us = 0; us < 1000; us += 27) { - const int micros = ms * 1000 + us; - for (int ns = 0; ns < 1000; ns += 9) { - const auto expected = chrono::system_clock::from_time_t(0) + - chrono::nanoseconds(micros * 1000 + ns); - std::ostringstream oss; - oss << "0." << std::setfill('0') << std::setw(3); - oss << ms << std::setw(3) << us << std::setw(3) << ns; - const std::string input = oss.str(); - EXPECT_TRUE(parse("%E*S", input, tz, &tp)); - EXPECT_EQ(expected, tp) << input; - } - } - } -} - -TEST(Parse, ExtendedSubeconds) { - const time_zone tz = utc_time_zone(); - const time_point<chrono::nanoseconds> unix_epoch = - chrono::system_clock::from_time_t(0); - - // All %E<prec>f cases are treated the same as %E*f on input. - auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", - "8", "9", "10", "11", "12", "13", "14", "15"}; - for (const std::string& prec : precisions) { - const std::string fmt = "%E" + prec + "f"; - SCOPED_TRACE(fmt); - time_point<chrono::nanoseconds> tp = unix_epoch - chrono::seconds(1); - EXPECT_TRUE(parse(fmt, "", tz, &tp)); - EXPECT_EQ(unix_epoch, tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "6", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "60", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "600", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "67", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "670", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "678", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::milliseconds(678), tp); - tp = unix_epoch; - EXPECT_TRUE(parse(fmt, "6789", tz, &tp)); - EXPECT_EQ( - unix_epoch + chrono::milliseconds(678) + chrono::microseconds(900), tp); - } - - // Here is a "%E*f" case we got wrong for a while. The fractional - // part of the first instant is less than 2^31 and was correctly - // parsed, while the second (and any subsecond field >=2^31) failed. - time_point<chrono::nanoseconds> tp = unix_epoch; - EXPECT_TRUE(parse("%E*f", "2147483647", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); - tp = unix_epoch; - EXPECT_TRUE(parse("%E*f", "2147483648", tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); - - // We should also be able to specify long strings of digits far - // beyond the current resolution and have them convert the same way. - tp = unix_epoch; - EXPECT_TRUE(parse( - "%E*f", "214748364801234567890123456789012345678901234567890123456789", - tz, &tp)); - EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); -} - -TEST(Parse, ExtendedSubecondsScan) { - time_point<chrono::nanoseconds> tp; - const time_zone tz = utc_time_zone(); - for (int ms = 0; ms < 1000; ms += 111) { - for (int us = 0; us < 1000; us += 27) { - const int micros = ms * 1000 + us; - for (int ns = 0; ns < 1000; ns += 9) { - std::ostringstream oss; - oss << std::setfill('0') << std::setw(3) << ms; - oss << std::setw(3) << us << std::setw(3) << ns; - const std::string nanos = oss.str(); - const auto expected = chrono::system_clock::from_time_t(0) + - chrono::nanoseconds(micros * 1000 + ns); - for (int ps = 0; ps < 1000; ps += 250) { - std::ostringstream ps_oss; - oss << std::setfill('0') << std::setw(3) << ps; - const std::string input = nanos + ps_oss.str() + "999"; - EXPECT_TRUE(parse("%E*f", input, tz, &tp)); - EXPECT_EQ(expected + chrono::nanoseconds(ps) / 1000, tp) << input; - } - } - } - } -} - -TEST(Parse, ExtendedOffset) { - const time_zone utc = utc_time_zone(); - time_point<cctz::seconds> tp; - - EXPECT_TRUE(parse("%Ez", "+00:00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%Ez", "-12:34", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); - EXPECT_TRUE(parse("%Ez", "+12:34", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); - EXPECT_FALSE(parse("%Ez", "-12:3", utc, &tp)); - - for (auto fmt : {"%Ez", "%z"}) { - EXPECT_TRUE(parse(fmt, "+0000", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "-1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "+1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); - EXPECT_FALSE(parse(fmt, "-123", utc, &tp)); - - EXPECT_TRUE(parse(fmt, "+00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "-12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "+12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp); - EXPECT_FALSE(parse(fmt, "-1", utc, &tp)); - } -} - -TEST(Parse, ExtendedSecondOffset) { - const time_zone utc = utc_time_zone(); - time_point<cctz::seconds> tp; - - for (auto fmt : {"%Ez", "%E*z", "%:z", "%::z", "%:::z"}) { - EXPECT_TRUE(parse(fmt, "+00:00:00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "-12:34:56", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); - EXPECT_TRUE(parse(fmt, "+12:34:56", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); - EXPECT_FALSE(parse(fmt, "-12:34:5", utc, &tp)); - - EXPECT_TRUE(parse(fmt, "+000000", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "-123456", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); - EXPECT_TRUE(parse(fmt, "+123456", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); - EXPECT_FALSE(parse(fmt, "-12345", utc, &tp)); - - EXPECT_TRUE(parse(fmt, "+00:00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "-12:34", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "+12:34", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); - EXPECT_FALSE(parse(fmt, "-12:3", utc, &tp)); - - EXPECT_TRUE(parse(fmt, "+0000", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "-1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "+1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); - EXPECT_FALSE(parse(fmt, "-123", utc, &tp)); - - EXPECT_TRUE(parse(fmt, "+00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "-12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp); - EXPECT_TRUE(parse(fmt, "+12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp); - EXPECT_FALSE(parse(fmt, "-1", utc, &tp)); - } -} - -TEST(Parse, ExtendedYears) { - const time_zone utc = utc_time_zone(); - const char e4y_fmt[] = "%E4Y%m%d"; // no separators - time_point<cctz::seconds> tp; - - // %E4Y consumes exactly four chars, including any sign. - EXPECT_TRUE(parse(e4y_fmt, "-9991127", utc, &tp)); - EXPECT_EQ(convert(civil_second(-999, 11, 27, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(e4y_fmt, "-0991127", utc, &tp)); - EXPECT_EQ(convert(civil_second(-99, 11, 27, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(e4y_fmt, "-0091127", utc, &tp)); - EXPECT_EQ(convert(civil_second(-9, 11, 27, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(e4y_fmt, "-0011127", utc, &tp)); - EXPECT_EQ(convert(civil_second(-1, 11, 27, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(e4y_fmt, "00001127", utc, &tp)); - EXPECT_EQ(convert(civil_second(0, 11, 27, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(e4y_fmt, "00011127", utc, &tp)); - EXPECT_EQ(convert(civil_second(1, 11, 27, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(e4y_fmt, "00091127", utc, &tp)); - EXPECT_EQ(convert(civil_second(9, 11, 27, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(e4y_fmt, "00991127", utc, &tp)); - EXPECT_EQ(convert(civil_second(99, 11, 27, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(e4y_fmt, "09991127", utc, &tp)); - EXPECT_EQ(convert(civil_second(999, 11, 27, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse(e4y_fmt, "99991127", utc, &tp)); - EXPECT_EQ(convert(civil_second(9999, 11, 27, 0, 0, 0), utc), tp); - - // When the year is outside [-999:9999], the parse fails. - EXPECT_FALSE(parse(e4y_fmt, "-10001127", utc, &tp)); - EXPECT_FALSE(parse(e4y_fmt, "100001127", utc, &tp)); -} - -TEST(Parse, RFC3339Format) { - const time_zone tz = utc_time_zone(); - time_point<chrono::nanoseconds> tp; - EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp)); - ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC"); - - // Check that %ET also accepts "t". - time_point<chrono::nanoseconds> tp2; - EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12t20:21:00+00:00", tz, &tp2)); - EXPECT_EQ(tp, tp2); - - // Check that %Ez also accepts "Z" as a synonym for "+00:00". - time_point<chrono::nanoseconds> tp3; - EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp3)); - EXPECT_EQ(tp, tp3); - - // Check that %Ez also accepts "z" as a synonym for "+00:00". - time_point<chrono::nanoseconds> tp4; - EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00z", tz, &tp4)); - EXPECT_EQ(tp, tp4); -} - -TEST(Parse, Week) { - const time_zone utc = utc_time_zone(); - time_point<cctz::seconds> tp; - - auto exp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc); - EXPECT_TRUE(parse("%Y-%U-%u", "2017-01-7", utc, &tp)); - EXPECT_EQ(exp, tp); - EXPECT_TRUE(parse("%Y-%W-%w", "2017-00-0", utc, &tp)); - EXPECT_EQ(exp, tp); - - exp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc); - EXPECT_TRUE(parse("%Y-%U-%u", "2017-53-7", utc, &tp)); - EXPECT_EQ(exp, tp); - EXPECT_TRUE(parse("%Y-%W-%w", "2017-52-0", utc, &tp)); - EXPECT_EQ(exp, tp); - - exp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc); - EXPECT_TRUE(parse("%Y-%U-%u", "2018-00-1", utc, &tp)); - EXPECT_EQ(exp, tp); - EXPECT_TRUE(parse("%Y-%W-%w", "2018-01-1", utc, &tp)); - EXPECT_EQ(exp, tp); - - exp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc); - EXPECT_TRUE(parse("%Y-%U-%u", "2018-52-1", utc, &tp)); - EXPECT_EQ(exp, tp); - EXPECT_TRUE(parse("%Y-%W-%w", "2018-53-1", utc, &tp)); - EXPECT_EQ(exp, tp); - - exp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc); - EXPECT_TRUE(parse("%Y-%U-%u", "2019-00-2", utc, &tp)); - EXPECT_EQ(exp, tp); - EXPECT_TRUE(parse("%Y-%W-%w", "2019-00-2", utc, &tp)); - EXPECT_EQ(exp, tp); - - exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); - EXPECT_TRUE(parse("%Y-%U-%u", "2019-52-2", utc, &tp)); - EXPECT_EQ(exp, tp); - EXPECT_TRUE(parse("%Y-%W-%w", "2019-52-2", utc, &tp)); - EXPECT_EQ(exp, tp); -} - -TEST(Parse, WeekYearShift) { - // %U/%W conversions with week values in {0, 52, 53} can slip - // into the previous/following calendar years. - const time_zone utc = utc_time_zone(); - time_point<cctz::seconds> tp; - - auto exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); - EXPECT_TRUE(parse("%Y-%U-%u", "2020-00-2", utc, &tp)); - EXPECT_EQ(exp, tp); - EXPECT_TRUE(parse("%Y-%W-%w", "2020-00-2", utc, &tp)); - EXPECT_EQ(exp, tp); - - exp = convert(civil_second(2021, 1, 1, 0, 0, 0), utc); - EXPECT_TRUE(parse("%Y-%U-%u", "2020-52-5", utc, &tp)); - EXPECT_EQ(exp, tp); - EXPECT_TRUE(parse("%Y-%W-%w", "2020-52-5", utc, &tp)); - EXPECT_EQ(exp, tp); - - // Slipping into the previous/following calendar years should fail when - // we're already at the extremes. - EXPECT_FALSE(parse("%Y-%U-%u", "-9223372036854775808-0-7", utc, &tp)); - EXPECT_FALSE(parse("%Y-%U-%u", "9223372036854775807-53-7", utc, &tp)); -} - -TEST(Parse, MaxRange) { - const time_zone utc = utc_time_zone(); - time_point<cctz::seconds> tp; - - // tests the upper limit using +00:00 offset - EXPECT_TRUE( - parse(RFC3339_sec, "292277026596-12-04T15:30:07+00:00", utc, &tp)); - EXPECT_EQ(tp, time_point<cctz::seconds>::max()); - EXPECT_FALSE( - parse(RFC3339_sec, "292277026596-12-04T15:30:08+00:00", utc, &tp)); - - // tests the upper limit using -01:00 offset - EXPECT_TRUE( - parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp)); - EXPECT_EQ(tp, time_point<cctz::seconds>::max()); - EXPECT_FALSE( - parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp)); - - // tests the lower limit using +00:00 offset - EXPECT_TRUE( - parse(RFC3339_sec, "-292277022657-01-27T08:29:52+00:00", utc, &tp)); - EXPECT_EQ(tp, time_point<cctz::seconds>::min()); - EXPECT_FALSE( - parse(RFC3339_sec, "-292277022657-01-27T08:29:51+00:00", utc, &tp)); - - // tests the lower limit using +01:00 offset - EXPECT_TRUE( - parse(RFC3339_sec, "-292277022657-01-27T09:29:52+01:00", utc, &tp)); - EXPECT_EQ(tp, time_point<cctz::seconds>::min()); - EXPECT_FALSE( - parse(RFC3339_sec, "-292277022657-01-27T08:29:51+01:00", utc, &tp)); - - // tests max/min civil-second overflow - EXPECT_FALSE(parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", - utc, &tp)); - EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01", - utc, &tp)); - - // TODO: Add tests that parsing times with fractional seconds overflow - // appropriately. This can't be done until cctz::parse() properly detects - // overflow when combining the chrono seconds and femto. -} - -// -// Roundtrip test for format()/parse(). -// - -TEST(FormatParse, RoundTrip) { - time_zone lax; - EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax)); - const auto in = convert(civil_second(1977, 6, 28, 9, 8, 7), lax); - const auto subseconds = chrono::nanoseconds(654321); - - // RFC3339, which renders subseconds. - { - time_point<chrono::nanoseconds> out; - const std::string s = format(RFC3339_full, in + subseconds, lax); - EXPECT_TRUE(parse(RFC3339_full, s, lax, &out)) << s; - EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez - } - - // RFC1123, which only does whole seconds. - { - time_point<chrono::nanoseconds> out; - const std::string s = format(RFC1123_full, in, lax); - EXPECT_TRUE(parse(RFC1123_full, s, lax, &out)) << s; - EXPECT_EQ(in, out); // RFC1123_full includes %z - } - -#if defined(_WIN32) || defined(_WIN64) - // Initial investigations indicate the %c does not roundtrip on Windows. - // TODO: Figure out what is going on here (perhaps a locale problem). -#else - // Even though we don't know what %c will produce, it should roundtrip, - // but only in the 0-offset timezone. - { - time_point<chrono::nanoseconds> out; - time_zone utc = utc_time_zone(); - const std::string s = format("%c", in, utc); - EXPECT_TRUE(parse("%c", s, utc, &out)) << s; - EXPECT_EQ(in, out); - } -#endif -} - -TEST(FormatParse, RoundTripDistantFuture) { - const time_zone utc = utc_time_zone(); - const time_point<cctz::seconds> in = time_point<cctz::seconds>::max(); - const std::string s = format(RFC3339_full, in, utc); - time_point<cctz::seconds> out; - EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s; - EXPECT_EQ(in, out); -} - -TEST(FormatParse, RoundTripDistantPast) { - const time_zone utc = utc_time_zone(); - const time_point<cctz::seconds> in = time_point<cctz::seconds>::min(); - const std::string s = format(RFC3339_full, in, utc); - time_point<cctz::seconds> out; - EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s; - EXPECT_EQ(in, out); -} - -} // namespace cctz +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cctz/time_zone.h" + +#include <chrono> +#include <iomanip> +#include <sstream> +#include <string> + +#include "cctz/civil_time.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace chrono = std::chrono; + +namespace cctz { + +namespace { + +// This helper is a macro so that failed expectations show up with the +// correct line numbers. +#define ExpectTime(tp, tz, y, m, d, hh, mm, ss, off, isdst, zone) \ + do { \ + time_zone::absolute_lookup al = tz.lookup(tp); \ + EXPECT_EQ(y, al.cs.year()); \ + EXPECT_EQ(m, al.cs.month()); \ + EXPECT_EQ(d, al.cs.day()); \ + EXPECT_EQ(hh, al.cs.hour()); \ + EXPECT_EQ(mm, al.cs.minute()); \ + EXPECT_EQ(ss, al.cs.second()); \ + EXPECT_EQ(off, al.offset); \ + EXPECT_TRUE(isdst == al.is_dst); \ + EXPECT_STREQ(zone, al.abbr); \ + } while (0) + +const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez"; +const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez"; + +const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z"; +const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; + +// A helper that tests the given format specifier by itself, and with leading +// and trailing characters. For example: TestFormatSpecifier(tp, "%a", "Thu"). +template <typename D> +void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt, + const std::string& ans) { + EXPECT_EQ(ans, format(fmt, tp, tz)) << fmt; + EXPECT_EQ("xxx " + ans, format("xxx " + fmt, tp, tz)); + EXPECT_EQ(ans + " yyy", format(fmt + " yyy", tp, tz)); + EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz)); +} + +} // namespace + +// +// Testing format() +// + +TEST(Format, TimePointResolution) { + const char kFmt[] = "%H:%M:%E*S"; + const time_zone utc = utc_time_zone(); + const time_point<chrono::nanoseconds> t0 = + chrono::system_clock::from_time_t(1420167845) + + chrono::milliseconds(123) + chrono::microseconds(456) + + chrono::nanoseconds(789); + EXPECT_EQ( + "03:04:05.123456789", + format(kFmt, chrono::time_point_cast<chrono::nanoseconds>(t0), utc)); + EXPECT_EQ( + "03:04:05.123456", + format(kFmt, chrono::time_point_cast<chrono::microseconds>(t0), utc)); + EXPECT_EQ( + "03:04:05.123", + format(kFmt, chrono::time_point_cast<chrono::milliseconds>(t0), utc)); + EXPECT_EQ("03:04:05", + format(kFmt, chrono::time_point_cast<chrono::seconds>(t0), utc)); + EXPECT_EQ("03:04:05", + format(kFmt, chrono::time_point_cast<cctz::seconds>(t0), utc)); + EXPECT_EQ("03:04:00", + format(kFmt, chrono::time_point_cast<chrono::minutes>(t0), utc)); + EXPECT_EQ("03:00:00", + format(kFmt, chrono::time_point_cast<chrono::hours>(t0), utc)); +} + +TEST(Format, TimePointExtendedResolution) { + const char kFmt[] = "%H:%M:%E*S"; + const time_zone utc = utc_time_zone(); + const time_point<cctz::seconds> tp = + chrono::time_point_cast<cctz::seconds>( + chrono::system_clock::from_time_t(0)) + + chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56); + + EXPECT_EQ( + "12:34:56.123456789012345", + detail::format(kFmt, tp, detail::femtoseconds(123456789012345), utc)); + EXPECT_EQ( + "12:34:56.012345678901234", + detail::format(kFmt, tp, detail::femtoseconds(12345678901234), utc)); + EXPECT_EQ( + "12:34:56.001234567890123", + detail::format(kFmt, tp, detail::femtoseconds(1234567890123), utc)); + EXPECT_EQ( + "12:34:56.000123456789012", + detail::format(kFmt, tp, detail::femtoseconds(123456789012), utc)); + + EXPECT_EQ("12:34:56.000000000000123", + detail::format(kFmt, tp, detail::femtoseconds(123), utc)); + EXPECT_EQ("12:34:56.000000000000012", + detail::format(kFmt, tp, detail::femtoseconds(12), utc)); + EXPECT_EQ("12:34:56.000000000000001", + detail::format(kFmt, tp, detail::femtoseconds(1), utc)); +} + +TEST(Format, Basics) { + time_zone tz = utc_time_zone(); + time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); + + // Starts with a couple basic edge cases. + EXPECT_EQ("", format("", tp, tz)); + EXPECT_EQ(" ", format(" ", tp, tz)); + EXPECT_EQ(" ", format(" ", tp, tz)); + EXPECT_EQ("xxx", format("xxx", tp, tz)); + std::string big(128, 'x'); + EXPECT_EQ(big, format(big, tp, tz)); + // Cause the 1024-byte buffer to grow. + std::string bigger(100000, 'x'); + EXPECT_EQ(bigger, format(bigger, tp, tz)); + + tp += chrono::hours(13) + chrono::minutes(4) + chrono::seconds(5); + tp += chrono::milliseconds(6) + chrono::microseconds(7) + + chrono::nanoseconds(8); + EXPECT_EQ("1970-01-01", format("%Y-%m-%d", tp, tz)); + EXPECT_EQ("13:04:05", format("%H:%M:%S", tp, tz)); + EXPECT_EQ("13:04:05.006", format("%H:%M:%E3S", tp, tz)); + EXPECT_EQ("13:04:05.006007", format("%H:%M:%E6S", tp, tz)); + EXPECT_EQ("13:04:05.006007008", format("%H:%M:%E9S", tp, tz)); +} + +TEST(Format, PosixConversions) { + const time_zone tz = utc_time_zone(); + auto tp = chrono::system_clock::from_time_t(0); + + TestFormatSpecifier(tp, tz, "%d", "01"); + TestFormatSpecifier(tp, tz, "%e", " 1"); // extension but internal support + TestFormatSpecifier(tp, tz, "%H", "00"); + TestFormatSpecifier(tp, tz, "%I", "12"); + TestFormatSpecifier(tp, tz, "%j", "001"); + TestFormatSpecifier(tp, tz, "%m", "01"); + TestFormatSpecifier(tp, tz, "%M", "00"); + TestFormatSpecifier(tp, tz, "%S", "00"); + TestFormatSpecifier(tp, tz, "%U", "00"); + TestFormatSpecifier(tp, tz, "%w", "4"); // 4=Thursday + TestFormatSpecifier(tp, tz, "%W", "00"); + TestFormatSpecifier(tp, tz, "%y", "70"); + TestFormatSpecifier(tp, tz, "%Y", "1970"); + TestFormatSpecifier(tp, tz, "%z", "+0000"); + TestFormatSpecifier(tp, tz, "%Z", "UTC"); + TestFormatSpecifier(tp, tz, "%%", "%"); + +#if defined(__linux__) + // SU/C99/TZ extensions + TestFormatSpecifier(tp, tz, "%C", "19"); + TestFormatSpecifier(tp, tz, "%D", "01/01/70"); + TestFormatSpecifier(tp, tz, "%F", "1970-01-01"); + TestFormatSpecifier(tp, tz, "%g", "70"); + TestFormatSpecifier(tp, tz, "%G", "1970"); + TestFormatSpecifier(tp, tz, "%k", " 0"); + TestFormatSpecifier(tp, tz, "%l", "12"); + TestFormatSpecifier(tp, tz, "%n", "\n"); + TestFormatSpecifier(tp, tz, "%R", "00:00"); + TestFormatSpecifier(tp, tz, "%t", "\t"); + TestFormatSpecifier(tp, tz, "%T", "00:00:00"); + TestFormatSpecifier(tp, tz, "%u", "4"); // 4=Thursday + TestFormatSpecifier(tp, tz, "%V", "01"); + TestFormatSpecifier(tp, tz, "%s", "0"); +#endif +} + +TEST(Format, LocaleSpecific) { + const time_zone tz = utc_time_zone(); + auto tp = chrono::system_clock::from_time_t(0); + + TestFormatSpecifier(tp, tz, "%a", "Thu"); + TestFormatSpecifier(tp, tz, "%A", "Thursday"); + TestFormatSpecifier(tp, tz, "%b", "Jan"); + TestFormatSpecifier(tp, tz, "%B", "January"); + + // %c should at least produce the numeric year and time-of-day. + const std::string s = format("%c", tp, utc_time_zone()); + EXPECT_THAT(s, testing::HasSubstr("1970")); + EXPECT_THAT(s, testing::HasSubstr("00:00:00")); + + TestFormatSpecifier(tp, tz, "%p", "AM"); + TestFormatSpecifier(tp, tz, "%x", "01/01/70"); + TestFormatSpecifier(tp, tz, "%X", "00:00:00"); + +#if defined(__linux__) + // SU/C99/TZ extensions + TestFormatSpecifier(tp, tz, "%h", "Jan"); // Same as %b + TestFormatSpecifier(tp, tz, "%P", "am"); + TestFormatSpecifier(tp, tz, "%r", "12:00:00 AM"); + + // Modified conversion specifiers %E_ + TestFormatSpecifier(tp, tz, "%Ec", "Thu Jan 1 00:00:00 1970"); + TestFormatSpecifier(tp, tz, "%EC", "19"); + TestFormatSpecifier(tp, tz, "%Ex", "01/01/70"); + TestFormatSpecifier(tp, tz, "%EX", "00:00:00"); + TestFormatSpecifier(tp, tz, "%Ey", "70"); + TestFormatSpecifier(tp, tz, "%EY", "1970"); + + // Modified conversion specifiers %O_ + TestFormatSpecifier(tp, tz, "%Od", "01"); + TestFormatSpecifier(tp, tz, "%Oe", " 1"); + TestFormatSpecifier(tp, tz, "%OH", "00"); + TestFormatSpecifier(tp, tz, "%OI", "12"); + TestFormatSpecifier(tp, tz, "%Om", "01"); + TestFormatSpecifier(tp, tz, "%OM", "00"); + TestFormatSpecifier(tp, tz, "%OS", "00"); + TestFormatSpecifier(tp, tz, "%Ou", "4"); // 4=Thursday + TestFormatSpecifier(tp, tz, "%OU", "00"); + TestFormatSpecifier(tp, tz, "%OV", "01"); + TestFormatSpecifier(tp, tz, "%Ow", "4"); // 4=Thursday + TestFormatSpecifier(tp, tz, "%OW", "00"); + TestFormatSpecifier(tp, tz, "%Oy", "70"); +#endif +} + +TEST(Format, Escaping) { + const time_zone tz = utc_time_zone(); + auto tp = chrono::system_clock::from_time_t(0); + + TestFormatSpecifier(tp, tz, "%%", "%"); + TestFormatSpecifier(tp, tz, "%%a", "%a"); + TestFormatSpecifier(tp, tz, "%%b", "%b"); + TestFormatSpecifier(tp, tz, "%%Ea", "%Ea"); + TestFormatSpecifier(tp, tz, "%%Es", "%Es"); + TestFormatSpecifier(tp, tz, "%%E3S", "%E3S"); + TestFormatSpecifier(tp, tz, "%%OS", "%OS"); + TestFormatSpecifier(tp, tz, "%%O3S", "%O3S"); + + // Multiple levels of escaping. + TestFormatSpecifier(tp, tz, "%%%Y", "%1970"); + TestFormatSpecifier(tp, tz, "%%%E3S", "%00.000"); + TestFormatSpecifier(tp, tz, "%%%%E3S", "%%E3S"); +} + +TEST(Format, ExtendedSeconds) { + const time_zone tz = utc_time_zone(); + + // No subseconds. + time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); + tp += chrono::seconds(5); + EXPECT_EQ("05", format("%E*S", tp, tz)); + EXPECT_EQ("05", format("%E0S", tp, tz)); + EXPECT_EQ("05.0", format("%E1S", tp, tz)); + EXPECT_EQ("05.00", format("%E2S", tp, tz)); + EXPECT_EQ("05.000", format("%E3S", tp, tz)); + EXPECT_EQ("05.0000", format("%E4S", tp, tz)); + EXPECT_EQ("05.00000", format("%E5S", tp, tz)); + EXPECT_EQ("05.000000", format("%E6S", tp, tz)); + EXPECT_EQ("05.0000000", format("%E7S", tp, tz)); + EXPECT_EQ("05.00000000", format("%E8S", tp, tz)); + EXPECT_EQ("05.000000000", format("%E9S", tp, tz)); + EXPECT_EQ("05.0000000000", format("%E10S", tp, tz)); + EXPECT_EQ("05.00000000000", format("%E11S", tp, tz)); + EXPECT_EQ("05.000000000000", format("%E12S", tp, tz)); + EXPECT_EQ("05.0000000000000", format("%E13S", tp, tz)); + EXPECT_EQ("05.00000000000000", format("%E14S", tp, tz)); + EXPECT_EQ("05.000000000000000", format("%E15S", tp, tz)); + + // With subseconds. + tp += chrono::milliseconds(6) + chrono::microseconds(7) + + chrono::nanoseconds(8); + EXPECT_EQ("05.006007008", format("%E*S", tp, tz)); + EXPECT_EQ("05", format("%E0S", tp, tz)); + EXPECT_EQ("05.0", format("%E1S", tp, tz)); + EXPECT_EQ("05.00", format("%E2S", tp, tz)); + EXPECT_EQ("05.006", format("%E3S", tp, tz)); + EXPECT_EQ("05.0060", format("%E4S", tp, tz)); + EXPECT_EQ("05.00600", format("%E5S", tp, tz)); + EXPECT_EQ("05.006007", format("%E6S", tp, tz)); + EXPECT_EQ("05.0060070", format("%E7S", tp, tz)); + EXPECT_EQ("05.00600700", format("%E8S", tp, tz)); + EXPECT_EQ("05.006007008", format("%E9S", tp, tz)); + EXPECT_EQ("05.0060070080", format("%E10S", tp, tz)); + EXPECT_EQ("05.00600700800", format("%E11S", tp, tz)); + EXPECT_EQ("05.006007008000", format("%E12S", tp, tz)); + EXPECT_EQ("05.0060070080000", format("%E13S", tp, tz)); + EXPECT_EQ("05.00600700800000", format("%E14S", tp, tz)); + EXPECT_EQ("05.006007008000000", format("%E15S", tp, tz)); + + // Times before the Unix epoch. + tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1); + EXPECT_EQ("1969-12-31 23:59:59.999999", + format("%Y-%m-%d %H:%M:%E*S", tp, tz)); + + // Here is a "%E*S" case we got wrong for a while. While the first + // instant below is correctly rendered as "...:07.333304", the second + // one used to appear as "...:07.33330499999999999". + tp = chrono::system_clock::from_time_t(0) + + chrono::microseconds(1395024427333304); + EXPECT_EQ("2014-03-17 02:47:07.333304", + format("%Y-%m-%d %H:%M:%E*S", tp, tz)); + tp += chrono::microseconds(1); + EXPECT_EQ("2014-03-17 02:47:07.333305", + format("%Y-%m-%d %H:%M:%E*S", tp, tz)); +} + +TEST(Format, ExtendedSubeconds) { + const time_zone tz = utc_time_zone(); + + // No subseconds. + time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); + tp += chrono::seconds(5); + EXPECT_EQ("0", format("%E*f", tp, tz)); + EXPECT_EQ("", format("%E0f", tp, tz)); + EXPECT_EQ("0", format("%E1f", tp, tz)); + EXPECT_EQ("00", format("%E2f", tp, tz)); + EXPECT_EQ("000", format("%E3f", tp, tz)); + EXPECT_EQ("0000", format("%E4f", tp, tz)); + EXPECT_EQ("00000", format("%E5f", tp, tz)); + EXPECT_EQ("000000", format("%E6f", tp, tz)); + EXPECT_EQ("0000000", format("%E7f", tp, tz)); + EXPECT_EQ("00000000", format("%E8f", tp, tz)); + EXPECT_EQ("000000000", format("%E9f", tp, tz)); + EXPECT_EQ("0000000000", format("%E10f", tp, tz)); + EXPECT_EQ("00000000000", format("%E11f", tp, tz)); + EXPECT_EQ("000000000000", format("%E12f", tp, tz)); + EXPECT_EQ("0000000000000", format("%E13f", tp, tz)); + EXPECT_EQ("00000000000000", format("%E14f", tp, tz)); + EXPECT_EQ("000000000000000", format("%E15f", tp, tz)); + + // With subseconds. + tp += chrono::milliseconds(6) + chrono::microseconds(7) + + chrono::nanoseconds(8); + EXPECT_EQ("006007008", format("%E*f", tp, tz)); + EXPECT_EQ("", format("%E0f", tp, tz)); + EXPECT_EQ("0", format("%E1f", tp, tz)); + EXPECT_EQ("00", format("%E2f", tp, tz)); + EXPECT_EQ("006", format("%E3f", tp, tz)); + EXPECT_EQ("0060", format("%E4f", tp, tz)); + EXPECT_EQ("00600", format("%E5f", tp, tz)); + EXPECT_EQ("006007", format("%E6f", tp, tz)); + EXPECT_EQ("0060070", format("%E7f", tp, tz)); + EXPECT_EQ("00600700", format("%E8f", tp, tz)); + EXPECT_EQ("006007008", format("%E9f", tp, tz)); + EXPECT_EQ("0060070080", format("%E10f", tp, tz)); + EXPECT_EQ("00600700800", format("%E11f", tp, tz)); + EXPECT_EQ("006007008000", format("%E12f", tp, tz)); + EXPECT_EQ("0060070080000", format("%E13f", tp, tz)); + EXPECT_EQ("00600700800000", format("%E14f", tp, tz)); + EXPECT_EQ("006007008000000", format("%E15f", tp, tz)); + + // Times before the Unix epoch. + tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1); + EXPECT_EQ("1969-12-31 23:59:59.999999", + format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); + + // Here is a "%E*S" case we got wrong for a while. While the first + // instant below is correctly rendered as "...:07.333304", the second + // one used to appear as "...:07.33330499999999999". + tp = chrono::system_clock::from_time_t(0) + + chrono::microseconds(1395024427333304); + EXPECT_EQ("2014-03-17 02:47:07.333304", + format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); + tp += chrono::microseconds(1); + EXPECT_EQ("2014-03-17 02:47:07.333305", + format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); +} + +TEST(Format, CompareExtendSecondsVsSubseconds) { + const time_zone tz = utc_time_zone(); + + // This test case illustrates the differences/similarities between: + // fmt_A: %E<prec>S + // fmt_B: %S.%E<prec>f + auto fmt_A = [](const std::string& prec) { return "%E" + prec + "S"; }; + auto fmt_B = [](const std::string& prec) { return "%S.%E" + prec + "f"; }; + + // No subseconds: + time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); + tp += chrono::seconds(5); + // ... %E*S and %S.%E*f are different. + EXPECT_EQ("05", format(fmt_A("*"), tp, tz)); + EXPECT_EQ("05.0", format(fmt_B("*"), tp, tz)); + // ... %E0S and %S.%E0f are different. + EXPECT_EQ("05", format(fmt_A("0"), tp, tz)); + EXPECT_EQ("05.", format(fmt_B("0"), tp, tz)); + // ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15]. + for (int prec = 1; prec <= 15; ++prec) { + const std::string a = format(fmt_A(std::to_string(prec)), tp, tz); + const std::string b = format(fmt_B(std::to_string(prec)), tp, tz); + EXPECT_EQ(a, b) << "prec=" << prec; + } + + // With subseconds: + // ... %E*S and %S.%E*f are the same. + tp += chrono::milliseconds(6) + chrono::microseconds(7) + + chrono::nanoseconds(8); + EXPECT_EQ("05.006007008", format(fmt_A("*"), tp, tz)); + EXPECT_EQ("05.006007008", format(fmt_B("*"), tp, tz)); + // ... %E0S and %S.%E0f are different. + EXPECT_EQ("05", format(fmt_A("0"), tp, tz)); + EXPECT_EQ("05.", format(fmt_B("0"), tp, tz)); + // ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15]. + for (int prec = 1; prec <= 15; ++prec) { + const std::string a = format(fmt_A(std::to_string(prec)), tp, tz); + const std::string b = format(fmt_B(std::to_string(prec)), tp, tz); + EXPECT_EQ(a, b) << "prec=" << prec; + } +} + +TEST(Format, ExtendedOffset) { + const auto tp = chrono::system_clock::from_time_t(0); + + auto tz = fixed_time_zone(cctz::seconds::zero()); + TestFormatSpecifier(tp, tz, "%z", "+0000"); + TestFormatSpecifier(tp, tz, "%:z", "+00:00"); + TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); + + tz = fixed_time_zone(chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "+0000"); + TestFormatSpecifier(tp, tz, "%:z", "+00:00"); + TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); + + tz = fixed_time_zone(-chrono::seconds(56)); // NOTE: +00:00 + TestFormatSpecifier(tp, tz, "%z", "+0000"); + TestFormatSpecifier(tp, tz, "%:z", "+00:00"); + TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); + + tz = fixed_time_zone(chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%z", "+0034"); + TestFormatSpecifier(tp, tz, "%:z", "+00:34"); + TestFormatSpecifier(tp, tz, "%Ez", "+00:34"); + + tz = fixed_time_zone(-chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%z", "-0034"); + TestFormatSpecifier(tp, tz, "%:z", "-00:34"); + TestFormatSpecifier(tp, tz, "%Ez", "-00:34"); + + tz = fixed_time_zone(chrono::minutes(34) + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "+0034"); + TestFormatSpecifier(tp, tz, "%:z", "+00:34"); + TestFormatSpecifier(tp, tz, "%Ez", "+00:34"); + + tz = fixed_time_zone(-chrono::minutes(34) - chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "-0034"); + TestFormatSpecifier(tp, tz, "%:z", "-00:34"); + TestFormatSpecifier(tp, tz, "%Ez", "-00:34"); + + tz = fixed_time_zone(chrono::hours(12)); + TestFormatSpecifier(tp, tz, "%z", "+1200"); + TestFormatSpecifier(tp, tz, "%:z", "+12:00"); + TestFormatSpecifier(tp, tz, "%Ez", "+12:00"); + + tz = fixed_time_zone(-chrono::hours(12)); + TestFormatSpecifier(tp, tz, "%z", "-1200"); + TestFormatSpecifier(tp, tz, "%:z", "-12:00"); + TestFormatSpecifier(tp, tz, "%Ez", "-12:00"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "+1200"); + TestFormatSpecifier(tp, tz, "%:z", "+12:00"); + TestFormatSpecifier(tp, tz, "%Ez", "+12:00"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "-1200"); + TestFormatSpecifier(tp, tz, "%:z", "-12:00"); + TestFormatSpecifier(tp, tz, "%Ez", "-12:00"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%z", "+1234"); + TestFormatSpecifier(tp, tz, "%:z", "+12:34"); + TestFormatSpecifier(tp, tz, "%Ez", "+12:34"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%z", "-1234"); + TestFormatSpecifier(tp, tz, "%:z", "-12:34"); + TestFormatSpecifier(tp, tz, "%Ez", "-12:34"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34) + + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "+1234"); + TestFormatSpecifier(tp, tz, "%:z", "+12:34"); + TestFormatSpecifier(tp, tz, "%Ez", "+12:34"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34) - + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "-1234"); + TestFormatSpecifier(tp, tz, "%:z", "-12:34"); + TestFormatSpecifier(tp, tz, "%Ez", "-12:34"); +} + +TEST(Format, ExtendedSecondOffset) { + const auto tp = chrono::system_clock::from_time_t(0); + + auto tz = fixed_time_zone(cctz::seconds::zero()); + TestFormatSpecifier(tp, tz, "%E*z", "+00:00:00"); + TestFormatSpecifier(tp, tz, "%::z", "+00:00:00"); + TestFormatSpecifier(tp, tz, "%:::z", "+00"); + + tz = fixed_time_zone(chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "+00:00:56"); + TestFormatSpecifier(tp, tz, "%::z", "+00:00:56"); + TestFormatSpecifier(tp, tz, "%:::z", "+00:00:56"); + + tz = fixed_time_zone(-chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "-00:00:56"); + TestFormatSpecifier(tp, tz, "%::z", "-00:00:56"); + TestFormatSpecifier(tp, tz, "%:::z", "-00:00:56"); + + tz = fixed_time_zone(chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%E*z", "+00:34:00"); + TestFormatSpecifier(tp, tz, "%::z", "+00:34:00"); + TestFormatSpecifier(tp, tz, "%:::z", "+00:34"); + + tz = fixed_time_zone(-chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%E*z", "-00:34:00"); + TestFormatSpecifier(tp, tz, "%::z", "-00:34:00"); + TestFormatSpecifier(tp, tz, "%:::z", "-00:34"); + + tz = fixed_time_zone(chrono::minutes(34) + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "+00:34:56"); + TestFormatSpecifier(tp, tz, "%::z", "+00:34:56"); + TestFormatSpecifier(tp, tz, "%:::z", "+00:34:56"); + + tz = fixed_time_zone(-chrono::minutes(34) - chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "-00:34:56"); + TestFormatSpecifier(tp, tz, "%::z", "-00:34:56"); + TestFormatSpecifier(tp, tz, "%:::z", "-00:34:56"); + + tz = fixed_time_zone(chrono::hours(12)); + TestFormatSpecifier(tp, tz, "%E*z", "+12:00:00"); + TestFormatSpecifier(tp, tz, "%::z", "+12:00:00"); + TestFormatSpecifier(tp, tz, "%:::z", "+12"); + + tz = fixed_time_zone(-chrono::hours(12)); + TestFormatSpecifier(tp, tz, "%E*z", "-12:00:00"); + TestFormatSpecifier(tp, tz, "%::z", "-12:00:00"); + TestFormatSpecifier(tp, tz, "%:::z", "-12"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "+12:00:56"); + TestFormatSpecifier(tp, tz, "%::z", "+12:00:56"); + TestFormatSpecifier(tp, tz, "%:::z", "+12:00:56"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "-12:00:56"); + TestFormatSpecifier(tp, tz, "%::z", "-12:00:56"); + TestFormatSpecifier(tp, tz, "%:::z", "-12:00:56"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%E*z", "+12:34:00"); + TestFormatSpecifier(tp, tz, "%::z", "+12:34:00"); + TestFormatSpecifier(tp, tz, "%:::z", "+12:34"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%E*z", "-12:34:00"); + TestFormatSpecifier(tp, tz, "%::z", "-12:34:00"); + TestFormatSpecifier(tp, tz, "%:::z", "-12:34"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34) + + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "+12:34:56"); + TestFormatSpecifier(tp, tz, "%::z", "+12:34:56"); + TestFormatSpecifier(tp, tz, "%:::z", "+12:34:56"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34) - + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "-12:34:56"); + TestFormatSpecifier(tp, tz, "%::z", "-12:34:56"); + TestFormatSpecifier(tp, tz, "%:::z", "-12:34:56"); +} + +TEST(Format, ExtendedYears) { + const time_zone utc = utc_time_zone(); + const char e4y_fmt[] = "%E4Y%m%d"; // no separators + + // %E4Y zero-pads the year to produce at least 4 chars, including the sign. + auto tp = convert(civil_second(-999, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("-9991127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(-99, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("-0991127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(-9, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("-0091127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(-1, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("-0011127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(0, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("00001127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(1, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("00011127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(9, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("00091127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(99, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("00991127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(999, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("09991127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(9999, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("99991127", format(e4y_fmt, tp, utc)); + + // When the year is outside [-999:9999], more than 4 chars are produced. + tp = convert(civil_second(-1000, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("-10001127", format(e4y_fmt, tp, utc)); + tp = convert(civil_second(10000, 11, 27, 0, 0, 0), utc); + EXPECT_EQ("100001127", format(e4y_fmt, tp, utc)); +} + +TEST(Format, RFC3339Format) { + time_zone tz; + EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); + + time_point<chrono::nanoseconds> tp = + convert(civil_second(1977, 6, 28, 9, 8, 7), tz); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + + tp += chrono::milliseconds(100); + EXPECT_EQ("1977-06-28T09:08:07.1-07:00", format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + + tp += chrono::milliseconds(20); + EXPECT_EQ("1977-06-28T09:08:07.12-07:00", format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + + tp += chrono::milliseconds(3); + EXPECT_EQ("1977-06-28T09:08:07.123-07:00", format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + + tp += chrono::microseconds(400); + EXPECT_EQ("1977-06-28T09:08:07.1234-07:00", format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + + tp += chrono::microseconds(50); + EXPECT_EQ("1977-06-28T09:08:07.12345-07:00", format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + + tp += chrono::microseconds(6); + EXPECT_EQ("1977-06-28T09:08:07.123456-07:00", format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + + tp += chrono::nanoseconds(700); + EXPECT_EQ("1977-06-28T09:08:07.1234567-07:00", format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + + tp += chrono::nanoseconds(80); + EXPECT_EQ("1977-06-28T09:08:07.12345678-07:00", format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + + tp += chrono::nanoseconds(9); + EXPECT_EQ("1977-06-28T09:08:07.123456789-07:00", + format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); +} + +TEST(Format, RFC1123Format) { // locale specific + time_zone tz; + EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); + + auto tp = convert(civil_second(1977, 6, 28, 9, 8, 7), tz); + EXPECT_EQ("Tue, 28 Jun 1977 09:08:07 -0700", format(RFC1123_full, tp, tz)); + EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz)); +} + +TEST(Format, Week) { + const time_zone utc = utc_time_zone(); + + auto tp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc); + EXPECT_EQ("2017-01-7", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2017-00-0", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc); + EXPECT_EQ("2017-53-7", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2017-52-0", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc); + EXPECT_EQ("2018-00-1", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2018-01-1", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc); + EXPECT_EQ("2018-52-1", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2018-53-1", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc); + EXPECT_EQ("2019-00-2", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2019-00-2", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); + EXPECT_EQ("2019-52-2", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2019-52-2", format("%Y-%W-%w", tp, utc)); +} + +// +// Testing parse() +// + +TEST(Parse, TimePointResolution) { + const char kFmt[] = "%H:%M:%E*S"; + const time_zone utc = utc_time_zone(); + + time_point<chrono::nanoseconds> tp_ns; + EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_ns)); + EXPECT_EQ("03:04:05.123456789", format(kFmt, tp_ns, utc)); + EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ns)); + EXPECT_EQ("03:04:05.123456", format(kFmt, tp_ns, utc)); + + time_point<chrono::microseconds> tp_us; + EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_us)); + EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc)); + EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_us)); + EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc)); + EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_us)); + EXPECT_EQ("03:04:05.123", format(kFmt, tp_us, utc)); + + time_point<chrono::milliseconds> tp_ms; + EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ms)); + EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc)); + EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_ms)); + EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc)); + EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_ms)); + EXPECT_EQ("03:04:05", format(kFmt, tp_ms, utc)); + + time_point<chrono::seconds> tp_s; + EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_s)); + EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc)); + EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_s)); + EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc)); + + time_point<chrono::minutes> tp_m; + EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_m)); + EXPECT_EQ("03:04:00", format(kFmt, tp_m, utc)); + + time_point<chrono::hours> tp_h; + EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_h)); + EXPECT_EQ("03:00:00", format(kFmt, tp_h, utc)); +} + +TEST(Parse, TimePointExtendedResolution) { + const char kFmt[] = "%H:%M:%E*S"; + const time_zone utc = utc_time_zone(); + + time_point<cctz::seconds> tp; + detail::femtoseconds fs; + EXPECT_TRUE(detail::parse(kFmt, "12:34:56.123456789012345", utc, &tp, &fs)); + EXPECT_EQ("12:34:56.123456789012345", detail::format(kFmt, tp, fs, utc)); + EXPECT_TRUE(detail::parse(kFmt, "12:34:56.012345678901234", utc, &tp, &fs)); + EXPECT_EQ("12:34:56.012345678901234", detail::format(kFmt, tp, fs, utc)); + EXPECT_TRUE(detail::parse(kFmt, "12:34:56.001234567890123", utc, &tp, &fs)); + EXPECT_EQ("12:34:56.001234567890123", detail::format(kFmt, tp, fs, utc)); + EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000123", utc, &tp, &fs)); + EXPECT_EQ("12:34:56.000000000000123", detail::format(kFmt, tp, fs, utc)); + EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000012", utc, &tp, &fs)); + EXPECT_EQ("12:34:56.000000000000012", detail::format(kFmt, tp, fs, utc)); + EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000001", utc, &tp, &fs)); + EXPECT_EQ("12:34:56.000000000000001", detail::format(kFmt, tp, fs, utc)); +} + +TEST(Parse, Basics) { + time_zone tz = utc_time_zone(); + time_point<chrono::nanoseconds> tp = + chrono::system_clock::from_time_t(1234567890); + + // Simple edge cases. + EXPECT_TRUE(parse("", "", tz, &tp)); + EXPECT_EQ(chrono::system_clock::from_time_t(0), tp); // everything defaulted + EXPECT_TRUE(parse(" ", " ", tz, &tp)); + EXPECT_TRUE(parse(" ", " ", tz, &tp)); + EXPECT_TRUE(parse("x", "x", tz, &tp)); + EXPECT_TRUE(parse("xxx", "xxx", tz, &tp)); + + EXPECT_TRUE( + parse("%Y-%m-%d %H:%M:%S %z", "2013-06-28 19:08:09 -0800", tz, &tp)); + ExpectTime(tp, tz, 2013, 6, 29, 3, 8, 9, 0, false, "UTC"); +} + +TEST(Parse, WithTimeZone) { + time_zone tz; + EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); + time_point<chrono::nanoseconds> tp; + + // We can parse a string without a UTC offset if we supply a timezone. + EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &tp)); + ExpectTime(tp, tz, 2013, 6, 28, 19, 8, 9, -7 * 60 * 60, true, "PDT"); + + // But the timezone is ignored when a UTC offset is present. + EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S %z", "2013-06-28 19:08:09 +0800", + utc_time_zone(), &tp)); + ExpectTime(tp, tz, 2013, 6, 28, 19 - 8 - 7, 8, 9, -7 * 60 * 60, true, "PDT"); + + // Check a skipped time (a Spring DST transition). parse() uses the + // pre-transition offset. + EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-03-13 02:15:00", tz, &tp)); + ExpectTime(tp, tz, 2011, 3, 13, 3, 15, 0, -7 * 60 * 60, true, "PDT"); + + // Check a repeated time (a Fall DST transition). parse() uses the + // pre-transition offset. + EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-11-06 01:15:00", tz, &tp)); + ExpectTime(tp, tz, 2011, 11, 6, 1, 15, 0, -7 * 60 * 60, true, "PDT"); +} + +TEST(Parse, LeapSecond) { + time_zone tz; + EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); + time_point<chrono::nanoseconds> tp; + + // ":59" -> ":59" + EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59-08:00", tz, &tp)); + ExpectTime(tp, tz, 2013, 6, 28, 8, 8, 59, -7 * 60 * 60, true, "PDT"); + + // ":59.5" -> ":59.5" + EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59.5-08:00", tz, &tp)); + ExpectTime(tp, tz, 2013, 6, 28, 8, 8, 59, -7 * 60 * 60, true, "PDT"); + + // ":60" -> ":00" + EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:60-08:00", tz, &tp)); + ExpectTime(tp, tz, 2013, 6, 28, 8, 9, 0, -7 * 60 * 60, true, "PDT"); + + // ":60.5" -> ":00.0" + EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:60.5-08:00", tz, &tp)); + ExpectTime(tp, tz, 2013, 6, 28, 8, 9, 0, -7 * 60 * 60, true, "PDT"); + + // ":61" -> error + EXPECT_FALSE(parse(RFC3339_full, "2013-06-28T07:08:61-08:00", tz, &tp)); +} + +TEST(Parse, ErrorCases) { + const time_zone tz = utc_time_zone(); + auto tp = chrono::system_clock::from_time_t(0); + + // Illegal trailing data. + EXPECT_FALSE(parse("%S", "123", tz, &tp)); + + // Can't parse an illegal format specifier. + EXPECT_FALSE(parse("%Q", "x", tz, &tp)); + + // Fails because of trailing, unparsed data "blah". + EXPECT_FALSE(parse("%m-%d", "2-3 blah", tz, &tp)); + + // Trailing whitespace is allowed. + EXPECT_TRUE(parse("%m-%d", "2-3 ", tz, &tp)); + EXPECT_EQ(2, convert(tp, utc_time_zone()).month()); + EXPECT_EQ(3, convert(tp, utc_time_zone()).day()); + + // Feb 31 requires normalization. + EXPECT_FALSE(parse("%m-%d", "2-31", tz, &tp)); + + // Check that we cannot have spaces in UTC offsets. + EXPECT_TRUE(parse("%z", "-0203", tz, &tp)); + EXPECT_FALSE(parse("%z", "- 2 3", tz, &tp)); + EXPECT_TRUE(parse("%Ez", "-02:03", tz, &tp)); + EXPECT_FALSE(parse("%Ez", "- 2: 3", tz, &tp)); + + // Check that we reject other malformed UTC offsets. + EXPECT_FALSE(parse("%Ez", "+-08:00", tz, &tp)); + EXPECT_FALSE(parse("%Ez", "-+08:00", tz, &tp)); + + // Check that we do not accept "-0" in fields that allow zero. + EXPECT_FALSE(parse("%Y", "-0", tz, &tp)); + EXPECT_FALSE(parse("%E4Y", "-0", tz, &tp)); + EXPECT_FALSE(parse("%H", "-0", tz, &tp)); + EXPECT_FALSE(parse("%M", "-0", tz, &tp)); + EXPECT_FALSE(parse("%S", "-0", tz, &tp)); + EXPECT_FALSE(parse("%z", "+-000", tz, &tp)); + EXPECT_FALSE(parse("%Ez", "+-0:00", tz, &tp)); + EXPECT_FALSE(parse("%z", "-00-0", tz, &tp)); + EXPECT_FALSE(parse("%Ez", "-00:-0", tz, &tp)); +} + +TEST(Parse, PosixConversions) { + time_zone tz = utc_time_zone(); + auto tp = chrono::system_clock::from_time_t(0); + const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz); + + tp = reset; + EXPECT_TRUE(parse("%d", "15", tz, &tp)); + EXPECT_EQ(15, convert(tp, tz).day()); + + // %e is an extension, but is supported internally. + tp = reset; + EXPECT_TRUE(parse("%e", "15", tz, &tp)); + EXPECT_EQ(15, convert(tp, tz).day()); // Equivalent to %d + + tp = reset; + EXPECT_TRUE(parse("%H", "17", tz, &tp)); + EXPECT_EQ(17, convert(tp, tz).hour()); + + tp = reset; + EXPECT_TRUE(parse("%I", "5", tz, &tp)); + EXPECT_EQ(5, convert(tp, tz).hour()); + + // %j is parsed but ignored. + EXPECT_TRUE(parse("%j", "32", tz, &tp)); + + tp = reset; + EXPECT_TRUE(parse("%m", "11", tz, &tp)); + EXPECT_EQ(11, convert(tp, tz).month()); + + tp = reset; + EXPECT_TRUE(parse("%M", "33", tz, &tp)); + EXPECT_EQ(33, convert(tp, tz).minute()); + + tp = reset; + EXPECT_TRUE(parse("%S", "55", tz, &tp)); + EXPECT_EQ(55, convert(tp, tz).second()); + + // %U is parsed but ignored. + EXPECT_TRUE(parse("%U", "15", tz, &tp)); + + // %w is parsed but ignored. + EXPECT_TRUE(parse("%w", "2", tz, &tp)); + + // %W is parsed but ignored. + EXPECT_TRUE(parse("%W", "22", tz, &tp)); + + tp = reset; + EXPECT_TRUE(parse("%y", "04", tz, &tp)); + EXPECT_EQ(2004, convert(tp, tz).year()); + + tp = reset; + EXPECT_TRUE(parse("%Y", "2004", tz, &tp)); + EXPECT_EQ(2004, convert(tp, tz).year()); + + EXPECT_TRUE(parse("%%", "%", tz, &tp)); + +#if defined(__linux__) + // SU/C99/TZ extensions + + // Because we handle each (non-internal) specifier in a separate call + // to strptime(), there is no way to group %C and %y together. So we + // just skip the %C/%y case. +#if 0 + tp = reset; + EXPECT_TRUE(parse("%C %y", "20 04", tz, &tp)); + EXPECT_EQ(2004, convert(tp, tz).year()); +#endif + + tp = reset; + EXPECT_TRUE(parse("%D", "02/03/04", tz, &tp)); + EXPECT_EQ(2, convert(tp, tz).month()); + EXPECT_EQ(3, convert(tp, tz).day()); + EXPECT_EQ(2004, convert(tp, tz).year()); + + EXPECT_TRUE(parse("%n", "\n", tz, &tp)); + + tp = reset; + EXPECT_TRUE(parse("%R", "03:44", tz, &tp)); + EXPECT_EQ(3, convert(tp, tz).hour()); + EXPECT_EQ(44, convert(tp, tz).minute()); + + EXPECT_TRUE(parse("%t", "\t\v\f\n\r ", tz, &tp)); + + tp = reset; + EXPECT_TRUE(parse("%T", "03:44:55", tz, &tp)); + EXPECT_EQ(3, convert(tp, tz).hour()); + EXPECT_EQ(44, convert(tp, tz).minute()); + EXPECT_EQ(55, convert(tp, tz).second()); + + tp = reset; + EXPECT_TRUE(parse("%s", "1234567890", tz, &tp)); + EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp); + + // %s conversion, like %z/%Ez, pays no heed to the optional zone. + time_zone lax; + EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax)); + tp = reset; + EXPECT_TRUE(parse("%s", "1234567890", lax, &tp)); + EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp); + + // This is most important when the time has the same YMDhms + // breakdown in the zone as some other time. For example, ... + // 1414917000 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PDT) + // 1414920600 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PST) + tp = reset; + EXPECT_TRUE(parse("%s", "1414917000", lax, &tp)); + EXPECT_EQ(chrono::system_clock::from_time_t(1414917000), tp); + tp = reset; + EXPECT_TRUE(parse("%s", "1414920600", lax, &tp)); + EXPECT_EQ(chrono::system_clock::from_time_t(1414920600), tp); +#endif +} + +TEST(Parse, LocaleSpecific) { + time_zone tz = utc_time_zone(); + auto tp = chrono::system_clock::from_time_t(0); + const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz); + + // %a is parsed but ignored. + EXPECT_TRUE(parse("%a", "Mon", tz, &tp)); + + // %A is parsed but ignored. + EXPECT_TRUE(parse("%A", "Monday", tz, &tp)); + + tp = reset; + EXPECT_TRUE(parse("%b", "Feb", tz, &tp)); + EXPECT_EQ(2, convert(tp, tz).month()); + + tp = reset; + EXPECT_TRUE(parse("%B", "February", tz, &tp)); + EXPECT_EQ(2, convert(tp, tz).month()); + + // %p is parsed but ignored if it's alone. But it's used with %I. + EXPECT_TRUE(parse("%p", "AM", tz, &tp)); + tp = reset; + EXPECT_TRUE(parse("%I %p", "5 PM", tz, &tp)); + EXPECT_EQ(17, convert(tp, tz).hour()); + + tp = reset; + EXPECT_TRUE(parse("%x", "02/03/04", tz, &tp)); + if (convert(tp, tz).month() == 2) { + EXPECT_EQ(3, convert(tp, tz).day()); + } else { + EXPECT_EQ(2, convert(tp, tz).day()); + EXPECT_EQ(3, convert(tp, tz).month()); + } + EXPECT_EQ(2004, convert(tp, tz).year()); + + tp = reset; + EXPECT_TRUE(parse("%X", "15:44:55", tz, &tp)); + EXPECT_EQ(15, convert(tp, tz).hour()); + EXPECT_EQ(44, convert(tp, tz).minute()); + EXPECT_EQ(55, convert(tp, tz).second()); + +#if defined(__linux__) + // SU/C99/TZ extensions + + tp = reset; + EXPECT_TRUE(parse("%h", "Feb", tz, &tp)); + EXPECT_EQ(2, convert(tp, tz).month()); // Equivalent to %b + + tp = reset; + EXPECT_TRUE(parse("%l %p", "5 PM", tz, &tp)); + EXPECT_EQ(17, convert(tp, tz).hour()); + + tp = reset; + EXPECT_TRUE(parse("%r", "03:44:55 PM", tz, &tp)); + EXPECT_EQ(15, convert(tp, tz).hour()); + EXPECT_EQ(44, convert(tp, tz).minute()); + EXPECT_EQ(55, convert(tp, tz).second()); + + tp = reset; + EXPECT_TRUE(parse("%Ec", "Tue Nov 19 05:06:07 2013", tz, &tp)); + EXPECT_EQ(convert(civil_second(2013, 11, 19, 5, 6, 7), tz), tp); + + // Modified conversion specifiers %E_ + + tp = reset; + EXPECT_TRUE(parse("%Ex", "02/03/04", tz, &tp)); + EXPECT_EQ(2, convert(tp, tz).month()); + EXPECT_EQ(3, convert(tp, tz).day()); + EXPECT_EQ(2004, convert(tp, tz).year()); + + tp = reset; + EXPECT_TRUE(parse("%EX", "15:44:55", tz, &tp)); + EXPECT_EQ(15, convert(tp, tz).hour()); + EXPECT_EQ(44, convert(tp, tz).minute()); + EXPECT_EQ(55, convert(tp, tz).second()); + + // %Ey, the year offset from %EC, doesn't really make sense alone as there + // is no way to represent it in tm_year (%EC is not simply the century). + // Yet, because we handle each (non-internal) specifier in a separate call + // to strptime(), there is no way to group %EC and %Ey either. So we just + // skip the %EC and %Ey cases. + + tp = reset; + EXPECT_TRUE(parse("%EY", "2004", tz, &tp)); + EXPECT_EQ(2004, convert(tp, tz).year()); + + // Modified conversion specifiers %O_ + + tp = reset; + EXPECT_TRUE(parse("%Od", "15", tz, &tp)); + EXPECT_EQ(15, convert(tp, tz).day()); + + tp = reset; + EXPECT_TRUE(parse("%Oe", "15", tz, &tp)); + EXPECT_EQ(15, convert(tp, tz).day()); // Equivalent to %d + + tp = reset; + EXPECT_TRUE(parse("%OH", "17", tz, &tp)); + EXPECT_EQ(17, convert(tp, tz).hour()); + + tp = reset; + EXPECT_TRUE(parse("%OI", "5", tz, &tp)); + EXPECT_EQ(5, convert(tp, tz).hour()); + + tp = reset; + EXPECT_TRUE(parse("%Om", "11", tz, &tp)); + EXPECT_EQ(11, convert(tp, tz).month()); + + tp = reset; + EXPECT_TRUE(parse("%OM", "33", tz, &tp)); + EXPECT_EQ(33, convert(tp, tz).minute()); + + tp = reset; + EXPECT_TRUE(parse("%OS", "55", tz, &tp)); + EXPECT_EQ(55, convert(tp, tz).second()); + + // %OU is parsed but ignored. + EXPECT_TRUE(parse("%OU", "15", tz, &tp)); + + // %Ow is parsed but ignored. + EXPECT_TRUE(parse("%Ow", "2", tz, &tp)); + + // %OW is parsed but ignored. + EXPECT_TRUE(parse("%OW", "22", tz, &tp)); + + tp = reset; + EXPECT_TRUE(parse("%Oy", "04", tz, &tp)); + EXPECT_EQ(2004, convert(tp, tz).year()); +#endif +} + +TEST(Parse, ExtendedSeconds) { + const time_zone tz = utc_time_zone(); + const time_point<chrono::nanoseconds> unix_epoch = + chrono::system_clock::from_time_t(0); + + // All %E<prec>S cases are treated the same as %E*S on input. + auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15"}; + for (const std::string& prec : precisions) { + const std::string fmt = "%E" + prec + "S"; + SCOPED_TRACE(fmt); + time_point<chrono::nanoseconds> tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "5", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "05", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "05.0", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "05.00", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "05.6", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "05.60", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "05.600", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(600), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "05.67", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "05.670", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(670), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "05.678", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::seconds(5) + chrono::milliseconds(678), tp); + } + + // Here is a "%E*S" case we got wrong for a while. The fractional + // part of the first instant is less than 2^31 and was correctly + // parsed, while the second (and any subsecond field >=2^31) failed. + time_point<chrono::nanoseconds> tp = unix_epoch; + EXPECT_TRUE(parse("%E*S", "0.2147483647", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); + tp = unix_epoch; + EXPECT_TRUE(parse("%E*S", "0.2147483648", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); + + // We should also be able to specify long strings of digits far + // beyond the current resolution and have them convert the same way. + tp = unix_epoch; + EXPECT_TRUE(parse( + "%E*S", "0.214748364801234567890123456789012345678901234567890123456789", + tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); +} + +TEST(Parse, ExtendedSecondsScan) { + const time_zone tz = utc_time_zone(); + time_point<chrono::nanoseconds> tp; + for (int ms = 0; ms < 1000; ms += 111) { + for (int us = 0; us < 1000; us += 27) { + const int micros = ms * 1000 + us; + for (int ns = 0; ns < 1000; ns += 9) { + const auto expected = chrono::system_clock::from_time_t(0) + + chrono::nanoseconds(micros * 1000 + ns); + std::ostringstream oss; + oss << "0." << std::setfill('0') << std::setw(3); + oss << ms << std::setw(3) << us << std::setw(3) << ns; + const std::string input = oss.str(); + EXPECT_TRUE(parse("%E*S", input, tz, &tp)); + EXPECT_EQ(expected, tp) << input; + } + } + } +} + +TEST(Parse, ExtendedSubeconds) { + const time_zone tz = utc_time_zone(); + const time_point<chrono::nanoseconds> unix_epoch = + chrono::system_clock::from_time_t(0); + + // All %E<prec>f cases are treated the same as %E*f on input. + auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15"}; + for (const std::string& prec : precisions) { + const std::string fmt = "%E" + prec + "f"; + SCOPED_TRACE(fmt); + time_point<chrono::nanoseconds> tp = unix_epoch - chrono::seconds(1); + EXPECT_TRUE(parse(fmt, "", tz, &tp)); + EXPECT_EQ(unix_epoch, tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "6", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "60", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "600", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "67", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "670", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "678", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::milliseconds(678), tp); + tp = unix_epoch; + EXPECT_TRUE(parse(fmt, "6789", tz, &tp)); + EXPECT_EQ( + unix_epoch + chrono::milliseconds(678) + chrono::microseconds(900), tp); + } + + // Here is a "%E*f" case we got wrong for a while. The fractional + // part of the first instant is less than 2^31 and was correctly + // parsed, while the second (and any subsecond field >=2^31) failed. + time_point<chrono::nanoseconds> tp = unix_epoch; + EXPECT_TRUE(parse("%E*f", "2147483647", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); + tp = unix_epoch; + EXPECT_TRUE(parse("%E*f", "2147483648", tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); + + // We should also be able to specify long strings of digits far + // beyond the current resolution and have them convert the same way. + tp = unix_epoch; + EXPECT_TRUE(parse( + "%E*f", "214748364801234567890123456789012345678901234567890123456789", + tz, &tp)); + EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); +} + +TEST(Parse, ExtendedSubecondsScan) { + time_point<chrono::nanoseconds> tp; + const time_zone tz = utc_time_zone(); + for (int ms = 0; ms < 1000; ms += 111) { + for (int us = 0; us < 1000; us += 27) { + const int micros = ms * 1000 + us; + for (int ns = 0; ns < 1000; ns += 9) { + std::ostringstream oss; + oss << std::setfill('0') << std::setw(3) << ms; + oss << std::setw(3) << us << std::setw(3) << ns; + const std::string nanos = oss.str(); + const auto expected = chrono::system_clock::from_time_t(0) + + chrono::nanoseconds(micros * 1000 + ns); + for (int ps = 0; ps < 1000; ps += 250) { + std::ostringstream ps_oss; + oss << std::setfill('0') << std::setw(3) << ps; + const std::string input = nanos + ps_oss.str() + "999"; + EXPECT_TRUE(parse("%E*f", input, tz, &tp)); + EXPECT_EQ(expected + chrono::nanoseconds(ps) / 1000, tp) << input; + } + } + } + } +} + +TEST(Parse, ExtendedOffset) { + const time_zone utc = utc_time_zone(); + time_point<cctz::seconds> tp; + + EXPECT_TRUE(parse("%Ez", "+00:00", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse("%Ez", "-12:34", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); + EXPECT_TRUE(parse("%Ez", "+12:34", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); + EXPECT_FALSE(parse("%Ez", "-12:3", utc, &tp)); + + for (auto fmt : {"%Ez", "%z"}) { + EXPECT_TRUE(parse(fmt, "+0000", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-1234", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+1234", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-123", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+00", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-12", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+12", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-1", utc, &tp)); + } +} + +TEST(Parse, ExtendedSecondOffset) { + const time_zone utc = utc_time_zone(); + time_point<cctz::seconds> tp; + + for (auto fmt : {"%Ez", "%E*z", "%:z", "%::z", "%:::z"}) { + EXPECT_TRUE(parse(fmt, "+00:00:00", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-12:34:56", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); + EXPECT_TRUE(parse(fmt, "+12:34:56", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); + EXPECT_FALSE(parse(fmt, "-12:34:5", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+000000", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-123456", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); + EXPECT_TRUE(parse(fmt, "+123456", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); + EXPECT_FALSE(parse(fmt, "-12345", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+00:00", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-12:34", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+12:34", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-12:3", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+0000", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-1234", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+1234", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-123", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+00", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-12", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+12", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-1", utc, &tp)); + } +} + +TEST(Parse, ExtendedYears) { + const time_zone utc = utc_time_zone(); + const char e4y_fmt[] = "%E4Y%m%d"; // no separators + time_point<cctz::seconds> tp; + + // %E4Y consumes exactly four chars, including any sign. + EXPECT_TRUE(parse(e4y_fmt, "-9991127", utc, &tp)); + EXPECT_EQ(convert(civil_second(-999, 11, 27, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(e4y_fmt, "-0991127", utc, &tp)); + EXPECT_EQ(convert(civil_second(-99, 11, 27, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(e4y_fmt, "-0091127", utc, &tp)); + EXPECT_EQ(convert(civil_second(-9, 11, 27, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(e4y_fmt, "-0011127", utc, &tp)); + EXPECT_EQ(convert(civil_second(-1, 11, 27, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(e4y_fmt, "00001127", utc, &tp)); + EXPECT_EQ(convert(civil_second(0, 11, 27, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(e4y_fmt, "00011127", utc, &tp)); + EXPECT_EQ(convert(civil_second(1, 11, 27, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(e4y_fmt, "00091127", utc, &tp)); + EXPECT_EQ(convert(civil_second(9, 11, 27, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(e4y_fmt, "00991127", utc, &tp)); + EXPECT_EQ(convert(civil_second(99, 11, 27, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(e4y_fmt, "09991127", utc, &tp)); + EXPECT_EQ(convert(civil_second(999, 11, 27, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(e4y_fmt, "99991127", utc, &tp)); + EXPECT_EQ(convert(civil_second(9999, 11, 27, 0, 0, 0), utc), tp); + + // When the year is outside [-999:9999], the parse fails. + EXPECT_FALSE(parse(e4y_fmt, "-10001127", utc, &tp)); + EXPECT_FALSE(parse(e4y_fmt, "100001127", utc, &tp)); +} + +TEST(Parse, RFC3339Format) { + const time_zone tz = utc_time_zone(); + time_point<chrono::nanoseconds> tp; + EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp)); + ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC"); + + // Check that %ET also accepts "t". + time_point<chrono::nanoseconds> tp2; + EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12t20:21:00+00:00", tz, &tp2)); + EXPECT_EQ(tp, tp2); + + // Check that %Ez also accepts "Z" as a synonym for "+00:00". + time_point<chrono::nanoseconds> tp3; + EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp3)); + EXPECT_EQ(tp, tp3); + + // Check that %Ez also accepts "z" as a synonym for "+00:00". + time_point<chrono::nanoseconds> tp4; + EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00z", tz, &tp4)); + EXPECT_EQ(tp, tp4); +} + +TEST(Parse, Week) { + const time_zone utc = utc_time_zone(); + time_point<cctz::seconds> tp; + + auto exp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2017-01-7", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2017-00-0", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2017-53-7", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2017-52-0", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2018-00-1", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2018-01-1", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2018-52-1", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2018-53-1", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2019-00-2", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2019-00-2", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2019-52-2", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2019-52-2", utc, &tp)); + EXPECT_EQ(exp, tp); +} + +TEST(Parse, WeekYearShift) { + // %U/%W conversions with week values in {0, 52, 53} can slip + // into the previous/following calendar years. + const time_zone utc = utc_time_zone(); + time_point<cctz::seconds> tp; + + auto exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2020-00-2", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2020-00-2", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2021, 1, 1, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2020-52-5", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2020-52-5", utc, &tp)); + EXPECT_EQ(exp, tp); + + // Slipping into the previous/following calendar years should fail when + // we're already at the extremes. + EXPECT_FALSE(parse("%Y-%U-%u", "-9223372036854775808-0-7", utc, &tp)); + EXPECT_FALSE(parse("%Y-%U-%u", "9223372036854775807-53-7", utc, &tp)); +} + +TEST(Parse, MaxRange) { + const time_zone utc = utc_time_zone(); + time_point<cctz::seconds> tp; + + // tests the upper limit using +00:00 offset + EXPECT_TRUE( + parse(RFC3339_sec, "292277026596-12-04T15:30:07+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point<cctz::seconds>::max()); + EXPECT_FALSE( + parse(RFC3339_sec, "292277026596-12-04T15:30:08+00:00", utc, &tp)); + + // tests the upper limit using -01:00 offset + EXPECT_TRUE( + parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp)); + EXPECT_EQ(tp, time_point<cctz::seconds>::max()); + EXPECT_FALSE( + parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp)); + + // tests the lower limit using +00:00 offset + EXPECT_TRUE( + parse(RFC3339_sec, "-292277022657-01-27T08:29:52+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point<cctz::seconds>::min()); + EXPECT_FALSE( + parse(RFC3339_sec, "-292277022657-01-27T08:29:51+00:00", utc, &tp)); + + // tests the lower limit using +01:00 offset + EXPECT_TRUE( + parse(RFC3339_sec, "-292277022657-01-27T09:29:52+01:00", utc, &tp)); + EXPECT_EQ(tp, time_point<cctz::seconds>::min()); + EXPECT_FALSE( + parse(RFC3339_sec, "-292277022657-01-27T08:29:51+01:00", utc, &tp)); + + // tests max/min civil-second overflow + EXPECT_FALSE(parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", + utc, &tp)); + EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01", + utc, &tp)); + + // TODO: Add tests that parsing times with fractional seconds overflow + // appropriately. This can't be done until cctz::parse() properly detects + // overflow when combining the chrono seconds and femto. +} + +// +// Roundtrip test for format()/parse(). +// + +TEST(FormatParse, RoundTrip) { + time_zone lax; + EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax)); + const auto in = convert(civil_second(1977, 6, 28, 9, 8, 7), lax); + const auto subseconds = chrono::nanoseconds(654321); + + // RFC3339, which renders subseconds. + { + time_point<chrono::nanoseconds> out; + const std::string s = format(RFC3339_full, in + subseconds, lax); + EXPECT_TRUE(parse(RFC3339_full, s, lax, &out)) << s; + EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez + } + + // RFC1123, which only does whole seconds. + { + time_point<chrono::nanoseconds> out; + const std::string s = format(RFC1123_full, in, lax); + EXPECT_TRUE(parse(RFC1123_full, s, lax, &out)) << s; + EXPECT_EQ(in, out); // RFC1123_full includes %z + } + +#if defined(_WIN32) || defined(_WIN64) + // Initial investigations indicate the %c does not roundtrip on Windows. + // TODO: Figure out what is going on here (perhaps a locale problem). +#else + // Even though we don't know what %c will produce, it should roundtrip, + // but only in the 0-offset timezone. + { + time_point<chrono::nanoseconds> out; + time_zone utc = utc_time_zone(); + const std::string s = format("%c", in, utc); + EXPECT_TRUE(parse("%c", s, utc, &out)) << s; + EXPECT_EQ(in, out); + } +#endif +} + +TEST(FormatParse, RoundTripDistantFuture) { + const time_zone utc = utc_time_zone(); + const time_point<cctz::seconds> in = time_point<cctz::seconds>::max(); + const std::string s = format(RFC3339_full, in, utc); + time_point<cctz::seconds> out; + EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s; + EXPECT_EQ(in, out); +} + +TEST(FormatParse, RoundTripDistantPast) { + const time_zone utc = utc_time_zone(); + const time_point<cctz::seconds> in = time_point<cctz::seconds>::min(); + const std::string s = format(RFC3339_full, in, utc); + time_point<cctz::seconds> out; + EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s; + EXPECT_EQ(in, out); +} + +} // namespace cctz diff --git a/contrib/libs/cctz/test/time_zone_lookup_test.cc b/contrib/libs/cctz/test/time_zone_lookup_test.cc index 991d7af1c8..bf816ac6e9 100644 --- a/contrib/libs/cctz/test/time_zone_lookup_test.cc +++ b/contrib/libs/cctz/test/time_zone_lookup_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// https://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,21 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "cctz/time_zone.h" +#include "cctz/time_zone.h" #include <chrono> #include <cstddef> -#include <cstdlib> +#include <cstdlib> #include <future> -#include <limits> +#include <limits> #include <string> #include <thread> #include <vector> -#include "cctz/civil_time.h" -#include "gtest/gtest.h" +#include "cctz/civil_time.h" +#include "gtest/gtest.h" -namespace chrono = std::chrono; +namespace chrono = std::chrono; namespace cctz { @@ -209,7 +209,7 @@ const char* const kTimeZoneNames[] = { "America/North_Dakota/Beulah", "America/North_Dakota/Center", "America/North_Dakota/New_Salem", - "America/Nuuk", + "America/Nuuk", "America/Ojinaga", "America/Panama", "America/Pangnirtung", @@ -336,7 +336,7 @@ const char* const kTimeZoneNames[] = { "Asia/Pontianak", "Asia/Pyongyang", "Asia/Qatar", - "Asia/Qostanay", + "Asia/Qostanay", "Asia/Qyzylorda", "Asia/Rangoon", "Asia/Riyadh", @@ -653,17 +653,17 @@ time_zone LoadZone(const std::string& name) { /* EXPECT_STREQ(zone, al.abbr); */ \ } while (0) -// These tests sometimes run on platforms that have zoneinfo data so old -// that the transition we are attempting to check does not exist, most -// notably Android emulators. Fortunately, AndroidZoneInfoSource supports -// time_zone::version() so, in cases where we've learned that it matters, -// we can make the check conditionally. -int VersionCmp(time_zone tz, const std::string& target) { - std::string version = tz.version(); - if (version.empty() && !target.empty()) return 1; // unknown > known - return version.compare(target); -} - +// These tests sometimes run on platforms that have zoneinfo data so old +// that the transition we are attempting to check does not exist, most +// notably Android emulators. Fortunately, AndroidZoneInfoSource supports +// time_zone::version() so, in cases where we've learned that it matters, +// we can make the check conditionally. +int VersionCmp(time_zone tz, const std::string& target) { + std::string version = tz.version(); + if (version.empty() && !target.empty()) return 1; // unknown > known + return version.compare(target); +} + } // namespace TEST(TimeZones, LoadZonesConcurrently) { @@ -684,7 +684,7 @@ TEST(TimeZones, LoadZonesConcurrently) { } }; - const std::size_t n_threads = 128; + const std::size_t n_threads = 128; std::vector<std::thread> threads; std::vector<std::set<std::string>> thread_failures(n_threads); for (std::size_t i = 0; i != n_threads; ++i) { @@ -699,19 +699,19 @@ TEST(TimeZones, LoadZonesConcurrently) { // Allow a small number of failures to account for skew between // the contents of kTimeZoneNames and the zoneinfo data source. -#if defined(__ANDROID__) - // Cater to the possibility of using an even older zoneinfo data - // source when running on Android, where it is difficult to override - // the bionic tzdata provided by the test environment. - const std::size_t max_failures = 20; -#else +#if defined(__ANDROID__) + // Cater to the possibility of using an even older zoneinfo data + // source when running on Android, where it is difficult to override + // the bionic tzdata provided by the test environment. + const std::size_t max_failures = 20; +#else const std::size_t max_failures = 3; -#endif +#endif std::set<std::string> failures; for (const auto& thread_failure : thread_failures) { failures.insert(thread_failure.begin(), thread_failure.end()); } - EXPECT_LE(failures.size(), max_failures) << testing::PrintToString(failures); + EXPECT_LE(failures.size(), max_failures) << testing::PrintToString(failures); } TEST(TimeZone, NamedTimeZones) { @@ -721,13 +721,13 @@ TEST(TimeZone, NamedTimeZones) { EXPECT_EQ("America/New_York", nyc.name()); const time_zone syd = LoadZone("Australia/Sydney"); EXPECT_EQ("Australia/Sydney", syd.name()); - const time_zone fixed0 = fixed_time_zone(cctz::seconds::zero()); + const time_zone fixed0 = fixed_time_zone(cctz::seconds::zero()); EXPECT_EQ("UTC", fixed0.name()); - const time_zone fixed_pos = fixed_time_zone( - chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45)); + const time_zone fixed_pos = fixed_time_zone( + chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45)); EXPECT_EQ("Fixed/UTC+03:25:45", fixed_pos.name()); - const time_zone fixed_neg = fixed_time_zone( - -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56))); + const time_zone fixed_neg = fixed_time_zone( + -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56))); EXPECT_EQ("Fixed/UTC-12:34:56", fixed_neg.name()); } @@ -737,34 +737,34 @@ TEST(TimeZone, Failures) { tz = LoadZone("America/Los_Angeles"); EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz)); - EXPECT_EQ(chrono::system_clock::from_time_t(0), + EXPECT_EQ(chrono::system_clock::from_time_t(0), convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC // Ensures that the load still fails on a subsequent attempt. tz = LoadZone("America/Los_Angeles"); EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz)); - EXPECT_EQ(chrono::system_clock::from_time_t(0), + EXPECT_EQ(chrono::system_clock::from_time_t(0), convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC // Loading an empty string timezone should fail. tz = LoadZone("America/Los_Angeles"); EXPECT_FALSE(load_time_zone("", &tz)); - EXPECT_EQ(chrono::system_clock::from_time_t(0), + EXPECT_EQ(chrono::system_clock::from_time_t(0), convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC } TEST(TimeZone, Equality) { - const time_zone a; - const time_zone b; + const time_zone a; + const time_zone b; EXPECT_EQ(a, b); EXPECT_EQ(a.name(), b.name()); - const time_zone implicit_utc; + const time_zone implicit_utc; const time_zone explicit_utc = utc_time_zone(); EXPECT_EQ(implicit_utc, explicit_utc); EXPECT_EQ(implicit_utc.name(), explicit_utc.name()); - const time_zone fixed_zero = fixed_time_zone(cctz::seconds::zero()); + const time_zone fixed_zero = fixed_time_zone(cctz::seconds::zero()); EXPECT_EQ(fixed_zero, LoadZone(fixed_zero.name())); EXPECT_EQ(fixed_zero, explicit_utc); @@ -772,25 +772,25 @@ TEST(TimeZone, Equality) { EXPECT_EQ(fixed_utc, LoadZone(fixed_utc.name())); EXPECT_EQ(fixed_utc, explicit_utc); - const time_zone fixed_pos = fixed_time_zone( - chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45)); + const time_zone fixed_pos = fixed_time_zone( + chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45)); EXPECT_EQ(fixed_pos, LoadZone(fixed_pos.name())); EXPECT_NE(fixed_pos, explicit_utc); - const time_zone fixed_neg = fixed_time_zone( - -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56))); + const time_zone fixed_neg = fixed_time_zone( + -(chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56))); EXPECT_EQ(fixed_neg, LoadZone(fixed_neg.name())); EXPECT_NE(fixed_neg, explicit_utc); - const time_zone fixed_lim = fixed_time_zone(chrono::hours(24)); + const time_zone fixed_lim = fixed_time_zone(chrono::hours(24)); EXPECT_EQ(fixed_lim, LoadZone(fixed_lim.name())); EXPECT_NE(fixed_lim, explicit_utc); - const time_zone fixed_ovfl = - fixed_time_zone(chrono::hours(24) + chrono::seconds(1)); + const time_zone fixed_ovfl = + fixed_time_zone(chrono::hours(24) + chrono::seconds(1)); EXPECT_EQ(fixed_ovfl, LoadZone(fixed_ovfl.name())); EXPECT_EQ(fixed_ovfl, explicit_utc); - EXPECT_EQ(fixed_time_zone(chrono::seconds(1)), - fixed_time_zone(chrono::seconds(1))); + EXPECT_EQ(fixed_time_zone(chrono::seconds(1)), + fixed_time_zone(chrono::seconds(1))); const time_zone local = local_time_zone(); EXPECT_EQ(local, LoadZone(local.name())); @@ -803,113 +803,113 @@ TEST(TimeZone, Equality) { TEST(StdChronoTimePoint, TimeTAlignment) { // Ensures that the Unix epoch and the system clock epoch are an integral // number of seconds apart. This simplifies conversions to/from time_t. - auto diff = chrono::system_clock::time_point() - - chrono::system_clock::from_time_t(0); - EXPECT_EQ(chrono::system_clock::time_point::duration::zero(), - diff % chrono::seconds(1)); + auto diff = chrono::system_clock::time_point() - + chrono::system_clock::from_time_t(0); + EXPECT_EQ(chrono::system_clock::time_point::duration::zero(), + diff % chrono::seconds(1)); } TEST(BreakTime, TimePointResolution) { const time_zone utc = utc_time_zone(); - const auto t0 = chrono::system_clock::from_time_t(0); + const auto t0 = chrono::system_clock::from_time_t(0); - ExpectTime(chrono::time_point_cast<chrono::nanoseconds>(t0), utc, + ExpectTime(chrono::time_point_cast<chrono::nanoseconds>(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::microseconds>(t0), utc, + ExpectTime(chrono::time_point_cast<chrono::microseconds>(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::milliseconds>(t0), utc, + ExpectTime(chrono::time_point_cast<chrono::milliseconds>(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::seconds>(t0), utc, + ExpectTime(chrono::time_point_cast<chrono::seconds>(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<cctz::seconds>(t0), utc, + ExpectTime(chrono::time_point_cast<cctz::seconds>(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::minutes>(t0), utc, + ExpectTime(chrono::time_point_cast<chrono::minutes>(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(chrono::time_point_cast<chrono::hours>(t0), utc, + ExpectTime(chrono::time_point_cast<chrono::hours>(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); } TEST(BreakTime, LocalTimeInUTC) { const time_zone tz = utc_time_zone(); - const auto tp = chrono::system_clock::from_time_t(0); + const auto tp = chrono::system_clock::from_time_t(0); ExpectTime(tp, tz, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz))); + EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimeInUTCUnaligned) { const time_zone tz = utc_time_zone(); - const auto tp = - chrono::system_clock::from_time_t(0) - chrono::milliseconds(500); + const auto tp = + chrono::system_clock::from_time_t(0) - chrono::milliseconds(500); ExpectTime(tp, tz, 1969, 12, 31, 23, 59, 59, 0, false, "UTC"); - EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); + EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimePosix) { // See IEEE Std 1003.1-1988 B.2.3 General Terms, Epoch. const time_zone tz = utc_time_zone(); - const auto tp = chrono::system_clock::from_time_t(536457599); + const auto tp = chrono::system_clock::from_time_t(536457599); ExpectTime(tp, tz, 1986, 12, 31, 23, 59, 59, 0, false, "UTC"); - EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); + EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); } TEST(TimeZoneImpl, LocalTimeInFixed) { - const cctz::seconds offset = - -(chrono::hours(8) + chrono::minutes(33) + chrono::seconds(47)); + const cctz::seconds offset = + -(chrono::hours(8) + chrono::minutes(33) + chrono::seconds(47)); const time_zone tz = fixed_time_zone(offset); - const auto tp = chrono::system_clock::from_time_t(0); + const auto tp = chrono::system_clock::from_time_t(0); ExpectTime(tp, tz, 1969, 12, 31, 15, 26, 13, offset.count(), false, - "-083347"); - EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); + "-083347"); + EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimeInNewYork) { const time_zone tz = LoadZone("America/New_York"); - const auto tp = chrono::system_clock::from_time_t(45); + const auto tp = chrono::system_clock::from_time_t(45); ExpectTime(tp, tz, 1969, 12, 31, 19, 0, 45, -5 * 60 * 60, false, "EST"); - EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); + EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimeInMTV) { const time_zone tz = LoadZone("America/Los_Angeles"); - const auto tp = chrono::system_clock::from_time_t(1380855729); + const auto tp = chrono::system_clock::from_time_t(1380855729); ExpectTime(tp, tz, 2013, 10, 3, 20, 2, 9, -7 * 60 * 60, true, "PDT"); - EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz))); + EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimeInSydney) { const time_zone tz = LoadZone("Australia/Sydney"); - const auto tp = chrono::system_clock::from_time_t(90); + const auto tp = chrono::system_clock::from_time_t(90); ExpectTime(tp, tz, 1970, 1, 1, 10, 1, 30, 10 * 60 * 60, false, "AEST"); - EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz))); + EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz))); } TEST(MakeTime, TimePointResolution) { const time_zone utc = utc_time_zone(); - const time_point<chrono::nanoseconds> tp_ns = + const time_point<chrono::nanoseconds> tp_ns = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); EXPECT_EQ("04:05", format("%M:%E*S", tp_ns, utc)); - const time_point<chrono::microseconds> tp_us = + const time_point<chrono::microseconds> tp_us = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); EXPECT_EQ("04:05", format("%M:%E*S", tp_us, utc)); - const time_point<chrono::milliseconds> tp_ms = + const time_point<chrono::milliseconds> tp_ms = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); EXPECT_EQ("04:05", format("%M:%E*S", tp_ms, utc)); - const time_point<chrono::seconds> tp_s = + const time_point<chrono::seconds> tp_s = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); EXPECT_EQ("04:05", format("%M:%E*S", tp_s, utc)); - const time_point<cctz::seconds> tp_s64 = + const time_point<cctz::seconds> tp_s64 = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); EXPECT_EQ("04:05", format("%M:%E*S", tp_s64, utc)); - // These next two require chrono::time_point_cast because the conversion - // from a resolution of seconds (the return value of convert()) to a - // coarser resolution requires an explicit cast. - const time_point<chrono::minutes> tp_m = - chrono::time_point_cast<chrono::minutes>( + // These next two require chrono::time_point_cast because the conversion + // from a resolution of seconds (the return value of convert()) to a + // coarser resolution requires an explicit cast. + const time_point<chrono::minutes> tp_m = + chrono::time_point_cast<chrono::minutes>( convert(civil_second(2015, 1, 2, 3, 4, 5), utc)); EXPECT_EQ("04:00", format("%M:%E*S", tp_m, utc)); - const time_point<chrono::hours> tp_h = - chrono::time_point_cast<chrono::hours>( + const time_point<chrono::hours> tp_h = + chrono::time_point_cast<chrono::hours>( convert(civil_second(2015, 1, 2, 3, 4, 5), utc)); EXPECT_EQ("00:00", format("%M:%E*S", tp_h, utc)); } @@ -917,7 +917,7 @@ TEST(MakeTime, TimePointResolution) { TEST(MakeTime, Normalization) { const time_zone tz = LoadZone("America/New_York"); const auto tp = convert(civil_second(2009, 2, 13, 18, 31, 30), tz); - EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp); + EXPECT_EQ(chrono::system_clock::from_time_t(1234567890), tp); // Now requests for the same time_point but with out-of-range fields. EXPECT_EQ(tp, convert(civil_second(2008, 14, 13, 18, 31, 30), tz)); // month @@ -927,253 +927,253 @@ TEST(MakeTime, Normalization) { EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 18, 30, 90), tz)); // second } -// NOTE: Run this with -ftrapv to detect overflow problems. +// NOTE: Run this with -ftrapv to detect overflow problems. TEST(MakeTime, SysSecondsLimits) { - const char RFC3339[] = "%Y-%m-%d%ET%H:%M:%S%Ez"; + const char RFC3339[] = "%Y-%m-%d%ET%H:%M:%S%Ez"; const time_zone utc = utc_time_zone(); - const time_zone east = fixed_time_zone(chrono::hours(14)); - const time_zone west = fixed_time_zone(-chrono::hours(14)); - time_point<cctz::seconds> tp; + const time_zone east = fixed_time_zone(chrono::hours(14)); + const time_zone west = fixed_time_zone(-chrono::hours(14)); + time_point<cctz::seconds> tp; - // Approach the maximal time_point<cctz::seconds> value from below. + // Approach the maximal time_point<cctz::seconds> value from below. tp = convert(civil_second(292277026596, 12, 4, 15, 30, 6), utc); EXPECT_EQ("292277026596-12-04T15:30:06+00:00", format(RFC3339, tp, utc)); tp = convert(civil_second(292277026596, 12, 4, 15, 30, 7), utc); EXPECT_EQ("292277026596-12-04T15:30:07+00:00", format(RFC3339, tp, utc)); - EXPECT_EQ(time_point<cctz::seconds>::max(), tp); + EXPECT_EQ(time_point<cctz::seconds>::max(), tp); tp = convert(civil_second(292277026596, 12, 4, 15, 30, 8), utc); - EXPECT_EQ(time_point<cctz::seconds>::max(), tp); + EXPECT_EQ(time_point<cctz::seconds>::max(), tp); tp = convert(civil_second::max(), utc); - EXPECT_EQ(time_point<cctz::seconds>::max(), tp); + EXPECT_EQ(time_point<cctz::seconds>::max(), tp); // Checks that we can also get the maximal value for a far-east zone. tp = convert(civil_second(292277026596, 12, 5, 5, 30, 7), east); EXPECT_EQ("292277026596-12-05T05:30:07+14:00", format(RFC3339, tp, east)); - EXPECT_EQ(time_point<cctz::seconds>::max(), tp); + EXPECT_EQ(time_point<cctz::seconds>::max(), tp); tp = convert(civil_second(292277026596, 12, 5, 5, 30, 8), east); - EXPECT_EQ(time_point<cctz::seconds>::max(), tp); + EXPECT_EQ(time_point<cctz::seconds>::max(), tp); tp = convert(civil_second::max(), east); - EXPECT_EQ(time_point<cctz::seconds>::max(), tp); + EXPECT_EQ(time_point<cctz::seconds>::max(), tp); // Checks that we can also get the maximal value for a far-west zone. tp = convert(civil_second(292277026596, 12, 4, 1, 30, 7), west); EXPECT_EQ("292277026596-12-04T01:30:07-14:00", format(RFC3339, tp, west)); - EXPECT_EQ(time_point<cctz::seconds>::max(), tp); + EXPECT_EQ(time_point<cctz::seconds>::max(), tp); tp = convert(civil_second(292277026596, 12, 4, 7, 30, 8), west); - EXPECT_EQ(time_point<cctz::seconds>::max(), tp); + EXPECT_EQ(time_point<cctz::seconds>::max(), tp); tp = convert(civil_second::max(), west); - EXPECT_EQ(time_point<cctz::seconds>::max(), tp); + EXPECT_EQ(time_point<cctz::seconds>::max(), tp); - // Approach the minimal time_point<cctz::seconds> value from above. + // Approach the minimal time_point<cctz::seconds> value from above. tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 53), utc); EXPECT_EQ("-292277022657-01-27T08:29:53+00:00", format(RFC3339, tp, utc)); tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 52), utc); EXPECT_EQ("-292277022657-01-27T08:29:52+00:00", format(RFC3339, tp, utc)); - EXPECT_EQ(time_point<cctz::seconds>::min(), tp); + EXPECT_EQ(time_point<cctz::seconds>::min(), tp); tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 51), utc); - EXPECT_EQ(time_point<cctz::seconds>::min(), tp); + EXPECT_EQ(time_point<cctz::seconds>::min(), tp); tp = convert(civil_second::min(), utc); - EXPECT_EQ(time_point<cctz::seconds>::min(), tp); + EXPECT_EQ(time_point<cctz::seconds>::min(), tp); // Checks that we can also get the minimal value for a far-east zone. tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 52), east); EXPECT_EQ("-292277022657-01-27T22:29:52+14:00", format(RFC3339, tp, east)); - EXPECT_EQ(time_point<cctz::seconds>::min(), tp); + EXPECT_EQ(time_point<cctz::seconds>::min(), tp); tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 51), east); - EXPECT_EQ(time_point<cctz::seconds>::min(), tp); + EXPECT_EQ(time_point<cctz::seconds>::min(), tp); tp = convert(civil_second::min(), east); - EXPECT_EQ(time_point<cctz::seconds>::min(), tp); + EXPECT_EQ(time_point<cctz::seconds>::min(), tp); // Checks that we can also get the minimal value for a far-west zone. tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 52), west); EXPECT_EQ("-292277022657-01-26T18:29:52-14:00", format(RFC3339, tp, west)); - EXPECT_EQ(time_point<cctz::seconds>::min(), tp); + EXPECT_EQ(time_point<cctz::seconds>::min(), tp); tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 51), west); - EXPECT_EQ(time_point<cctz::seconds>::min(), tp); + EXPECT_EQ(time_point<cctz::seconds>::min(), tp); tp = convert(civil_second::min(), west); - EXPECT_EQ(time_point<cctz::seconds>::min(), tp); - - // Some similar checks for the "libc" time-zone implementation. - if (sizeof(std::time_t) >= 8) { - // Checks that "tm_year + 1900", as used by the "libc" implementation, - // can produce year values beyond the range on an int without overflow. -#if defined(_WIN32) || defined(_WIN64) - // localtime_s() and gmtime_s() don't believe in years outside [1970:3000]. -#else - const time_zone cut = LoadZone("libc:UTC"); - const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900; - tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut); -#if defined(__FreeBSD__) || defined(__OpenBSD__) - // The BSD gmtime_r() fails on extreme positive tm_year values. -#else - EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, cut)); -#endif - const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900; - tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut); - EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut)); -#endif - } -} - -TEST(MakeTime, LocalTimeLibC) { - // Checks that cctz and libc agree on transition points in [1970:2037]. - // - // We limit this test case to environments where: - // 1) we know how to change the time zone used by localtime()/mktime(), - // 2) cctz and localtime()/mktime() will use similar-enough tzdata, and - // 3) we have some idea about how mktime() behaves during transitions. -#if defined(__linux__) && !defined(__ANDROID__) && defined(CCTZ_TEST_LIBC_LOCALTIME) - const char* const ep = getenv("TZ"); - std::string tz_name = (ep != nullptr) ? ep : ""; - for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) { - ASSERT_EQ(0, setenv("TZ", *np, 1)); // change what "localtime" means - const auto zi = local_time_zone(); - const auto lc = LoadZone("libc:localtime"); - time_zone::civil_transition transition; - for (auto tp = zi.lookup(civil_second()).trans; - zi.next_transition(tp, &transition); - tp = zi.lookup(transition.to).trans) { - const auto fcl = zi.lookup(transition.from); - const auto tcl = zi.lookup(transition.to); - civil_second cs; // compare cs in zi and lc - if (fcl.kind == time_zone::civil_lookup::UNIQUE) { - if (tcl.kind == time_zone::civil_lookup::UNIQUE) { - // Both unique; must be an is_dst or abbr change. - ASSERT_EQ(transition.from, transition.to); - const auto trans = fcl.trans; - const auto tal = zi.lookup(trans); - const auto tprev = trans - cctz::seconds(1); - const auto pal = zi.lookup(tprev); - if (pal.is_dst == tal.is_dst) { - ASSERT_STRNE(pal.abbr, tal.abbr); - } - continue; - } - ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind); - cs = transition.to; - } else { - ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind); - ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind); - cs = transition.from; - } - if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t) - const auto cl_zi = zi.lookup(cs); - if (zi.lookup(cl_zi.pre).is_dst == zi.lookup(cl_zi.post).is_dst) { - // The "libc" implementation cannot correctly classify transitions - // that don't change the "tm_isdst" flag. In Europe/Volgograd, for - // example, there is a SKIPPED transition from +03 to +04 with dst=F - // on both sides ... - // 1540681199 = 2018-10-28 01:59:59 +03:00:00 [dst=F off=10800] - // 1540681200 = 2018-10-28 03:00:00 +04:00:00 [dst=F off=14400] - // but std::mktime(2018-10-28 02:00:00, tm_isdst=0) fails, unlike, - // say, the similar Europe/Chisinau transition from +02 to +03 ... - // 1521935999 = 2018-03-25 01:59:59 +02:00:00 [dst=F off=7200] - // 1521936000 = 2018-03-25 03:00:00 +03:00:00 [dst=T off=10800] - // where std::mktime(2018-03-25 02:00:00, tm_isdst=0) succeeds and - // returns 1521936000. - continue; - } - if (cs == civil_second(2037, 10, 4, 2, 0, 0)) { - const std::string tzname = *np; - if (tzname == "Africa/Casablanca" || tzname == "Africa/El_Aaiun") { - // The "libc" implementation gets this transition wrong (at least - // until 2018g when it was removed), returning an offset of 3600 - // instead of 0. TODO: Revert this when 2018g is ubiquitous. - continue; - } - } - const auto cl_lc = lc.lookup(cs); - SCOPED_TRACE(testing::Message() << "For " << cs << " in " << *np); - EXPECT_EQ(cl_zi.kind, cl_lc.kind); - EXPECT_EQ(cl_zi.pre, cl_lc.pre); - EXPECT_EQ(cl_zi.trans, cl_lc.trans); - EXPECT_EQ(cl_zi.post, cl_lc.post); - } - } - if (ep == nullptr) { - ASSERT_EQ(0, unsetenv("TZ")); - } else { - ASSERT_EQ(0, setenv("TZ", tz_name.c_str(), 1)); - } -#endif -} - -TEST(NextTransition, UTC) { - const auto tz = utc_time_zone(); - time_zone::civil_transition trans; - - auto tp = time_point<cctz::seconds>::min(); - EXPECT_FALSE(tz.next_transition(tp, &trans)); - - tp = time_point<cctz::seconds>::max(); - EXPECT_FALSE(tz.next_transition(tp, &trans)); -} - -TEST(PrevTransition, UTC) { - const auto tz = utc_time_zone(); - time_zone::civil_transition trans; - - auto tp = time_point<cctz::seconds>::max(); - EXPECT_FALSE(tz.prev_transition(tp, &trans)); - - tp = time_point<cctz::seconds>::min(); - EXPECT_FALSE(tz.prev_transition(tp, &trans)); -} - -TEST(NextTransition, AmericaNewYork) { - const auto tz = LoadZone("America/New_York"); - time_zone::civil_transition trans; - - auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz); - EXPECT_TRUE(tz.next_transition(tp, &trans)); - EXPECT_EQ(civil_second(2018, 11, 4, 2, 0, 0), trans.from); - EXPECT_EQ(civil_second(2018, 11, 4, 1, 0, 0), trans.to); - - tp = time_point<cctz::seconds>::max(); - EXPECT_FALSE(tz.next_transition(tp, &trans)); - - tp = time_point<cctz::seconds>::min(); - EXPECT_TRUE(tz.next_transition(tp, &trans)); - if (trans.from == civil_second(1918, 3, 31, 2, 0, 0)) { - // It looks like the tzdata is only 32 bit (probably macOS), - // which bottoms out at 1901-12-13T20:45:52+00:00. - EXPECT_EQ(civil_second(1918, 3, 31, 3, 0, 0), trans.to); - } else { - EXPECT_EQ(civil_second(1883, 11, 18, 12, 3, 58), trans.from); - EXPECT_EQ(civil_second(1883, 11, 18, 12, 0, 0), trans.to); - } -} - -TEST(PrevTransition, AmericaNewYork) { - const auto tz = LoadZone("America/New_York"); - time_zone::civil_transition trans; - - auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz); - EXPECT_TRUE(tz.prev_transition(tp, &trans)); - EXPECT_EQ(civil_second(2018, 3, 11, 2, 0, 0), trans.from); - EXPECT_EQ(civil_second(2018, 3, 11, 3, 0, 0), trans.to); - - tp = time_point<cctz::seconds>::min(); - EXPECT_FALSE(tz.prev_transition(tp, &trans)); - - tp = time_point<cctz::seconds>::max(); - EXPECT_TRUE(tz.prev_transition(tp, &trans)); - // We have a transition but we don't know which one. + EXPECT_EQ(time_point<cctz::seconds>::min(), tp); + + // Some similar checks for the "libc" time-zone implementation. + if (sizeof(std::time_t) >= 8) { + // Checks that "tm_year + 1900", as used by the "libc" implementation, + // can produce year values beyond the range on an int without overflow. +#if defined(_WIN32) || defined(_WIN64) + // localtime_s() and gmtime_s() don't believe in years outside [1970:3000]. +#else + const time_zone cut = LoadZone("libc:UTC"); + const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900; + tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut); +#if defined(__FreeBSD__) || defined(__OpenBSD__) + // The BSD gmtime_r() fails on extreme positive tm_year values. +#else + EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, cut)); +#endif + const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900; + tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut); + EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut)); +#endif + } } +TEST(MakeTime, LocalTimeLibC) { + // Checks that cctz and libc agree on transition points in [1970:2037]. + // + // We limit this test case to environments where: + // 1) we know how to change the time zone used by localtime()/mktime(), + // 2) cctz and localtime()/mktime() will use similar-enough tzdata, and + // 3) we have some idea about how mktime() behaves during transitions. +#if defined(__linux__) && !defined(__ANDROID__) && defined(CCTZ_TEST_LIBC_LOCALTIME) + const char* const ep = getenv("TZ"); + std::string tz_name = (ep != nullptr) ? ep : ""; + for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) { + ASSERT_EQ(0, setenv("TZ", *np, 1)); // change what "localtime" means + const auto zi = local_time_zone(); + const auto lc = LoadZone("libc:localtime"); + time_zone::civil_transition transition; + for (auto tp = zi.lookup(civil_second()).trans; + zi.next_transition(tp, &transition); + tp = zi.lookup(transition.to).trans) { + const auto fcl = zi.lookup(transition.from); + const auto tcl = zi.lookup(transition.to); + civil_second cs; // compare cs in zi and lc + if (fcl.kind == time_zone::civil_lookup::UNIQUE) { + if (tcl.kind == time_zone::civil_lookup::UNIQUE) { + // Both unique; must be an is_dst or abbr change. + ASSERT_EQ(transition.from, transition.to); + const auto trans = fcl.trans; + const auto tal = zi.lookup(trans); + const auto tprev = trans - cctz::seconds(1); + const auto pal = zi.lookup(tprev); + if (pal.is_dst == tal.is_dst) { + ASSERT_STRNE(pal.abbr, tal.abbr); + } + continue; + } + ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind); + cs = transition.to; + } else { + ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind); + ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind); + cs = transition.from; + } + if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t) + const auto cl_zi = zi.lookup(cs); + if (zi.lookup(cl_zi.pre).is_dst == zi.lookup(cl_zi.post).is_dst) { + // The "libc" implementation cannot correctly classify transitions + // that don't change the "tm_isdst" flag. In Europe/Volgograd, for + // example, there is a SKIPPED transition from +03 to +04 with dst=F + // on both sides ... + // 1540681199 = 2018-10-28 01:59:59 +03:00:00 [dst=F off=10800] + // 1540681200 = 2018-10-28 03:00:00 +04:00:00 [dst=F off=14400] + // but std::mktime(2018-10-28 02:00:00, tm_isdst=0) fails, unlike, + // say, the similar Europe/Chisinau transition from +02 to +03 ... + // 1521935999 = 2018-03-25 01:59:59 +02:00:00 [dst=F off=7200] + // 1521936000 = 2018-03-25 03:00:00 +03:00:00 [dst=T off=10800] + // where std::mktime(2018-03-25 02:00:00, tm_isdst=0) succeeds and + // returns 1521936000. + continue; + } + if (cs == civil_second(2037, 10, 4, 2, 0, 0)) { + const std::string tzname = *np; + if (tzname == "Africa/Casablanca" || tzname == "Africa/El_Aaiun") { + // The "libc" implementation gets this transition wrong (at least + // until 2018g when it was removed), returning an offset of 3600 + // instead of 0. TODO: Revert this when 2018g is ubiquitous. + continue; + } + } + const auto cl_lc = lc.lookup(cs); + SCOPED_TRACE(testing::Message() << "For " << cs << " in " << *np); + EXPECT_EQ(cl_zi.kind, cl_lc.kind); + EXPECT_EQ(cl_zi.pre, cl_lc.pre); + EXPECT_EQ(cl_zi.trans, cl_lc.trans); + EXPECT_EQ(cl_zi.post, cl_lc.post); + } + } + if (ep == nullptr) { + ASSERT_EQ(0, unsetenv("TZ")); + } else { + ASSERT_EQ(0, setenv("TZ", tz_name.c_str(), 1)); + } +#endif +} + +TEST(NextTransition, UTC) { + const auto tz = utc_time_zone(); + time_zone::civil_transition trans; + + auto tp = time_point<cctz::seconds>::min(); + EXPECT_FALSE(tz.next_transition(tp, &trans)); + + tp = time_point<cctz::seconds>::max(); + EXPECT_FALSE(tz.next_transition(tp, &trans)); +} + +TEST(PrevTransition, UTC) { + const auto tz = utc_time_zone(); + time_zone::civil_transition trans; + + auto tp = time_point<cctz::seconds>::max(); + EXPECT_FALSE(tz.prev_transition(tp, &trans)); + + tp = time_point<cctz::seconds>::min(); + EXPECT_FALSE(tz.prev_transition(tp, &trans)); +} + +TEST(NextTransition, AmericaNewYork) { + const auto tz = LoadZone("America/New_York"); + time_zone::civil_transition trans; + + auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz); + EXPECT_TRUE(tz.next_transition(tp, &trans)); + EXPECT_EQ(civil_second(2018, 11, 4, 2, 0, 0), trans.from); + EXPECT_EQ(civil_second(2018, 11, 4, 1, 0, 0), trans.to); + + tp = time_point<cctz::seconds>::max(); + EXPECT_FALSE(tz.next_transition(tp, &trans)); + + tp = time_point<cctz::seconds>::min(); + EXPECT_TRUE(tz.next_transition(tp, &trans)); + if (trans.from == civil_second(1918, 3, 31, 2, 0, 0)) { + // It looks like the tzdata is only 32 bit (probably macOS), + // which bottoms out at 1901-12-13T20:45:52+00:00. + EXPECT_EQ(civil_second(1918, 3, 31, 3, 0, 0), trans.to); + } else { + EXPECT_EQ(civil_second(1883, 11, 18, 12, 3, 58), trans.from); + EXPECT_EQ(civil_second(1883, 11, 18, 12, 0, 0), trans.to); + } +} + +TEST(PrevTransition, AmericaNewYork) { + const auto tz = LoadZone("America/New_York"); + time_zone::civil_transition trans; + + auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz); + EXPECT_TRUE(tz.prev_transition(tp, &trans)); + EXPECT_EQ(civil_second(2018, 3, 11, 2, 0, 0), trans.from); + EXPECT_EQ(civil_second(2018, 3, 11, 3, 0, 0), trans.to); + + tp = time_point<cctz::seconds>::min(); + EXPECT_FALSE(tz.prev_transition(tp, &trans)); + + tp = time_point<cctz::seconds>::max(); + EXPECT_TRUE(tz.prev_transition(tp, &trans)); + // We have a transition but we don't know which one. +} + TEST(TimeZoneEdgeCase, AmericaNewYork) { const time_zone tz = LoadZone("America/New_York"); // Spring 1:59:59 -> 3:00:00 auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz); ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -5 * 3600, false, "EST"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -4 * 3600, true, "EDT"); // Fall 1:59:59 -> 1:00:00 tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz); ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -4 * 3600, true, "EDT"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -5 * 3600, false, "EST"); } @@ -1183,13 +1183,13 @@ TEST(TimeZoneEdgeCase, AmericaLosAngeles) { // Spring 1:59:59 -> 3:00:00 auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz); ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -8 * 3600, false, "PST"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -7 * 3600, true, "PDT"); // Fall 1:59:59 -> 1:00:00 tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz); ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, true, "PDT"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -8 * 3600, false, "PST"); } @@ -1199,13 +1199,13 @@ TEST(TimeZoneEdgeCase, ArizonaNoTransition) { // No transition in Spring. auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz); ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -7 * 3600, false, "MST"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 3, 10, 2, 0, 0, -7 * 3600, false, "MST"); // No transition in Fall. tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz); ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, false, "MST"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 11, 3, 2, 0, 0, -7 * 3600, false, "MST"); } @@ -1218,7 +1218,7 @@ TEST(TimeZoneEdgeCase, AsiaKathmandu) { // 504901800 == Wed, 1 Jan 1986 00:15:00 +0545 (+0545) auto tp = convert(civil_second(1985, 12, 31, 23, 59, 59), tz); ExpectTime(tp, tz, 1985, 12, 31, 23, 59, 59, 5.5 * 3600, false, "+0530"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 1986, 1, 1, 0, 15, 0, 5.75 * 3600, false, "+0545"); } @@ -1231,14 +1231,14 @@ TEST(TimeZoneEdgeCase, PacificChatham) { // 1365256800 == Sun, 7 Apr 2013 02:45:00 +1245 (+1245) auto tp = convert(civil_second(2013, 4, 7, 3, 44, 59), tz); ExpectTime(tp, tz, 2013, 4, 7, 3, 44, 59, 13.75 * 3600, true, "+1345"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 4, 7, 2, 45, 0, 12.75 * 3600, false, "+1245"); // 1380376799 == Sun, 29 Sep 2013 02:44:59 +1245 (+1245) // 1380376800 == Sun, 29 Sep 2013 03:45:00 +1345 (+1345) tp = convert(civil_second(2013, 9, 29, 2, 44, 59), tz); ExpectTime(tp, tz, 2013, 9, 29, 2, 44, 59, 12.75 * 3600, false, "+1245"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 9, 29, 3, 45, 0, 13.75 * 3600, true, "+1345"); } @@ -1251,14 +1251,14 @@ TEST(TimeZoneEdgeCase, AustraliaLordHowe) { // 1365260400 == Sun, 7 Apr 2013 01:30:00 +1030 (+1030) auto tp = convert(civil_second(2013, 4, 7, 1, 59, 59), tz); ExpectTime(tp, tz, 2013, 4, 7, 1, 59, 59, 11 * 3600, true, "+11"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 4, 7, 1, 30, 0, 10.5 * 3600, false, "+1030"); // 1380986999 == Sun, 6 Oct 2013 01:59:59 +1030 (+1030) // 1380987000 == Sun, 6 Oct 2013 02:30:00 +1100 (+11) tp = convert(civil_second(2013, 10, 6, 1, 59, 59), tz); ExpectTime(tp, tz, 2013, 10, 6, 1, 59, 59, 10.5 * 3600, false, "+1030"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2013, 10, 6, 2, 30, 0, 11 * 3600, true, "+11"); } @@ -1275,40 +1275,40 @@ TEST(TimeZoneEdgeCase, PacificApia) { // 1325239200 == Sat, 31 Dec 2011 00:00:00 +1400 (+14) auto tp = convert(civil_second(2011, 12, 29, 23, 59, 59), tz); ExpectTime(tp, tz, 2011, 12, 29, 23, 59, 59, -10 * 3600, true, "-10"); - EXPECT_EQ(363, get_yearday(convert(tp, tz))); - tp += cctz::seconds(1); + EXPECT_EQ(363, get_yearday(convert(tp, tz))); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2011, 12, 31, 0, 0, 0, 14 * 3600, true, "+14"); - EXPECT_EQ(365, get_yearday(convert(tp, tz))); + EXPECT_EQ(365, get_yearday(convert(tp, tz))); } TEST(TimeZoneEdgeCase, AfricaCairo) { const time_zone tz = LoadZone("Africa/Cairo"); - if (VersionCmp(tz, "2014c") >= 0) { - // An interesting case of midnight not existing. - // - // 1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET) - // 1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST) - auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz); - ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET"); - tp += cctz::seconds(1); - ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST"); - } + if (VersionCmp(tz, "2014c") >= 0) { + // An interesting case of midnight not existing. + // + // 1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET) + // 1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST) + auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz); + ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET"); + tp += cctz::seconds(1); + ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST"); + } } TEST(TimeZoneEdgeCase, AfricaMonrovia) { const time_zone tz = LoadZone("Africa/Monrovia"); - if (VersionCmp(tz, "2017b") >= 0) { - // Strange offset change -00:44:30 -> +00:00:00 (non-DST) - // - // 63593069 == Thu, 6 Jan 1972 23:59:59 -0044 (MMT) - // 63593070 == Fri, 7 Jan 1972 00:44:30 +0000 (GMT) - auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz); - ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT"); - tp += cctz::seconds(1); - ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT"); - } + if (VersionCmp(tz, "2017b") >= 0) { + // Strange offset change -00:44:30 -> +00:00:00 (non-DST) + // + // 63593069 == Thu, 6 Jan 1972 23:59:59 -0044 (MMT) + // 63593070 == Fri, 7 Jan 1972 00:44:30 +0000 (GMT) + auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz); + ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT"); + tp += cctz::seconds(1); + ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT"); + } } TEST(TimeZoneEdgeCase, AmericaJamaica) { @@ -1320,31 +1320,31 @@ TEST(TimeZoneEdgeCase, AmericaJamaica) { const time_zone tz = LoadZone("America/Jamaica"); // Before the first transition. - if (!tz.version().empty() && VersionCmp(tz, "2018d") >= 0) { - // We avoid the expectations on the -18430 offset below unless we are - // certain we have commit 907241e (Fix off-by-1 error for Jamaica and - // T&C before 1913) from 2018d. TODO: Remove the "version() not empty" - // part when 2018d is generally available from /usr/share/zoneinfo. - auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz); - ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false, - tz.lookup(tp).abbr); - - // Over the first (abbreviation-change only) transition. - // -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT) - // -2524503169 == Wed, 1 Jan 1890 00:00:00 -0507 (KMT) - tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz); - ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false, - tz.lookup(tp).abbr); - tp += cctz::seconds(1); - ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT"); - } + if (!tz.version().empty() && VersionCmp(tz, "2018d") >= 0) { + // We avoid the expectations on the -18430 offset below unless we are + // certain we have commit 907241e (Fix off-by-1 error for Jamaica and + // T&C before 1913) from 2018d. TODO: Remove the "version() not empty" + // part when 2018d is generally available from /usr/share/zoneinfo. + auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz); + ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false, + tz.lookup(tp).abbr); + + // Over the first (abbreviation-change only) transition. + // -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT) + // -2524503169 == Wed, 1 Jan 1890 00:00:00 -0507 (KMT) + tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz); + ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false, + tz.lookup(tp).abbr); + tp += cctz::seconds(1); + ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT"); + } // Over the last (DST) transition. // 436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT) // 436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST) - auto tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz); + auto tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz); ExpectTime(tp, tz, 1983, 10, 30, 1, 59, 59, -4 * 3600, true, "EDT"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST"); // After the last transition. @@ -1365,7 +1365,7 @@ TEST(TimeZoneEdgeCase, WET) { // 228877200 == Sun, 3 Apr 1977 02:00:00 +0100 (WEST) tp = convert(civil_second(1977, 4, 3, 0, 59, 59), tz); ExpectTime(tp, tz, 1977, 4, 3, 0, 59, 59, 0, false, "WET"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 1977, 4, 3, 2, 0, 0, 1 * 3600, true, "WEST"); // A non-existent time within the first transition. @@ -1387,12 +1387,12 @@ TEST(TimeZoneEdgeCase, FixedOffsets) { const time_zone gmtm5 = LoadZone("Etc/GMT+5"); // -0500 auto tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtm5); ExpectTime(tp, gmtm5, 1970, 1, 1, 0, 0, 0, -5 * 3600, false, "-05"); - EXPECT_EQ(chrono::system_clock::from_time_t(5 * 3600), tp); + EXPECT_EQ(chrono::system_clock::from_time_t(5 * 3600), tp); const time_zone gmtp5 = LoadZone("Etc/GMT-5"); // +0500 tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtp5); ExpectTime(tp, gmtp5, 1970, 1, 1, 0, 0, 0, 5 * 3600, false, "+05"); - EXPECT_EQ(chrono::system_clock::from_time_t(-5 * 3600), tp); + EXPECT_EQ(chrono::system_clock::from_time_t(-5 * 3600), tp); } TEST(TimeZoneEdgeCase, NegativeYear) { @@ -1400,10 +1400,10 @@ TEST(TimeZoneEdgeCase, NegativeYear) { const time_zone tz = utc_time_zone(); auto tp = convert(civil_second(0, 1, 1, 0, 0, 0), tz); ExpectTime(tp, tz, 0, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC"); - EXPECT_EQ(weekday::saturday, get_weekday(convert(tp, tz))); - tp -= cctz::seconds(1); + EXPECT_EQ(weekday::saturday, get_weekday(convert(tp, tz))); + tp -= cctz::seconds(1); ExpectTime(tp, tz, -1, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC"); - EXPECT_EQ(weekday::friday, get_weekday(convert(tp, tz))); + EXPECT_EQ(weekday::friday, get_weekday(convert(tp, tz))); } TEST(TimeZoneEdgeCase, UTC32bitLimit) { @@ -1415,7 +1415,7 @@ TEST(TimeZoneEdgeCase, UTC32bitLimit) { // 2147483648 == Tue, 19 Jan 2038 03:14:08 +0000 (UTC) auto tp = convert(civil_second(2038, 1, 19, 3, 14, 7), tz); ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 7, 0 * 3600, false, "UTC"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 8, 0 * 3600, false, "UTC"); } @@ -1428,7 +1428,7 @@ TEST(TimeZoneEdgeCase, UTC5DigitYear) { // 253402300800 == Sat, 1 Jan 1000 00:00:00 +0000 (UTC) auto tp = convert(civil_second(9999, 12, 31, 23, 59, 59), tz); ExpectTime(tp, tz, 9999, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC"); - tp += cctz::seconds(1); + tp += cctz::seconds(1); ExpectTime(tp, tz, 10000, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC"); } diff --git a/contrib/libs/cctz/test/ya.make b/contrib/libs/cctz/test/ya.make index 8815f9769d..57b9be0018 100644 --- a/contrib/libs/cctz/test/ya.make +++ b/contrib/libs/cctz/test/ya.make @@ -1,4 +1,4 @@ -GTEST() +GTEST() LICENSE(Apache-2.0) @@ -10,26 +10,26 @@ OWNER( ) PEERDIR( - contrib/libs/cctz + contrib/libs/cctz contrib/libs/cctz/tzdata ) ADDINCL( contrib/libs/cctz/include ) - -IF (NOT AUTOCHECK) - # We do not set TZDIR to a stable data source, so - # LoadZone("libc:localtime") is inconsistent and makes - # LocalTimeLibC test fail on distbuild. + +IF (NOT AUTOCHECK) + # We do not set TZDIR to a stable data source, so + # LoadZone("libc:localtime") is inconsistent and makes + # LocalTimeLibC test fail on distbuild. CFLAGS( -DCCTZ_TEST_LIBC_LOCALTIME ) -ENDIF() - +ENDIF() + SRCS( civil_time_test.cc - time_zone_format_test.cc + time_zone_format_test.cc time_zone_lookup_test.cc ) |