From fcb104594b0bb4b8ac306cb2f55ecdad40974683 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 4 Dec 2018 11:01:12 -0800 Subject: Creation of LTS branch "lts_2018_12_18" - 44b0fafc62d9b8f192e8180cbe9c4b806b339d57 Export of internal Abseil changes. by Abseil Team - 926bfeb9fff223429c12224b7514243886323e8d Export of internal Abseil changes. by Abseil Team - 13327debebc5c2d1d4991b69fe50450e340e50e4 Export of internal Abseil changes. by Abseil Team - 3088e76c597e068479e82508b1770a7ad0c806b6 Export of internal Abseil changes. by Abseil Team - f6ae816808cd913e0e2b3e2af14f328fa1071af0 Export of internal Abseil changes. by Abseil Team - a06c4a1d9093137b7217a5aaba8920d62e835dc0 Export of internal Abseil changes. by Abseil Team - 7b46e1d31a6b08b1c6da2a13e7b151a20446fa07 Export of internal Abseil changes. by Abseil Team - 070f6e47b33a2909d039e620c873204f78809492 Export of internal Abseil changes. by Abseil Team - 7990fd459e9339467814ddb95000c87cb1e4d945 Export of internal Abseil changes. by Abseil Team - f95179062eb65ce40895cc76f1398cce25394369 Export of internal Abseil changes. by Abseil Team - cc8dcd307b76a575d2e3e0958a4fe4c7193c2f68 Export of internal Abseil changes. by Abseil Team - a705aa78dc76fc5c79d501e61dcc077eca68a8a4 Merge pull request #194 from Mizux/windows by Xiaoyi Zhang - a4c3ffff11eec0ee45742f915c255e9f870b7e0f Export of internal Abseil changes. by Abseil Team - 01174578651b73021d9b8c3820f6fea707dacdf0 Merge pull request #201 from ccawley2011/fix-byteswap by Matt Calabrese <38107210+mattcalabrese-google@users.noreply.github.com> - f86f9413856b65afdd61fea938d684b8ab73115a Export of internal Abseil changes. by Abseil Team - 94c298e2a0ae409e283cab96c954a685bd865a70 Export of internal Abseil changes. by Abseil Team - 0884a6a04e4497d11b1b398cc0e422b118bf977a Export of internal Abseil changes. by Abseil Team - c16d5557cd05119b5b7b1318ef778ebe3195b4a1 Export of internal Abseil changes. by Abseil Team - 45221ccc4ed643e4209b0cc5798e97203f108fa8 Export of internal Abseil changes. by Abseil Team - 2019e17a520575ab365b2b5134d71068182c70b8 Export of internal Abseil changes. by Abseil Team - 5b70a8910b2e6fb0ce5193a41873139a126d2f7f Export of internal Abseil changes. by Abseil Team - a00bdd176d66ef0b417d9576052a19091fbdf891 Export of internal Abseil changes. by Abseil Team - f340f773edab951656b19b6f1a77c964a78ec4c2 Export of internal Abseil changes. by Abseil Team - 445998d7ac4e5d3c50411d377e3b50e960d2d6c2 Export of internal Abseil changes. by Abseil Team - e821380d69a549dc64900693942789d21aa4df5e Export of internal Abseil changes. by Abseil Team - f21d187b80e3b7f08fb279775ea9c8b48c636030 Export of internal Abseil changes. by Abseil Team - 5441bbe1db5d0f2ca24b5b60166367b0966790af Fix code snippet in comment (#174) by Loo Rong Jie - 5aae0cffae8ffaacab965756169b34e511b353df Fix CMake build (#173) by Stephan Dollberg - 48cd2c3f351ff188bc85684b84a91b6e6d17d896 Export of internal Abseil changes. by Abseil Team - e291c279e458761e77a69b09b129d3d1e81f1e80 Export of internal Abseil changes. by Abseil Team - e01d95528ea2137a4a27a88d1f57c6cb260aafed Export of internal Abseil changes. by Abseil Team - 8ff1374008259719b54a8cb128ef951c02da164c Export of internal Abseil changes. by Abseil Team - 02451914b9ad5320f81f56a89f3eef1f8683227c Export of internal Abseil changes. by Abseil Team - 921fd5cf02ec0d665439a790148d59faa7d4a72c Merge pull request #166 from rongjiecomputer/cmake-test by Gennadiy Civil - fb462224c058487763f263b7995d70efd0242c17 Export of internal Abseil changes. by Abseil Team - c075ad321696fa5072e097f0a51e4fe76a6fe13e Export of internal Abseil changes. by Abseil Team - 0f4bc966754ec6cd28d5f03467d56f1efdc598e3 Export of internal Abseil changes. by Abseil Team - 6c7e5ffc43decd92f7bdfc510ad8a245a20b6dea Export of internal Abseil changes. by Abseil Team - d6df769173bf0263489f98874b93034db0e479a2 Export of internal Abseil changes. by Abseil Team - 28080f5f050c9530aa9f2b39c60d8217038d64ff Export of internal Abseil changes. by Abseil Team - 9c987f429bba32fb4446280fd3b91e2472d71d4d Export of internal Abseil changes. by Abseil Team - 5e7d459eeca7bc53deab0ee9634601386b53d7c0 Export of internal Abseil changes. by Abseil Team - bed5bd6e185c7e0311f3a1f2dab4c96083dac636 Export of internal Abseil changes. by Abseil Team - fefc83638fb69395d259ed245699310610429064 Export of internal Abseil changes. by Abseil Team - d8cfe9f2a77fbee02c09642491e62a3f3677e0f6 Export of internal Abseil changes. by Abseil Team - ad5c960b2eb914881d1ceba0e996a0a8f3f6ca59 Export of internal Abseil changes. by Abseil Team - 86f0fe93ad9d6d033a319476736a3256369c1f75 Export of internal Abseil changes. by Abseil Team - f0f15c2778b0e4959244dd25e63f445a455870f5 Export of internal Abseil changes. by Abseil Team - 29ff6d4860070bf8fcbd39c8805d0c32d56628a3 Removed "warning treated as error" flag from MSVC (#153) by vocaviking - 083d04dd4a62ebbf037079b06e49b323c5e1192a Export of internal Abseil changes. by Abseil Team - bea85b52733022294eef108a2e42d77b616ddca2 Export of internal Abseil changes. by Abseil Team - 8f96be6ca60d967bd4b37f93d0a03bcff4145200 Export of internal Abseil changes. by Abseil Team - 92e07e5590752d6b8e67f7f2f86c6286561e8cea Merge pull request #152 from clnperez/fix-multi-defines-p... by Derek Mauro <761129+derekmauro@users.noreply.github.com> - 2125e6444a9de9e41f21ecdc674dd7d8759c149d Export of internal Abseil changes. by Abseil Team - 9acad869d21731f5bc50430a33fe61cc0ffcbb0b Merge pull request #150 from OlafvdSpek/patch-2 by Jonathan Cohen - c2e00d341913bf03b4597ade5b056042e23e8c58 Export of internal Abseil changes. by Abseil Team - 9e060686d1c325f34f9806b45fe77bafeed00aee Export of internal Abseil changes. by Abseil Team - 7aa411ceafc1272a28579cca739a97a2fb79055a Export of internal Abseil changes. by Abseil Team - 2c5af55ed34850d8b7dd46177c8ca53fdfda920e Export of internal Abseil changes. by Abseil Team - 44aa275286baf97fc13529aca547a88b180beb08 Merge pull request #143 from rongjiecomputer/kernel by Xiaoyi Zhang - 42f22a28401c952f1fc5942231c7fdac80811bf5 Export of internal Abseil changes. by Abseil Team - b973bc53ef366f0253b85eeed9a79b241884a843 Merge pull request #139 from siepkes/smartos-support by ahedberg - e0def7473e52336f58759e11db4cd9467e5e0356 Export of internal Abseil changes. by Abseil Team - f826f1d489b61b64df1d94afbe5981841a82e5fa Merge pull request #138 from edbaunton/remove-deprecated-... by ahedberg - 7b50a4a94b0c7df68b3a854c850b551aaef0a8b4 Export of internal Abseil changes. by Abseil Team - a5030ca5125b9d557ecfeea8acc8b1a8e49f6d27 Merge pull request #144 from rongjiecomputer/winsock2 by Xiaoyi Zhang - 02687955b7ca8fc02ada9b14bc247deeb108d341 Export of internal Abseil changes. by Abseil Team - 8f612ebb152fb7e05643a2bcf78cb89a8c0641ad Export of internal Abseil changes. by Abseil Team - 134496a31d8b324f762de3bee9a002658c984456 Export of internal Abseil changes. by Abseil Team - ba8d6cf07766263723e86736f20a51c1c9c67b19 Export of internal Abseil changes. by Abseil Team - be1e84b988fceabcea4fc9e93f899539f0c81901 Export of internal Abseil changes. by Abseil Team - 16ac2ec2e38cdf47f9330a312e319d57da659c10 Merge pull request #134 from rongjiecomputer/cmake by Alex Strelnikov - 7efd8dc0f1075356e9c7caa950afd1ecf854e8b9 Export of internal Abseil changes. by Abseil Team - 87a4c07856e7dc69958019d47b2f02ae47746ec0 Export of internal Abseil changes. by Abseil Team - 4491d606df34c44efda47b6d17b605262f17e182 Export of internal Abseil changes. by Abseil Team GitOrigin-RevId: 44b0fafc62d9b8f192e8180cbe9c4b806b339d57 Change-Id: I2c427b5b41b2d34101922048b00f3d9dafcb498d --- absl/time/internal/cctz/src/cctz_benchmark.cc | 64 ++- absl/time/internal/cctz/src/civil_time_detail.cc | 4 +- absl/time/internal/cctz/src/civil_time_test.cc | 6 +- absl/time/internal/cctz/src/time_zone_fixed.cc | 41 +- absl/time/internal/cctz/src/time_zone_fixed.h | 10 +- absl/time/internal/cctz/src/time_zone_format.cc | 23 +- .../internal/cctz/src/time_zone_format_test.cc | 314 +++++++------- absl/time/internal/cctz/src/time_zone_if.cc | 4 +- absl/time/internal/cctz/src/time_zone_if.h | 32 +- absl/time/internal/cctz/src/time_zone_impl.cc | 17 +- absl/time/internal/cctz/src/time_zone_impl.h | 45 +- absl/time/internal/cctz/src/time_zone_info.cc | 114 +++--- absl/time/internal/cctz/src/time_zone_info.h | 16 +- absl/time/internal/cctz/src/time_zone_libc.cc | 222 ++++++++-- absl/time/internal/cctz/src/time_zone_libc.h | 13 +- absl/time/internal/cctz/src/time_zone_lookup.cc | 41 +- .../internal/cctz/src/time_zone_lookup_test.cc | 456 ++++++++++++++------- absl/time/internal/cctz/src/time_zone_posix.cc | 4 +- absl/time/internal/cctz/src/time_zone_posix.h | 46 ++- absl/time/internal/cctz/src/tzfile.h | 2 + absl/time/internal/cctz/src/zone_info_source.cc | 23 +- 21 files changed, 945 insertions(+), 552 deletions(-) (limited to 'absl/time/internal/cctz/src') diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc index f13cb4ee..4498d7d0 100644 --- a/absl/time/internal/cctz/src/cctz_benchmark.cc +++ b/absl/time/internal/cctz/src/cctz_benchmark.cc @@ -754,23 +754,21 @@ void BM_Zone_LoadAllTimeZonesCached(benchmark::State& state) { } BENCHMARK(BM_Zone_LoadAllTimeZonesCached); -void BM_Zone_TimeZoneImplGetImplicit(benchmark::State& state) { +void BM_Zone_TimeZoneEqualityImplicit(benchmark::State& state) { cctz::time_zone tz; // implicit UTC - cctz::time_zone::Impl::get(tz); while (state.KeepRunning()) { - cctz::time_zone::Impl::get(tz); + benchmark::DoNotOptimize(tz == tz); } } -BENCHMARK(BM_Zone_TimeZoneImplGetImplicit); +BENCHMARK(BM_Zone_TimeZoneEqualityImplicit); -void BM_Zone_TimeZoneImplGetExplicit(benchmark::State& state) { +void BM_Zone_TimeZoneEqualityExplicit(benchmark::State& state) { cctz::time_zone tz = cctz::utc_time_zone(); // explicit UTC - cctz::time_zone::Impl::get(tz); while (state.KeepRunning()) { - cctz::time_zone::Impl::get(tz); + benchmark::DoNotOptimize(tz == tz); } } -BENCHMARK(BM_Zone_TimeZoneImplGetExplicit); +BENCHMARK(BM_Zone_TimeZoneEqualityExplicit); void BM_Zone_UTCTimeZone(benchmark::State& state) { cctz::time_zone tz; @@ -780,13 +778,13 @@ void BM_Zone_UTCTimeZone(benchmark::State& state) { } BENCHMARK(BM_Zone_UTCTimeZone); -// In each "ToDateTime" benchmark we switch between two instants -// separated by at least one transition in order to defeat any -// internal caching of previous results (e.g., see local_time_hint_). +// In each "ToCivil" benchmark we switch between two instants separated +// by at least one transition in order to defeat any internal caching of +// previous results (e.g., see local_time_hint_). // // The "UTC" variants use UTC instead of the Google/local time zone. -void BM_Time_ToDateTime_CCTZ(benchmark::State& state) { +void BM_Time_ToCivil_CCTZ(benchmark::State& state) { const cctz::time_zone tz = TestTimeZone(); std::chrono::system_clock::time_point tp = std::chrono::system_clock::from_time_t(1384569027); @@ -798,9 +796,9 @@ void BM_Time_ToDateTime_CCTZ(benchmark::State& state) { benchmark::DoNotOptimize(cctz::convert(tp, tz)); } } -BENCHMARK(BM_Time_ToDateTime_CCTZ); +BENCHMARK(BM_Time_ToCivil_CCTZ); -void BM_Time_ToDateTime_Libc(benchmark::State& state) { +void BM_Time_ToCivil_Libc(benchmark::State& state) { // No timezone support, so just use localtime. time_t t = 1384569027; time_t t2 = 1418962578; @@ -815,9 +813,9 @@ void BM_Time_ToDateTime_Libc(benchmark::State& state) { #endif } } -BENCHMARK(BM_Time_ToDateTime_Libc); +BENCHMARK(BM_Time_ToCivil_Libc); -void BM_Time_ToDateTimeUTC_CCTZ(benchmark::State& state) { +void BM_Time_ToCivilUTC_CCTZ(benchmark::State& state) { const cctz::time_zone tz = cctz::utc_time_zone(); std::chrono::system_clock::time_point tp = std::chrono::system_clock::from_time_t(1384569027); @@ -826,9 +824,9 @@ void BM_Time_ToDateTimeUTC_CCTZ(benchmark::State& state) { benchmark::DoNotOptimize(cctz::convert(tp, tz)); } } -BENCHMARK(BM_Time_ToDateTimeUTC_CCTZ); +BENCHMARK(BM_Time_ToCivilUTC_CCTZ); -void BM_Time_ToDateTimeUTC_Libc(benchmark::State& state) { +void BM_Time_ToCivilUTC_Libc(benchmark::State& state) { time_t t = 1384569027; struct tm tm; while (state.KeepRunning()) { @@ -840,16 +838,16 @@ void BM_Time_ToDateTimeUTC_Libc(benchmark::State& state) { #endif } } -BENCHMARK(BM_Time_ToDateTimeUTC_Libc); +BENCHMARK(BM_Time_ToCivilUTC_Libc); -// In each "FromDateTime" benchmark we switch between two YMDhms -// values separated by at least one transition in order to defeat any -// internal caching of previous results (e.g., see time_local_hint_). +// In each "FromCivil" benchmark we switch between two YMDhms values +// separated by at least one transition in order to defeat any internal +// caching of previous results (e.g., see time_local_hint_). // // The "UTC" variants use UTC instead of the Google/local time zone. // The "Day0" variants require normalization of the day of month. -void BM_Time_FromDateTime_CCTZ(benchmark::State& state) { +void BM_Time_FromCivil_CCTZ(benchmark::State& state) { const cctz::time_zone tz = TestTimeZone(); int i = 0; while (state.KeepRunning()) { @@ -862,9 +860,9 @@ void BM_Time_FromDateTime_CCTZ(benchmark::State& state) { } } } -BENCHMARK(BM_Time_FromDateTime_CCTZ); +BENCHMARK(BM_Time_FromCivil_CCTZ); -void BM_Time_FromDateTime_Libc(benchmark::State& state) { +void BM_Time_FromCivil_Libc(benchmark::State& state) { // No timezone support, so just use localtime. int i = 0; while (state.KeepRunning()) { @@ -888,20 +886,20 @@ void BM_Time_FromDateTime_Libc(benchmark::State& state) { benchmark::DoNotOptimize(mktime(&tm)); } } -BENCHMARK(BM_Time_FromDateTime_Libc); +BENCHMARK(BM_Time_FromCivil_Libc); -void BM_Time_FromDateTimeUTC_CCTZ(benchmark::State& state) { +void BM_Time_FromCivilUTC_CCTZ(benchmark::State& state) { const cctz::time_zone tz = cctz::utc_time_zone(); while (state.KeepRunning()) { benchmark::DoNotOptimize( cctz::convert(cctz::civil_second(2014, 12, 18, 20, 16, 18), tz)); } } -BENCHMARK(BM_Time_FromDateTimeUTC_CCTZ); +BENCHMARK(BM_Time_FromCivilUTC_CCTZ); -// There is no BM_Time_FromDateTimeUTC_Libc. +// There is no BM_Time_FromCivilUTC_Libc. -void BM_Time_FromDateTimeDay0_CCTZ(benchmark::State& state) { +void BM_Time_FromCivilDay0_CCTZ(benchmark::State& state) { const cctz::time_zone tz = TestTimeZone(); int i = 0; while (state.KeepRunning()) { @@ -914,9 +912,9 @@ void BM_Time_FromDateTimeDay0_CCTZ(benchmark::State& state) { } } } -BENCHMARK(BM_Time_FromDateTimeDay0_CCTZ); +BENCHMARK(BM_Time_FromCivilDay0_CCTZ); -void BM_Time_FromDateTimeDay0_Libc(benchmark::State& state) { +void BM_Time_FromCivilDay0_Libc(benchmark::State& state) { // No timezone support, so just use localtime. int i = 0; while (state.KeepRunning()) { @@ -940,7 +938,7 @@ void BM_Time_FromDateTimeDay0_Libc(benchmark::State& state) { benchmark::DoNotOptimize(mktime(&tm)); } } -BENCHMARK(BM_Time_FromDateTimeDay0_Libc); +BENCHMARK(BM_Time_FromCivilDay0_Libc); const char* const kFormats[] = { RFC1123_full, // 0 diff --git a/absl/time/internal/cctz/src/civil_time_detail.cc b/absl/time/internal/cctz/src/civil_time_detail.cc index 92a2e09c..e888066d 100644 --- a/absl/time/internal/cctz/src/civil_time_detail.cc +++ b/absl/time/internal/cctz/src/civil_time_detail.cc @@ -19,7 +19,7 @@ #include namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { namespace detail { @@ -88,5 +88,5 @@ std::ostream& operator<<(std::ostream& os, weekday wd) { } // namespace detail } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/civil_time_test.cc b/absl/time/internal/cctz/src/civil_time_test.cc index d26c6498..2417a2a9 100644 --- a/absl/time/internal/cctz/src/civil_time_test.cc +++ b/absl/time/internal/cctz/src/civil_time_test.cc @@ -23,7 +23,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -621,7 +621,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 CivilTime 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)); @@ -1047,5 +1047,5 @@ TEST(CivilTime, FirstThursdayInMonth) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc index 4b608f68..070abd26 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.cc +++ b/absl/time/internal/cctz/src/time_zone_fixed.cc @@ -15,13 +15,13 @@ #include "time_zone_fixed.h" #include +#include #include -#include #include #include namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -30,8 +30,15 @@ namespace { // The prefix used for the internal names of fixed-offset zones. const char kFixedOffsetPrefix[] = "Fixed/UTC"; +const char kDigits[] = "0123456789"; + +char* Format02d(char* p, int v) { + *p++ = kDigits[(v / 10) % 10]; + *p++ = kDigits[v % 10]; + return p; +} + int Parse02d(const char* p) { - static const char kDigits[] = "0123456789"; if (const char* ap = std::strchr(kDigits, *p)) { int v = static_cast(ap - kDigits); if (const char* bp = std::strchr(kDigits, *++p)) { @@ -43,9 +50,9 @@ int Parse02d(const char* p) { } // namespace -bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) { +bool FixedOffsetFromName(const std::string& name, seconds* offset) { if (name.compare(0, std::string::npos, "UTC", 3) == 0) { - *offset = sys_seconds::zero(); + *offset = seconds::zero(); return true; } @@ -70,12 +77,12 @@ bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) { secs += ((hours * 60) + mins) * 60; if (secs > 24 * 60 * 60) return false; // outside supported offset range - *offset = sys_seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west + *offset = seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west return true; } -std::string FixedOffsetToName(const sys_seconds& offset) { - if (offset == sys_seconds::zero()) return "UTC"; +std::string FixedOffsetToName(const seconds& offset) { + if (offset == seconds::zero()) return "UTC"; if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) { // We don't support fixed-offset zones more than 24 hours // away from UTC to avoid complications in rendering such @@ -96,13 +103,21 @@ std::string FixedOffsetToName(const sys_seconds& offset) { } int hours = minutes / 60; minutes %= 60; - char buf[sizeof(kFixedOffsetPrefix) + sizeof("-24:00:00")]; - snprintf(buf, sizeof(buf), "%s%c%02d:%02d:%02d", - kFixedOffsetPrefix, sign, hours, minutes, seconds); + char buf[sizeof(kFixedOffsetPrefix) - 1 + sizeof("-24:00:00")]; + std::strcpy(buf, kFixedOffsetPrefix); + char* ep = buf + sizeof(kFixedOffsetPrefix) - 1; + *ep++ = sign; + ep = Format02d(ep, hours); + *ep++ = ':'; + ep = Format02d(ep, minutes); + *ep++ = ':'; + ep = Format02d(ep, seconds); + *ep++ = '\0'; + assert(ep == buf + sizeof(buf)); return buf; } -std::string FixedOffsetToAbbr(const sys_seconds& offset) { +std::string FixedOffsetToAbbr(const seconds& offset) { std::string abbr = FixedOffsetToName(offset); const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1; if (abbr.size() == prefix_len + 9) { // +99:99:99 @@ -121,5 +136,5 @@ std::string FixedOffsetToAbbr(const sys_seconds& offset) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_fixed.h b/absl/time/internal/cctz/src/time_zone_fixed.h index 15e9db1e..dbb2958e 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.h +++ b/absl/time/internal/cctz/src/time_zone_fixed.h @@ -20,7 +20,7 @@ #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -39,13 +39,13 @@ namespace cctz { // Note: FixedOffsetFromName() fails on syntax errors or when the parsed // offset exceeds 24 hours. FixedOffsetToName() and FixedOffsetToAbbr() // both produce "UTC" when the argument offset exceeds 24 hours. -bool FixedOffsetFromName(const std::string& name, sys_seconds* offset); -std::string FixedOffsetToName(const sys_seconds& offset); -std::string FixedOffsetToAbbr(const sys_seconds& offset); +bool FixedOffsetFromName(const std::string& name, seconds* offset); +std::string FixedOffsetToName(const seconds& offset); +std::string FixedOffsetToAbbr(const seconds& offset); } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_ diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc index 0efbbc79..02ecb2cf 100644 --- a/absl/time/internal/cctz/src/time_zone_format.cc +++ b/absl/time/internal/cctz/src/time_zone_format.cc @@ -38,7 +38,7 @@ #include "time_zone_if.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { namespace detail { @@ -142,6 +142,9 @@ char* Format02d(char* ep, int v) { // Formats a UTC offset, like +00:00. char* FormatOffset(char* ep, int offset, const char* mode) { + // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and + // generate a "negative zero" when we're formatting a zero offset + // as the result of a failed load_time_zone(). char sign = '+'; if (offset < 0) { offset = -offset; // bounded by 24h so no overflow @@ -278,7 +281,7 @@ const std::int_fast64_t kExp10[kDigits10_64 + 1] = { // not support the tm_gmtoff and tm_zone extensions to std::tm. // // Requires that zero() <= fs < seconds(1). -std::string format(const std::string& format, const time_point& tp, +std::string format(const std::string& format, const time_point& tp, const detail::femtoseconds& fs, const time_zone& tz) { std::string result; result.reserve(format.size()); // A reasonable guess for the result size. @@ -531,7 +534,7 @@ const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) { return dp; } -// Parses a std::string into a std::tm using strptime(3). +// Parses a string into a std::tm using strptime(3). const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) { if (dp != nullptr) { dp = strptime(dp, fmt, tm); @@ -556,7 +559,7 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) { // We also handle the %z specifier to accommodate platforms that do not // support the tm_gmtoff extension to std::tm. %Z is parsed but ignored. bool parse(const std::string& format, const std::string& input, - const time_zone& tz, time_point* sec, + const time_zone& tz, time_point* sec, detail::femtoseconds* fs, std::string* err) { // The unparsed input. const char* data = input.c_str(); // NUL terminated @@ -741,7 +744,7 @@ bool parse(const std::string& format, const std::string& input, data = ParseTM(data, spec.c_str(), &tm); // If we successfully parsed %p we need to remember whether the result - // was AM or PM so that we can adjust tm_hour before ConvertDateTime(). + // was AM or PM so that we can adjust tm_hour before time_zone::lookup(). // So reparse the input with a known AM hour, and check if it is shifted // to a PM hour. if (spec == "%p" && data != nullptr) { @@ -823,15 +826,15 @@ bool parse(const std::string& format, const std::string& input, const auto tp = ptz.lookup(cs).pre; // Checks for overflow/underflow and returns an error as necessary. - if (tp == time_point::max()) { - const auto al = ptz.lookup(time_point::max()); + if (tp == time_point::max()) { + const auto al = ptz.lookup(time_point::max()); if (cs > al.cs) { if (err != nullptr) *err = "Out-of-range field"; return false; } } - if (tp == time_point::min()) { - const auto al = ptz.lookup(time_point::min()); + if (tp == time_point::min()) { + const auto al = ptz.lookup(time_point::min()); if (cs < al.cs) { if (err != nullptr) *err = "Out-of-range field"; return false; @@ -846,5 +849,5 @@ bool parse(const std::string& format, const std::string& input, } // namespace detail } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc index 649e9b9a..260c56ad 100644 --- a/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -23,18 +23,10 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -using std::chrono::time_point_cast; -using std::chrono::system_clock; -using std::chrono::nanoseconds; -using std::chrono::microseconds; -using std::chrono::milliseconds; -using std::chrono::seconds; -using std::chrono::minutes; -using std::chrono::hours; -using testing::HasSubstr; +namespace chrono = std::chrono; namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -73,6 +65,17 @@ void TestFormatSpecifier(time_point tp, time_zone tz, const std::string& fmt, EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz)); } +// 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 // @@ -82,33 +85,36 @@ void TestFormatSpecifier(time_point tp, time_zone tz, const std::string& fmt, TEST(Format, TimePointResolution) { const char kFmt[] = "%H:%M:%E*S"; const time_zone utc = utc_time_zone(); - const time_point t0 = system_clock::from_time_t(1420167845) + - milliseconds(123) + microseconds(456) + - nanoseconds(789); - EXPECT_EQ("03:04:05.123456789", - format(kFmt, time_point_cast(t0), utc)); - EXPECT_EQ("03:04:05.123456", - format(kFmt, time_point_cast(t0), utc)); - EXPECT_EQ("03:04:05.123", - format(kFmt, time_point_cast(t0), utc)); + const time_point 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(t0), utc)); + EXPECT_EQ( + "03:04:05.123456", + format(kFmt, chrono::time_point_cast(t0), utc)); + EXPECT_EQ( + "03:04:05.123", + format(kFmt, chrono::time_point_cast(t0), utc)); EXPECT_EQ("03:04:05", - format(kFmt, time_point_cast(t0), utc)); + format(kFmt, chrono::time_point_cast(t0), utc)); EXPECT_EQ("03:04:05", - format(kFmt, time_point_cast(t0), utc)); + format(kFmt, chrono::time_point_cast(t0), utc)); EXPECT_EQ("03:04:00", - format(kFmt, time_point_cast(t0), utc)); + format(kFmt, chrono::time_point_cast(t0), utc)); EXPECT_EQ("03:00:00", - format(kFmt, time_point_cast(t0), utc)); + format(kFmt, chrono::time_point_cast(t0), utc)); } TEST(Format, TimePointExtendedResolution) { const char kFmt[] = "%H:%M:%E*S"; const time_zone utc = utc_time_zone(); - const time_point tp = - std::chrono::time_point_cast( - std::chrono::system_clock::from_time_t(0)) + - std::chrono::hours(12) + std::chrono::minutes(34) + - std::chrono::seconds(56); + const time_point tp = + chrono::time_point_cast( + chrono::system_clock::from_time_t(0)) + + chrono::hours(12) + chrono::minutes(34) + chrono::seconds(56); EXPECT_EQ( "12:34:56.123456789012345", @@ -133,7 +139,7 @@ TEST(Format, TimePointExtendedResolution) { TEST(Format, Basics) { time_zone tz = utc_time_zone(); - time_point tp = system_clock::from_time_t(0); + time_point tp = chrono::system_clock::from_time_t(0); // Starts with a couple basic edge cases. EXPECT_EQ("", format("", tp, tz)); @@ -146,8 +152,9 @@ TEST(Format, Basics) { std::string bigger(100000, 'x'); EXPECT_EQ(bigger, format(bigger, tp, tz)); - tp += hours(13) + minutes(4) + seconds(5); - tp += milliseconds(6) + microseconds(7) + nanoseconds(8); + 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)); @@ -157,7 +164,7 @@ TEST(Format, Basics) { TEST(Format, PosixConversions) { const time_zone tz = utc_time_zone(); - auto tp = system_clock::from_time_t(0); + auto tp = chrono::system_clock::from_time_t(0); TestFormatSpecifier(tp, tz, "%d", "01"); TestFormatSpecifier(tp, tz, "%e", " 1"); // extension but internal support @@ -197,7 +204,7 @@ TEST(Format, PosixConversions) { TEST(Format, LocaleSpecific) { const time_zone tz = utc_time_zone(); - auto tp = system_clock::from_time_t(0); + auto tp = chrono::system_clock::from_time_t(0); TestFormatSpecifier(tp, tz, "%a", "Thu"); TestFormatSpecifier(tp, tz, "%A", "Thursday"); @@ -206,8 +213,8 @@ TEST(Format, LocaleSpecific) { // %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, HasSubstr("1970")); - EXPECT_THAT(s, HasSubstr("00:00:00")); + 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"); @@ -246,7 +253,7 @@ TEST(Format, LocaleSpecific) { TEST(Format, Escaping) { const time_zone tz = utc_time_zone(); - auto tp = system_clock::from_time_t(0); + auto tp = chrono::system_clock::from_time_t(0); TestFormatSpecifier(tp, tz, "%%", "%"); TestFormatSpecifier(tp, tz, "%%a", "%a"); @@ -267,8 +274,8 @@ TEST(Format, ExtendedSeconds) { const time_zone tz = utc_time_zone(); // No subseconds. - time_point tp = system_clock::from_time_t(0); - tp += seconds(5); + time_point 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)); @@ -288,7 +295,8 @@ TEST(Format, ExtendedSeconds) { EXPECT_EQ("05.000000000000000", format("%E15S", tp, tz)); // With subseconds. - tp += milliseconds(6) + microseconds(7) + nanoseconds(8); + 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)); @@ -308,17 +316,18 @@ TEST(Format, ExtendedSeconds) { EXPECT_EQ("05.006007008000000", format("%E15S", tp, tz)); // Times before the Unix epoch. - tp = system_clock::from_time_t(0) + microseconds(-1); + 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 = system_clock::from_time_t(0) + microseconds(1395024427333304); + 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 += microseconds(1); + tp += chrono::microseconds(1); EXPECT_EQ("2014-03-17 02:47:07.333305", format("%Y-%m-%d %H:%M:%E*S", tp, tz)); } @@ -327,8 +336,8 @@ TEST(Format, ExtendedSubeconds) { const time_zone tz = utc_time_zone(); // No subseconds. - time_point tp = system_clock::from_time_t(0); - tp += seconds(5); + time_point 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)); @@ -348,7 +357,8 @@ TEST(Format, ExtendedSubeconds) { EXPECT_EQ("000000000000000", format("%E15f", tp, tz)); // With subseconds. - tp += milliseconds(6) + microseconds(7) + nanoseconds(8); + 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)); @@ -368,17 +378,18 @@ TEST(Format, ExtendedSubeconds) { EXPECT_EQ("006007008000000", format("%E15f", tp, tz)); // Times before the Unix epoch. - tp = system_clock::from_time_t(0) + microseconds(-1); + 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 = system_clock::from_time_t(0) + microseconds(1395024427333304); + 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 += microseconds(1); + tp += chrono::microseconds(1); EXPECT_EQ("2014-03-17 02:47:07.333305", format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); } @@ -393,8 +404,8 @@ TEST(Format, CompareExtendSecondsVsSubseconds) { auto fmt_B = [](const std::string& prec) { return "%S.%E" + prec + "f"; }; // No subseconds: - time_point tp = system_clock::from_time_t(0); - tp += seconds(5); + time_point 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)); @@ -410,7 +421,8 @@ TEST(Format, CompareExtendSecondsVsSubseconds) { // With subseconds: // ... %E*S and %S.%E*f are the same. - tp += milliseconds(6) + microseconds(7) + nanoseconds(8); + 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. @@ -425,7 +437,7 @@ TEST(Format, CompareExtendSecondsVsSubseconds) { } TEST(Format, ExtendedOffset) { - auto tp = system_clock::from_time_t(0); + auto tp = chrono::system_clock::from_time_t(0); time_zone tz = utc_time_zone(); TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); @@ -447,30 +459,28 @@ TEST(Format, ExtendedOffset) { TEST(Format, ExtendedSecondOffset) { const time_zone utc = utc_time_zone(); - time_point tp; + time_point tp; time_zone tz; EXPECT_TRUE(load_time_zone("America/New_York", &tz)); tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc); if (tz.lookup(tp).offset == -5 * 60 * 60) { - // We're likely dealing with zoneinfo that doesn't support really old - // timestamps, so America/New_York never looks to be on local mean time. + // It looks like the tzdata is only 32 bit (probably macOS), + // which bottoms out at 1901-12-13T20:45:52+00:00. } else { TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02"); TestFormatSpecifier(tp, tz, "%Ez", "-04:56"); } - tp += seconds(1); + tp += chrono::seconds(1); TestFormatSpecifier(tp, tz, "%E*z", "-05:00:00"); EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz)); tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc); -#if defined(__ANDROID__) && __ANDROID_API__ < 25 - // Only Android 'N'.1 and beyond have this tz2016g transition. -#else - TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19"); - TestFormatSpecifier(tp, tz, "%Ez", "+04:31"); -#endif - tp += seconds(1); + if (VersionCmp(tz, "2016g") >= 0) { + TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19"); + TestFormatSpecifier(tp, tz, "%Ez", "+04:31"); + } + tp += chrono::seconds(1); TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00"); } @@ -511,44 +521,44 @@ TEST(Format, RFC3339Format) { time_zone tz; EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); - time_point tp = + time_point 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 += milliseconds(100); + 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 += milliseconds(20); + 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 += milliseconds(3); + 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 += microseconds(400); + 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 += microseconds(50); + 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 += microseconds(6); + 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 += nanoseconds(700); + 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 += nanoseconds(80); + 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 += nanoseconds(9); + 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)); @@ -571,13 +581,13 @@ TEST(Parse, TimePointResolution) { const char kFmt[] = "%H:%M:%E*S"; const time_zone utc = utc_time_zone(); - time_point tp_ns; + time_point 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 tp_us; + time_point 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)); @@ -585,7 +595,7 @@ TEST(Parse, TimePointResolution) { EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_us)); EXPECT_EQ("03:04:05.123", format(kFmt, tp_us, utc)); - time_point tp_ms; + time_point 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)); @@ -593,17 +603,17 @@ TEST(Parse, TimePointResolution) { EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_ms)); EXPECT_EQ("03:04:05", format(kFmt, tp_ms, utc)); - time_point tp_s; + time_point 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 tp_m; + time_point tp_m; EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_m)); EXPECT_EQ("03:04:00", format(kFmt, tp_m, utc)); - time_point tp_h; + time_point tp_h; EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_h)); EXPECT_EQ("03:00:00", format(kFmt, tp_h, utc)); } @@ -612,7 +622,7 @@ TEST(Parse, TimePointExtendedResolution) { const char kFmt[] = "%H:%M:%E*S"; const time_zone utc = utc_time_zone(); - time_point tp; + time_point 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)); @@ -630,11 +640,12 @@ TEST(Parse, TimePointExtendedResolution) { TEST(Parse, Basics) { time_zone tz = utc_time_zone(); - time_point tp = system_clock::from_time_t(1234567890); + time_point tp = + chrono::system_clock::from_time_t(1234567890); // Simple edge cases. EXPECT_TRUE(parse("", "", tz, &tp)); - EXPECT_EQ(system_clock::from_time_t(0), tp); // everything defaulted + 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)); @@ -648,7 +659,7 @@ TEST(Parse, Basics) { TEST(Parse, WithTimeZone) { time_zone tz; EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); - time_point tp; + time_point tp; // We can parse a std::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)); @@ -659,13 +670,13 @@ TEST(Parse, WithTimeZone) { 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() returns - // the preferred-offset result, as defined for ConvertDateTime(). + // 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() returns - // the preferred-offset result, as defined for ConvertDateTime(). + // 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"); } @@ -673,7 +684,7 @@ TEST(Parse, WithTimeZone) { TEST(Parse, LeapSecond) { time_zone tz; EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); - time_point tp; + time_point tp; // ":59" -> ":59" EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59-08:00", tz, &tp)); @@ -697,7 +708,7 @@ TEST(Parse, LeapSecond) { TEST(Parse, ErrorCases) { const time_zone tz = utc_time_zone(); - auto tp = system_clock::from_time_t(0); + auto tp = chrono::system_clock::from_time_t(0); // Illegal trailing data. EXPECT_FALSE(parse("%S", "123", tz, &tp)); @@ -740,7 +751,7 @@ TEST(Parse, ErrorCases) { TEST(Parse, PosixConversions) { time_zone tz = utc_time_zone(); - auto tp = system_clock::from_time_t(0); + auto tp = chrono::system_clock::from_time_t(0); const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz); tp = reset; @@ -829,14 +840,14 @@ TEST(Parse, PosixConversions) { tp = reset; EXPECT_TRUE(parse("%s", "1234567890", tz, &tp)); - EXPECT_EQ(system_clock::from_time_t(1234567890), 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(system_clock::from_time_t(1234567890), 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, ... @@ -844,16 +855,16 @@ TEST(Parse, PosixConversions) { // 1414920600 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PST) tp = reset; EXPECT_TRUE(parse("%s", "1414917000", lax, &tp)); - EXPECT_EQ(system_clock::from_time_t(1414917000), tp); + EXPECT_EQ(chrono::system_clock::from_time_t(1414917000), tp); tp = reset; EXPECT_TRUE(parse("%s", "1414920600", lax, &tp)); - EXPECT_EQ(system_clock::from_time_t(1414920600), tp); + EXPECT_EQ(chrono::system_clock::from_time_t(1414920600), tp); #endif } TEST(Parse, LocaleSpecific) { time_zone tz = utc_time_zone(); - auto tp = system_clock::from_time_t(0); + 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. @@ -984,7 +995,8 @@ TEST(Parse, LocaleSpecific) { TEST(Parse, ExtendedSeconds) { const time_zone tz = utc_time_zone(); - const time_point unix_epoch = system_clock::from_time_t(0); + const time_point unix_epoch = + chrono::system_clock::from_time_t(0); // All %ES cases are treated the same as %E*S on input. auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", @@ -992,47 +1004,47 @@ TEST(Parse, ExtendedSeconds) { for (const std::string& prec : precisions) { const std::string fmt = "%E" + prec + "S"; SCOPED_TRACE(fmt); - time_point tp = unix_epoch; + time_point tp = unix_epoch; EXPECT_TRUE(parse(fmt, "5", tz, &tp)); - EXPECT_EQ(unix_epoch + seconds(5), tp); + EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "05", tz, &tp)); - EXPECT_EQ(unix_epoch + seconds(5), tp); + EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "05.0", tz, &tp)); - EXPECT_EQ(unix_epoch + seconds(5), tp); + EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "05.00", tz, &tp)); - EXPECT_EQ(unix_epoch + seconds(5), tp); + EXPECT_EQ(unix_epoch + chrono::seconds(5), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "05.6", tz, &tp)); - EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), 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 + seconds(5) + milliseconds(600), 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 + seconds(5) + milliseconds(600), 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 + seconds(5) + milliseconds(670), 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 + seconds(5) + milliseconds(670), 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 + seconds(5) + milliseconds(678), 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 tp = unix_epoch; + time_point tp = unix_epoch; EXPECT_TRUE(parse("%E*S", "0.2147483647", tz, &tp)); - EXPECT_EQ(unix_epoch + nanoseconds(214748364), 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 + nanoseconds(214748364), 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. @@ -1040,18 +1052,18 @@ TEST(Parse, ExtendedSeconds) { EXPECT_TRUE(parse( "%E*S", "0.214748364801234567890123456789012345678901234567890123456789", tz, &tp)); - EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp); + EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); } TEST(Parse, ExtendedSecondsScan) { const time_zone tz = utc_time_zone(); - time_point tp; + time_point 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 = - system_clock::from_time_t(0) + nanoseconds(micros * 1000 + ns); + 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; @@ -1065,7 +1077,8 @@ TEST(Parse, ExtendedSecondsScan) { TEST(Parse, ExtendedSubeconds) { const time_zone tz = utc_time_zone(); - const time_point unix_epoch = system_clock::from_time_t(0); + const time_point unix_epoch = + chrono::system_clock::from_time_t(0); // All %Ef cases are treated the same as %E*f on input. auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", @@ -1073,41 +1086,42 @@ TEST(Parse, ExtendedSubeconds) { for (const std::string& prec : precisions) { const std::string fmt = "%E" + prec + "f"; SCOPED_TRACE(fmt); - time_point tp = unix_epoch - seconds(1); + time_point 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 + milliseconds(600), tp); + EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "60", tz, &tp)); - EXPECT_EQ(unix_epoch + milliseconds(600), tp); + EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "600", tz, &tp)); - EXPECT_EQ(unix_epoch + milliseconds(600), tp); + EXPECT_EQ(unix_epoch + chrono::milliseconds(600), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "67", tz, &tp)); - EXPECT_EQ(unix_epoch + milliseconds(670), tp); + EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "670", tz, &tp)); - EXPECT_EQ(unix_epoch + milliseconds(670), tp); + EXPECT_EQ(unix_epoch + chrono::milliseconds(670), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "678", tz, &tp)); - EXPECT_EQ(unix_epoch + milliseconds(678), tp); + EXPECT_EQ(unix_epoch + chrono::milliseconds(678), tp); tp = unix_epoch; EXPECT_TRUE(parse(fmt, "6789", tz, &tp)); - EXPECT_EQ(unix_epoch + milliseconds(678) + microseconds(900), 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 tp = unix_epoch; + time_point tp = unix_epoch; EXPECT_TRUE(parse("%E*f", "2147483647", tz, &tp)); - EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp); + EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); tp = unix_epoch; EXPECT_TRUE(parse("%E*f", "2147483648", tz, &tp)); - EXPECT_EQ(unix_epoch + nanoseconds(214748364), 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. @@ -1115,11 +1129,11 @@ TEST(Parse, ExtendedSubeconds) { EXPECT_TRUE(parse( "%E*f", "214748364801234567890123456789012345678901234567890123456789", tz, &tp)); - EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp); + EXPECT_EQ(unix_epoch + chrono::nanoseconds(214748364), tp); } TEST(Parse, ExtendedSubecondsScan) { - time_point tp; + time_point tp; const time_zone tz = utc_time_zone(); for (int ms = 0; ms < 1000; ms += 111) { for (int us = 0; us < 1000; us += 27) { @@ -1129,14 +1143,14 @@ TEST(Parse, ExtendedSubecondsScan) { 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 = - system_clock::from_time_t(0) + nanoseconds(micros * 1000 + ns); + 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 oss; oss << std::setfill('0') << std::setw(3) << ps; const std::string input = nanos + oss.str() + "999"; EXPECT_TRUE(parse("%E*f", input, tz, &tp)); - EXPECT_EQ(expected + nanoseconds(ps) / 1000, tp) << input; + EXPECT_EQ(expected + chrono::nanoseconds(ps) / 1000, tp) << input; } } } @@ -1145,7 +1159,7 @@ TEST(Parse, ExtendedSubecondsScan) { TEST(Parse, ExtendedOffset) { const time_zone utc = utc_time_zone(); - time_point tp; + time_point tp; // %z against +-HHMM. EXPECT_TRUE(parse("%z", "+0000", utc, &tp)); @@ -1195,7 +1209,7 @@ TEST(Parse, ExtendedOffset) { TEST(Parse, ExtendedSecondOffset) { const time_zone utc = utc_time_zone(); - time_point tp; + time_point tp; // %Ez against +-HH:MM:SS. EXPECT_TRUE(parse("%Ez", "+00:00:00", utc, &tp)); @@ -1264,7 +1278,7 @@ TEST(Parse, ExtendedSecondOffset) { TEST(Parse, ExtendedYears) { const time_zone utc = utc_time_zone(); const char e4y_fmt[] = "%E4Y%m%d"; // no separators - time_point tp; + time_point tp; // %E4Y consumes exactly four chars, including any sign. EXPECT_TRUE(parse(e4y_fmt, "-9991127", utc, &tp)); @@ -1295,45 +1309,45 @@ TEST(Parse, ExtendedYears) { TEST(Parse, RFC3339Format) { const time_zone tz = utc_time_zone(); - time_point tp; + time_point 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 %Ez also accepts "Z" as a synonym for "+00:00". - time_point tp2; + time_point tp2; EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp2)); EXPECT_EQ(tp, tp2); } TEST(Parse, MaxRange) { const time_zone utc = utc_time_zone(); - time_point tp; + time_point 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::max()); + EXPECT_EQ(tp, time_point::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::max()); + EXPECT_EQ(tp, time_point::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::min()); + EXPECT_EQ(tp, time_point::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::min()); + EXPECT_EQ(tp, time_point::min()); EXPECT_FALSE( parse(RFC3339_sec, "-292277022657-01-27T08:29:51+01:00", utc, &tp)); @@ -1356,11 +1370,11 @@ 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 = nanoseconds(654321); + const auto subseconds = chrono::nanoseconds(654321); // RFC3339, which renders subseconds. { - time_point out; + time_point 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 @@ -1368,7 +1382,7 @@ TEST(FormatParse, RoundTrip) { // RFC1123, which only does whole seconds. { - time_point out; + time_point 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 @@ -1381,7 +1395,7 @@ TEST(FormatParse, RoundTrip) { // Even though we don't know what %c will produce, it should roundtrip, // but only in the 0-offset timezone. { - time_point out; + time_point out; time_zone utc = utc_time_zone(); const std::string s = format("%c", in, utc); EXPECT_TRUE(parse("%c", s, utc, &out)) << s; @@ -1392,23 +1406,23 @@ TEST(FormatParse, RoundTrip) { TEST(FormatParse, RoundTripDistantFuture) { const time_zone utc = utc_time_zone(); - const time_point in = time_point::max(); + const time_point in = time_point::max(); const std::string s = format(RFC3339_full, in, utc); - time_point out; + time_point 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 in = time_point::min(); + const time_point in = time_point::min(); const std::string s = format(RFC3339_full, in, utc); - time_point out; + time_point out; EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s; EXPECT_EQ(in, out); } } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_if.cc b/absl/time/internal/cctz/src/time_zone_if.cc index d289a5c9..f7c36b2b 100644 --- a/absl/time/internal/cctz/src/time_zone_if.cc +++ b/absl/time/internal/cctz/src/time_zone_if.cc @@ -17,7 +17,7 @@ #include "time_zone_libc.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -39,5 +39,5 @@ TimeZoneIf::~TimeZoneIf() {} } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h index 2d5f885d..9886f2c5 100644 --- a/absl/time/internal/cctz/src/time_zone_if.h +++ b/absl/time/internal/cctz/src/time_zone_if.h @@ -24,7 +24,7 @@ #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -38,35 +38,37 @@ class TimeZoneIf { virtual ~TimeZoneIf(); virtual time_zone::absolute_lookup BreakTime( - const time_point& tp) const = 0; + const time_point& tp) const = 0; virtual time_zone::civil_lookup MakeTime( const civil_second& cs) const = 0; + virtual bool NextTransition(const time_point& tp, + time_zone::civil_transition* trans) const = 0; + virtual bool PrevTransition(const time_point& tp, + time_zone::civil_transition* trans) const = 0; + + virtual std::string Version() const = 0; virtual std::string Description() const = 0; - virtual bool NextTransition(time_point* tp) const = 0; - virtual bool PrevTransition(time_point* tp) const = 0; protected: TimeZoneIf() {} }; -// Convert between time_point and a count of seconds since -// the Unix epoch. We assume that the std::chrono::system_clock and the +// Convert between time_point and a count of seconds since the +// Unix epoch. We assume that the std::chrono::system_clock and the // Unix clock are second aligned, but not that they share an epoch. -inline std::int_fast64_t ToUnixSeconds(const time_point& tp) { - return (tp - std::chrono::time_point_cast( - std::chrono::system_clock::from_time_t(0))) - .count(); +inline std::int_fast64_t ToUnixSeconds(const time_point& tp) { + return (tp - std::chrono::time_point_cast( + std::chrono::system_clock::from_time_t(0))).count(); } -inline time_point FromUnixSeconds(std::int_fast64_t t) { - return std::chrono::time_point_cast( - std::chrono::system_clock::from_time_t(0)) + - sys_seconds(t); +inline time_point FromUnixSeconds(std::int_fast64_t t) { + return std::chrono::time_point_cast( + std::chrono::system_clock::from_time_t(0)) + seconds(t); } } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_ diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc index 3ba40ac2..a4e42916 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.cc +++ b/absl/time/internal/cctz/src/time_zone_impl.cc @@ -22,7 +22,7 @@ #include "time_zone_fixed.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -46,8 +46,8 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { const time_zone::Impl* const utc_impl = UTCImpl(); // First check for UTC (which is never a key in time_zone_map). - auto offset = sys_seconds::zero(); - if (FixedOffsetFromName(name, &offset) && offset == sys_seconds::zero()) { + auto offset = seconds::zero(); + if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) { *tz = time_zone(utc_impl); return true; } @@ -84,15 +84,6 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { return impl != utc_impl; } -const time_zone::Impl& time_zone::Impl::get(const time_zone& tz) { - if (tz.impl_ == nullptr) { - // Dereferencing an implicit-UTC time_zone is expected to be - // rare, so we don't mind paying a small synchronization cost. - return *UTCImpl(); - } - return *tz.impl_; -} - void time_zone::Impl::ClearTimeZoneMapTestOnly() { std::lock_guard lock(time_zone_mutex); if (time_zone_map != nullptr) { @@ -115,5 +106,5 @@ const time_zone::Impl* time_zone::Impl::UTCImpl() { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_impl.h b/absl/time/internal/cctz/src/time_zone_impl.h index a955e40e..7da2e99d 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.h +++ b/absl/time/internal/cctz/src/time_zone_impl.h @@ -24,7 +24,7 @@ #include "time_zone_info.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -38,19 +38,18 @@ class time_zone::Impl { // some other kind of error occurs. Note that loading "UTC" never fails. static bool LoadTimeZone(const std::string& name, time_zone* tz); - // Dereferences the time_zone to obtain its Impl. - static const time_zone::Impl& get(const time_zone& tz); - // Clears the map of cached time zones. Primarily for use in benchmarks // that gauge the performance of loading/parsing the time-zone data. static void ClearTimeZoneMapTestOnly(); // The primary key is the time-zone ID (e.g., "America/New_York"). - const std::string& name() const { return name_; } + const std::string& Name() const { + // TODO: It would nice if the zoneinfo data included the zone name. + return name_; + } // Breaks a time_point down to civil-time components in this time zone. - time_zone::absolute_lookup BreakTime( - const time_point& tp) const { + time_zone::absolute_lookup BreakTime(const time_point& tp) const { return zone_->BreakTime(tp); } @@ -61,28 +60,22 @@ class time_zone::Impl { return zone_->MakeTime(cs); } - // Returns an implementation-specific description of this time zone. - std::string Description() const { return zone_->Description(); } - // Finds the time of the next/previous offset change in this time zone. - // - // By definition, NextTransition(&tp) returns false when tp has its - // maximum value, and PrevTransition(&tp) returns false when tp has its - // mimimum value. If the zone has no transitions, the result will also - // be false no matter what the argument. - // - // Otherwise, when tp has its mimimum value, NextTransition(&tp) returns - // true and sets tp to the first recorded transition. Chains of calls - // to NextTransition()/PrevTransition() will eventually return false, - // but it is unspecified exactly when NextTransition(&tp) jumps to false, - // or what time is set by PrevTransition(&tp) for a very distant tp. - bool NextTransition(time_point* tp) const { - return zone_->NextTransition(tp); + bool NextTransition(const time_point& tp, + time_zone::civil_transition* trans) const { + return zone_->NextTransition(tp, trans); } - bool PrevTransition(time_point* tp) const { - return zone_->PrevTransition(tp); + bool PrevTransition(const time_point& tp, + time_zone::civil_transition* trans) const { + return zone_->PrevTransition(tp, trans); } + // Returns an implementation-defined version std::string for this time zone. + std::string Version() const { return zone_->Version(); } + + // Returns an implementation-defined description of this time zone. + std::string Description() const { return zone_->Description(); } + private: explicit Impl(const std::string& name); static const Impl* UTCImpl(); @@ -93,7 +86,7 @@ class time_zone::Impl { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_ diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc index 7f5a8fad..e19d1d2d 100644 --- a/absl/time/internal/cctz/src/time_zone_info.cc +++ b/absl/time/internal/cctz/src/time_zone_info.cc @@ -50,7 +50,7 @@ #include "time_zone_posix.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -141,7 +141,7 @@ std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday, return (days * kSecsPerDay) + pt.time.offset; } -inline time_zone::civil_lookup MakeUnique(const time_point& tp) { +inline time_zone::civil_lookup MakeUnique(const time_point& tp) { time_zone::civil_lookup cl; cl.kind = time_zone::civil_lookup::UNIQUE; cl.pre = cl.trans = cl.post = tp; @@ -180,21 +180,20 @@ inline civil_second YearShift(const civil_second& cs, year_t shift) { } // namespace // What (no leap-seconds) UTC+seconds zoneinfo would look like. -bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) { +bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { transition_types_.resize(1); TransitionType& tt(transition_types_.back()); tt.utc_offset = static_cast(offset.count()); tt.is_dst = false; tt.abbr_index = 0; - // We temporarily add some redundant, contemporary (2012 through 2021) + // We temporarily add some redundant, contemporary (2013 through 2023) // transitions for performance reasons. See TimeZoneInfo::LocalTime(). // TODO: Fix the performance issue and remove the extra transitions. transitions_.clear(); transitions_.reserve(12); for (const std::int_fast64_t unix_time : { -(1LL << 59), // BIG_BANG - 1325376000LL, // 2012-01-01T00:00:00+00:00 1356998400LL, // 2013-01-01T00:00:00+00:00 1388534400LL, // 2014-01-01T00:00:00+00:00 1420070400LL, // 2015-01-01T00:00:00+00:00 @@ -204,6 +203,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) { 1546300800LL, // 2019-01-01T00:00:00+00:00 1577836800LL, // 2020-01-01T00:00:00+00:00 1609459200LL, // 2021-01-01T00:00:00+00:00 + 1640995200LL, // 2022-01-01T00:00:00+00:00 + 1672531200LL, // 2023-01-01T00:00:00+00:00 2147483647LL, // 2^31 - 1 }) { Transition& tr(*transitions_.emplace(transitions_.end())); @@ -219,8 +220,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) { future_spec_.clear(); // never needed for a fixed-offset zone extended_ = false; - tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs; - tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs; + tt.civil_max = LocalTime(seconds::max().count(), tt).cs; + tt.civil_min = LocalTime(seconds::min().count(), tt).cs; transitions_.shrink_to_fit(); return true; @@ -286,7 +287,7 @@ bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index, return true; } -// Use the POSIX-TZ-environment-variable-style std::string to handle times +// Use the POSIX-TZ-environment-variable-style string to handle times // in years after the last transition stored in the zoneinfo data. void TimeZoneInfo::ExtendTransitions(const std::string& name, const Header& hdr) { @@ -520,6 +521,13 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { // We don't check for EOF so that we're forwards compatible. + // If we did not find version information during the standard loading + // process (as of tzh_version '3' that is unsupported), then ask the + // ZoneInfoSource for any out-of-bound version std::string it may be privy to. + if (version_.empty()) { + version_ = zip->Version(); + } + // Trim redundant transitions. zic may have added these to work around // differences between the glibc and reference implementations (see // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071). @@ -566,10 +574,10 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { } // Compute the maximum/minimum civil times that can be converted to a - // time_point for each of the zone's transition types. + // time_point for each of the zone's transition types. for (auto& tt : transition_types_) { - tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs; - tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs; + tt.civil_max = LocalTime(seconds::max().count(), tt).cs; + tt.civil_min = LocalTime(seconds::min().count(), tt).cs; } transitions_.shrink_to_fit(); @@ -606,6 +614,10 @@ class FileZoneInfoSource : public ZoneInfoSource { if (rc == 0) len_ -= offset; return rc; } + std::string Version() const override { + // TODO: It would nice if the zoneinfo data included the tzdb version. + return std::string(); + } protected: explicit FileZoneInfoSource( @@ -655,14 +667,15 @@ std::unique_ptr FileZoneInfoSource::Open( return std::unique_ptr(new FileZoneInfoSource(fp, length)); } -#if defined(__ANDROID__) class AndroidZoneInfoSource : public FileZoneInfoSource { public: static std::unique_ptr Open(const std::string& name); + std::string Version() const override { return version_; } private: - explicit AndroidZoneInfoSource(FILE* fp, std::size_t len) - : FileZoneInfoSource(fp, len) {} + explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers) + : FileZoneInfoSource(fp, len), version_(vers) {} + std::string version_; }; std::unique_ptr AndroidZoneInfoSource::Open( @@ -670,6 +683,7 @@ std::unique_ptr AndroidZoneInfoSource::Open( // Use of the "file:" prefix is intended for testing purposes only. if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5)); +#if defined(__ANDROID__) // See Android's libc/tzcode/bionic.cpp for additional information. for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", "/system/usr/share/zoneinfo/tzdata"}) { @@ -679,6 +693,7 @@ std::unique_ptr AndroidZoneInfoSource::Open( char hbuf[24]; // covers header.zonetab_offset too if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue; if (strncmp(hbuf, "tzdata", 6) != 0) continue; + const char* vers = (hbuf[11] == '\0') ? hbuf + 6 : ""; const std::int_fast32_t index_offset = Decode32(hbuf + 12); const std::int_fast32_t data_offset = Decode32(hbuf + 16); if (index_offset < 0 || data_offset < index_offset) continue; @@ -699,13 +714,13 @@ std::unique_ptr AndroidZoneInfoSource::Open( if (strcmp(name.c_str(), ebuf) == 0) { if (fseek(fp.get(), static_cast(start), SEEK_SET) != 0) break; return std::unique_ptr(new AndroidZoneInfoSource( - fp.release(), static_cast(length))); + fp.release(), static_cast(length), vers)); } } } +#endif // __ANDROID__ return nullptr; } -#endif } // namespace @@ -714,7 +729,7 @@ bool TimeZoneInfo::Load(const std::string& name) { // zone never fails because the simple, fixed-offset state can be // internally generated. Note that this depends on our choice to not // accept leap-second encoded ("right") zoneinfo. - auto offset = sys_seconds::zero(); + auto offset = seconds::zero(); if (FixedOffsetFromName(name, &offset)) { return ResetToBuiltinUTC(offset); } @@ -723,9 +738,7 @@ bool TimeZoneInfo::Load(const std::string& name) { auto zip = cctz_extension::zone_info_source_factory( name, [](const std::string& name) -> std::unique_ptr { if (auto zip = FileZoneInfoSource::Open(name)) return zip; -#if defined(__ANDROID__) if (auto zip = AndroidZoneInfoSource::Open(name)) return zip; -#endif return nullptr; }); return zip != nullptr && Load(name, zip.get()); @@ -756,14 +769,14 @@ time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs, year_t c4_shift) const { assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_); time_zone::civil_lookup cl = MakeTime(cs); - if (c4_shift > sys_seconds::max().count() / kSecsPer400Years) { - cl.pre = cl.trans = cl.post = time_point::max(); + if (c4_shift > seconds::max().count() / kSecsPer400Years) { + cl.pre = cl.trans = cl.post = time_point::max(); } else { - const auto offset = sys_seconds(c4_shift * kSecsPer400Years); - const auto limit = time_point::max() - offset; + const auto offset = seconds(c4_shift * kSecsPer400Years); + const auto limit = time_point::max() - offset; for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) { if (*tp > limit) { - *tp = time_point::max(); + *tp = time_point::max(); } else { *tp += offset; } @@ -773,7 +786,7 @@ time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs, } time_zone::absolute_lookup TimeZoneInfo::BreakTime( - const time_point& tp) const { + const time_point& tp) const { std::int_fast64_t unix_time = ToUnixSeconds(tp); const std::size_t timecnt = transitions_.size(); assert(timecnt != 0); // We always add a transition. @@ -789,7 +802,7 @@ time_zone::absolute_lookup TimeZoneInfo::BreakTime( const std::int_fast64_t diff = unix_time - transitions_[timecnt - 1].unix_time; const year_t shift = diff / kSecsPer400Years + 1; - const auto d = sys_seconds(shift * kSecsPer400Years); + const auto d = seconds(shift * kSecsPer400Years); time_zone::absolute_lookup al = BreakTime(tp - d); al.cs = YearShift(al.cs, shift * 400); return al; @@ -848,7 +861,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const { if (tr->prev_civil_sec >= cs) { // Before first transition, so use the default offset. const TransitionType& tt(transition_types_[default_transition_type_]); - if (cs < tt.civil_min) return MakeUnique(time_point::min()); + if (cs < tt.civil_min) return MakeUnique(time_point::min()); return MakeUnique(cs - (civil_second() + tt.utc_offset)); } // tr->prev_civil_sec < cs < tr->civil_sec @@ -865,7 +878,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const { return TimeLocal(YearShift(cs, shift * -400), shift); } const TransitionType& tt(transition_types_[tr->type_index]); - if (cs > tt.civil_max) return MakeUnique(time_point::max()); + if (cs > tt.civil_max) return MakeUnique(time_point::max()); return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); } // tr->civil_sec <= cs <= tr->prev_civil_sec @@ -886,17 +899,20 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const { return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); } +std::string TimeZoneInfo::Version() const { + return version_; +} + std::string TimeZoneInfo::Description() const { std::ostringstream oss; - // TODO: It would nice if the zoneinfo data included the zone name. - // TODO: It would nice if the zoneinfo data included the tzdb version. oss << "#trans=" << transitions_.size(); oss << " #types=" << transition_types_.size(); oss << " spec='" << future_spec_ << "'"; return oss.str(); } -bool TimeZoneInfo::NextTransition(time_point* tp) const { +bool TimeZoneInfo::NextTransition(const time_point& tp, + time_zone::civil_transition* trans) const { if (transitions_.empty()) return false; const Transition* begin = &transitions_[0]; const Transition* end = begin + transitions_.size(); @@ -905,22 +921,24 @@ bool TimeZoneInfo::NextTransition(time_point* tp) const { // really a sentinel, not a transition. See tz/zic.c. ++begin; } - std::int_fast64_t unix_time = ToUnixSeconds(*tp); + std::int_fast64_t unix_time = ToUnixSeconds(tp); const Transition target = { unix_time }; const Transition* tr = std::upper_bound(begin, end, target, Transition::ByUnixTime()); - if (tr != begin) { // skip no-op transitions - for (; tr != end; ++tr) { - if (!EquivTransitions(tr[-1].type_index, tr[0].type_index)) break; - } + for (; tr != end; ++tr) { // skip no-op transitions + std::uint_fast8_t prev_type_index = + (tr == begin) ? default_transition_type_ : tr[-1].type_index; + if (!EquivTransitions(prev_type_index, tr[0].type_index)) break; } // When tr == end we return false, ignoring future_spec_. if (tr == end) return false; - *tp = FromUnixSeconds(tr->unix_time); + trans->from = tr->prev_civil_sec + 1; + trans->to = tr->civil_sec; return true; } -bool TimeZoneInfo::PrevTransition(time_point* tp) const { +bool TimeZoneInfo::PrevTransition(const time_point& tp, + time_zone::civil_transition* trans) const { if (transitions_.empty()) return false; const Transition* begin = &transitions_[0]; const Transition* end = begin + transitions_.size(); @@ -929,11 +947,12 @@ bool TimeZoneInfo::PrevTransition(time_point* tp) const { // really a sentinel, not a transition. See tz/zic.c. ++begin; } - std::int_fast64_t unix_time = ToUnixSeconds(*tp); - if (FromUnixSeconds(unix_time) != *tp) { + std::int_fast64_t unix_time = ToUnixSeconds(tp); + if (FromUnixSeconds(unix_time) != tp) { if (unix_time == std::numeric_limits::max()) { if (end == begin) return false; // Ignore future_spec_. - *tp = FromUnixSeconds((--end)->unix_time); + trans->from = (--end)->prev_civil_sec + 1; + trans->to = end->civil_sec; return true; } unix_time += 1; // ceils @@ -941,18 +960,19 @@ bool TimeZoneInfo::PrevTransition(time_point* tp) const { const Transition target = { unix_time }; const Transition* tr = std::lower_bound(begin, end, target, Transition::ByUnixTime()); - if (tr != begin) { // skip no-op transitions - for (; tr - 1 != begin; --tr) { - if (!EquivTransitions(tr[-2].type_index, tr[-1].type_index)) break; - } + for (; tr != begin; --tr) { // skip no-op transitions + std::uint_fast8_t prev_type_index = + (tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index; + if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break; } // When tr == end we return the "last" transition, ignoring future_spec_. if (tr == begin) return false; - *tp = FromUnixSeconds((--tr)->unix_time); + trans->from = (--tr)->prev_civil_sec + 1; + trans->to = tr->civil_sec; return true; } } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_info.h b/absl/time/internal/cctz/src/time_zone_info.h index 5abf2811..e7a7d02f 100644 --- a/absl/time/internal/cctz/src/time_zone_info.h +++ b/absl/time/internal/cctz/src/time_zone_info.h @@ -28,7 +28,7 @@ #include "tzfile.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -72,12 +72,15 @@ class TimeZoneInfo : public TimeZoneIf { // TimeZoneIf implementations. time_zone::absolute_lookup BreakTime( - const time_point& tp) const override; + const time_point& tp) const override; time_zone::civil_lookup MakeTime( const civil_second& cs) const override; + bool NextTransition(const time_point& tp, + time_zone::civil_transition* trans) const override; + bool PrevTransition(const time_point& tp, + time_zone::civil_transition* trans) const override; + std::string Version() const override; std::string Description() const override; - bool NextTransition(time_point* tp) const override; - bool PrevTransition(time_point* tp) const override; private: struct Header { // counts of: @@ -99,7 +102,7 @@ class TimeZoneInfo : public TimeZoneIf { std::uint_fast8_t tt2_index) const; void ExtendTransitions(const std::string& name, const Header& hdr); - bool ResetToBuiltinUTC(const sys_seconds& offset); + bool ResetToBuiltinUTC(const seconds& offset); bool Load(const std::string& name, ZoneInfoSource* zip); // Helpers for BreakTime() and MakeTime(). @@ -115,6 +118,7 @@ class TimeZoneInfo : public TimeZoneIf { std::uint_fast8_t default_transition_type_; // for before first transition std::string abbreviations_; // all the NUL-terminated abbreviations + std::string version_; // the tzdata version if available std::string future_spec_; // for after the last zic transition bool extended_; // future_spec_ was used to generate transitions year_t last_year_; // the final year of the generated transitions @@ -128,7 +132,7 @@ class TimeZoneInfo : public TimeZoneIf { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_ diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc index 32a7a927..3dd75b50 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.cc +++ b/absl/time/internal/cctz/src/time_zone_libc.cc @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -27,7 +28,7 @@ #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -86,73 +87,206 @@ OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::__tm_gmtoff) = nullptr, #endif // !defined(__tm_gmtoff) && !defined(__tm_zone) #endif +inline std::tm* gm_time(const std::time_t *timep, std::tm *result) { +#if defined(_WIN32) || defined(_WIN64) + return gmtime_s(result, timep) ? nullptr : result; +#else + return gmtime_r(timep, result); +#endif +} + +inline std::tm* local_time(const std::time_t *timep, std::tm *result) { +#if defined(_WIN32) || defined(_WIN64) + return localtime_s(result, timep) ? nullptr : result; +#else + return localtime_r(timep, result); +#endif +} + +// Converts a civil second and "dst" flag into a time_t and UTC offset. +// Returns false if time_t cannot represent the requested civil second. +// Caller must have already checked that cs.year() will fit into a tm_year. +bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) { + std::tm tm; + tm.tm_year = static_cast(cs.year() - year_t{1900}); + tm.tm_mon = cs.month() - 1; + tm.tm_mday = cs.day(); + tm.tm_hour = cs.hour(); + tm.tm_min = cs.minute(); + tm.tm_sec = cs.second(); + tm.tm_isdst = is_dst; + *t = std::mktime(&tm); + if (*t == std::time_t{-1}) { + std::tm tm2; + const std::tm* tmp = local_time(t, &tm2); + if (tmp == nullptr || tmp->tm_year != tm.tm_year || + tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday || + tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min || + tmp->tm_sec != tm.tm_sec) { + // A true error (not just one second before the epoch). + return false; + } + } + *off = get_offset_abbr(tm).first; + return true; +} + +// Find the least time_t in [lo:hi] where local time matches offset, given: +// (1) lo doesn't match, (2) hi does, and (3) there is only one transition. +std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) { + std::tm tm; + while (lo + 1 != hi) { + const std::time_t mid = lo + (hi - lo) / 2; + if (std::tm* tmp = local_time(&mid, &tm)) { + if (get_offset_abbr(*tmp).first == offset) { + hi = mid; + } else { + lo = mid; + } + } else { + // If std::tm cannot hold some result we resort to a linear search, + // ignoring all failed conversions. Slow, but never really happens. + while (++lo != hi) { + if (std::tm* tmp = local_time(&lo, &tm)) { + if (get_offset_abbr(*tmp).first == offset) break; + } + } + return lo; + } + } + return hi; +} + } // namespace TimeZoneLibC::TimeZoneLibC(const std::string& name) : local_(name == "localtime") {} time_zone::absolute_lookup TimeZoneLibC::BreakTime( - const time_point& tp) const { + const time_point& tp) const { time_zone::absolute_lookup al; - std::time_t t = ToUnixSeconds(tp); + al.offset = 0; + al.is_dst = false; + al.abbr = "-00"; + + const std::int_fast64_t s = ToUnixSeconds(tp); + + // If std::time_t cannot hold the input we saturate the output. + if (s < std::numeric_limits::min()) { + al.cs = civil_second::min(); + return al; + } + if (s > std::numeric_limits::max()) { + al.cs = civil_second::max(); + return al; + } + + const std::time_t t = static_cast(s); std::tm tm; - if (local_) { -#if defined(_WIN32) || defined(_WIN64) - localtime_s(&tm, &t); -#else - localtime_r(&t, &tm); -#endif - std::tie(al.offset, al.abbr) = get_offset_abbr(tm); - } else { -#if defined(_WIN32) || defined(_WIN64) - gmtime_s(&tm, &t); -#else - gmtime_r(&t, &tm); -#endif - al.offset = 0; - al.abbr = "UTC"; + std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm); + + // If std::tm cannot hold the result we saturate the output. + if (tmp == nullptr) { + al.cs = (s < 0) ? civil_second::min() : civil_second::max(); + return al; } - al.cs = civil_second(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - al.is_dst = tm.tm_isdst > 0; + + const year_t year = tmp->tm_year + year_t{1900}; + al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + std::tie(al.offset, al.abbr) = get_offset_abbr(*tmp); + if (!local_) al.abbr = "UTC"; // as expected by cctz + al.is_dst = tmp->tm_isdst > 0; return al; } time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { - time_zone::civil_lookup cl; - std::time_t t; - if (local_) { - // Does not handle SKIPPED/AMBIGUOUS or huge years. - std::tm tm; - tm.tm_year = static_cast(cs.year() - 1900); - tm.tm_mon = cs.month() - 1; - tm.tm_mday = cs.day(); - tm.tm_hour = cs.hour(); - tm.tm_min = cs.minute(); - tm.tm_sec = cs.second(); - tm.tm_isdst = -1; - t = std::mktime(&tm); + if (!local_) { + // If time_point cannot hold the result we saturate. + static const civil_second min_tp_cs = + civil_second() + ToUnixSeconds(time_point::min()); + static const civil_second max_tp_cs = + civil_second() + ToUnixSeconds(time_point::max()); + const time_point tp = + (cs < min_tp_cs) + ? time_point::min() + : (cs > max_tp_cs) ? time_point::max() + : FromUnixSeconds(cs - civil_second()); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; + } + + // If tm_year cannot hold the requested year we saturate the result. + if (cs.year() < 0) { + if (cs.year() < std::numeric_limits::min() + year_t{1900}) { + const time_point tp = time_point::min(); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; + } } else { - t = cs - civil_second(); + if (cs.year() - year_t{1900} > std::numeric_limits::max()) { + const time_point tp = time_point::max(); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; + } } - cl.kind = time_zone::civil_lookup::UNIQUE; - cl.pre = cl.trans = cl.post = FromUnixSeconds(t); - return cl; -} -std::string TimeZoneLibC::Description() const { - return local_ ? "localtime" : "UTC"; + // We probe with "is_dst" values of 0 and 1 to try to distinguish unique + // civil seconds from skipped or repeated ones. This is not always possible + // however, as the "dst" flag does not change over some offset transitions. + // We are also subject to the vagaries of mktime() implementations. + std::time_t t0, t1; + int offset0, offset1; + if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) { + if (t0 == t1) { + // The civil time was singular (pre == trans == post). + const time_point tp = FromUnixSeconds(t0); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; + } + + if (t0 > t1) { + std::swap(t0, t1); + std::swap(offset0, offset1); + } + const std::time_t tt = find_trans(t0, t1, offset1); + const time_point trans = FromUnixSeconds(tt); + + if (offset0 < offset1) { + // The civil time did not exist (pre >= trans > post). + const time_point pre = FromUnixSeconds(t1); + const time_point post = FromUnixSeconds(t0); + return {time_zone::civil_lookup::SKIPPED, pre, trans, post}; + } + + // The civil time was ambiguous (pre < trans <= post). + const time_point pre = FromUnixSeconds(t0); + const time_point post = FromUnixSeconds(t1); + return {time_zone::civil_lookup::REPEATED, pre, trans, post}; + } + + // make_time() failed somehow so we saturate the result. + const time_point tp = (cs < civil_second()) + ? time_point::min() + : time_point::max(); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } -bool TimeZoneLibC::NextTransition(time_point* tp) const { +bool TimeZoneLibC::NextTransition(const time_point& tp, + time_zone::civil_transition* trans) const { return false; } -bool TimeZoneLibC::PrevTransition(time_point* tp) const { +bool TimeZoneLibC::PrevTransition(const time_point& tp, + time_zone::civil_transition* trans) const { return false; } +std::string TimeZoneLibC::Version() const { + return std::string(); // unknown +} + +std::string TimeZoneLibC::Description() const { + return local_ ? "localtime" : "UTC"; +} + } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_libc.h b/absl/time/internal/cctz/src/time_zone_libc.h index 0856b200..67372d45 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.h +++ b/absl/time/internal/cctz/src/time_zone_libc.h @@ -20,7 +20,7 @@ #include "time_zone_if.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -33,12 +33,15 @@ class TimeZoneLibC : public TimeZoneIf { // TimeZoneIf implementations. time_zone::absolute_lookup BreakTime( - const time_point& tp) const override; + const time_point& tp) const override; time_zone::civil_lookup MakeTime( const civil_second& cs) const override; + bool NextTransition(const time_point& tp, + time_zone::civil_transition* trans) const override; + bool PrevTransition(const time_point& tp, + time_zone::civil_transition* trans) const override; + std::string Version() const override; std::string Description() const override; - bool NextTransition(time_point* tp) const override; - bool PrevTransition(time_point* tp) const override; private: const bool local_; // localtime or UTC @@ -46,7 +49,7 @@ class TimeZoneLibC : public TimeZoneIf { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_ diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc index 18d1565f..711d705a 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup.cc @@ -28,7 +28,7 @@ #include "time_zone_impl.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -62,20 +62,43 @@ int __system_property_get(const char* name, char* value) { #endif std::string time_zone::name() const { - return time_zone::Impl::get(*this).name(); + return effective_impl().Name(); } time_zone::absolute_lookup time_zone::lookup( - const time_point& tp) const { - return time_zone::Impl::get(*this).BreakTime(tp); + const time_point& tp) const { + return effective_impl().BreakTime(tp); } time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const { - return time_zone::Impl::get(*this).MakeTime(cs); + return effective_impl().MakeTime(cs); } -bool operator==(time_zone lhs, time_zone rhs) { - return &time_zone::Impl::get(lhs) == &time_zone::Impl::get(rhs); +bool time_zone::next_transition(const time_point& tp, + civil_transition* trans) const { + return effective_impl().NextTransition(tp, trans); +} + +bool time_zone::prev_transition(const time_point& tp, + civil_transition* trans) const { + return effective_impl().PrevTransition(tp, trans); +} + +std::string time_zone::version() const { + return effective_impl().Version(); +} + +std::string time_zone::description() const { + return effective_impl().Description(); +} + +const time_zone::Impl& time_zone::effective_impl() const { + if (impl_ == nullptr) { + // Dereferencing an implicit-UTC time_zone is expected to be + // rare, so we don't mind paying a small synchronization cost. + return *time_zone::Impl::UTC().impl_; + } + return *impl_; } bool load_time_zone(const std::string& name, time_zone* tz) { @@ -86,7 +109,7 @@ time_zone utc_time_zone() { return time_zone::Impl::UTC(); // avoid name lookup } -time_zone fixed_time_zone(const sys_seconds& offset) { +time_zone fixed_time_zone(const seconds& offset) { time_zone tz; load_time_zone(FixedOffsetToName(offset), &tz); return tz; @@ -143,5 +166,5 @@ time_zone local_time_zone() { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index a1b6c687..e0e355ee 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -16,7 +16,9 @@ #include #include +#include #include +#include #include #include #include @@ -24,17 +26,10 @@ #include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "gtest/gtest.h" -using std::chrono::time_point_cast; -using std::chrono::system_clock; -using std::chrono::nanoseconds; -using std::chrono::microseconds; -using std::chrono::milliseconds; -using std::chrono::seconds; -using std::chrono::minutes; -using std::chrono::hours; +namespace chrono = std::chrono; namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -659,6 +654,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); +} + } // namespace TEST(TimeZones, LoadZonesConcurrently) { @@ -716,13 +722,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(sys_seconds::zero()); + const time_zone fixed0 = fixed_time_zone(absl::time_internal::cctz::seconds::zero()); EXPECT_EQ("UTC", fixed0.name()); - const time_zone fixed_pos = - fixed_time_zone(hours(3) + minutes(25) + 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(-(hours(12) + minutes(34) + 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()); } @@ -732,19 +738,19 @@ TEST(TimeZone, Failures) { tz = LoadZone("America/Los_Angeles"); EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz)); - EXPECT_EQ(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(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 std::string timezone should fail. tz = LoadZone("America/Los_Angeles"); EXPECT_FALSE(load_time_zone("", &tz)); - EXPECT_EQ(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 } @@ -759,7 +765,7 @@ TEST(TimeZone, Equality) { EXPECT_EQ(implicit_utc, explicit_utc); EXPECT_EQ(implicit_utc.name(), explicit_utc.name()); - const time_zone fixed_zero = fixed_time_zone(sys_seconds::zero()); + const time_zone fixed_zero = fixed_time_zone(absl::time_internal::cctz::seconds::zero()); EXPECT_EQ(fixed_zero, LoadZone(fixed_zero.name())); EXPECT_EQ(fixed_zero, explicit_utc); @@ -767,23 +773,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(hours(3) + minutes(25) + 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(-(hours(12) + minutes(34) + 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(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(hours(24) + 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(seconds(1)), fixed_time_zone(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())); @@ -796,40 +804,43 @@ 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 = system_clock::time_point() - system_clock::from_time_t(0); - EXPECT_EQ(system_clock::time_point::duration::zero(), diff % 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 = system_clock::from_time_t(0); + const auto t0 = chrono::system_clock::from_time_t(0); - ExpectTime(time_point_cast(t0), utc, + ExpectTime(chrono::time_point_cast(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(time_point_cast(t0), utc, + ExpectTime(chrono::time_point_cast(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(time_point_cast(t0), utc, + ExpectTime(chrono::time_point_cast(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(time_point_cast(t0), utc, + ExpectTime(chrono::time_point_cast(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(time_point_cast(t0), utc, + ExpectTime(chrono::time_point_cast(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(time_point_cast(t0), utc, + ExpectTime(chrono::time_point_cast(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - ExpectTime(time_point_cast(t0), utc, + ExpectTime(chrono::time_point_cast(t0), utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); } TEST(BreakTime, LocalTimeInUTC) { const time_zone tz = utc_time_zone(); - const auto tp = 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(civil_day(convert(tp, tz)))); } TEST(BreakTime, LocalTimeInUTCUnaligned) { const time_zone tz = utc_time_zone(); - const auto tp = system_clock::from_time_t(0) - 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(civil_day(convert(tp, tz)))); } @@ -837,15 +848,16 @@ TEST(BreakTime, LocalTimeInUTCUnaligned) { 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 = 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(civil_day(convert(tp, tz)))); } TEST(TimeZoneImpl, LocalTimeInFixed) { - const sys_seconds offset = -(hours(8) + minutes(33) + seconds(47)); + const absl::time_internal::cctz::seconds offset = + -(chrono::hours(8) + chrono::minutes(33) + chrono::seconds(47)); const time_zone tz = fixed_time_zone(offset); - const auto tp = 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(civil_day(convert(tp, tz)))); @@ -853,52 +865,52 @@ TEST(TimeZoneImpl, LocalTimeInFixed) { TEST(BreakTime, LocalTimeInNewYork) { const time_zone tz = LoadZone("America/New_York"); - const auto tp = 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(civil_day(convert(tp, tz)))); } TEST(BreakTime, LocalTimeInMTV) { const time_zone tz = LoadZone("America/Los_Angeles"); - const auto tp = 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(civil_day(convert(tp, tz)))); } TEST(BreakTime, LocalTimeInSydney) { const time_zone tz = LoadZone("Australia/Sydney"); - const auto tp = 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(civil_day(convert(tp, tz)))); } TEST(MakeTime, TimePointResolution) { const time_zone utc = utc_time_zone(); - const time_point tp_ns = + const time_point 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 tp_us = + const time_point 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 tp_ms = + const time_point 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 tp_s = + const time_point 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 tp_s64 = + const time_point 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 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 tp_m = - time_point_cast( + // 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 tp_m = + chrono::time_point_cast( convert(civil_second(2015, 1, 2, 3, 4, 5), utc)); EXPECT_EQ("04:00", format("%M:%E*S", tp_m, utc)); - const time_point tp_h = - time_point_cast( + const time_point tp_h = + chrono::time_point_cast( convert(civil_second(2015, 1, 2, 3, 4, 5), utc)); EXPECT_EQ("00:00", format("%M:%E*S", tp_h, utc)); } @@ -906,7 +918,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(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 @@ -916,71 +928,234 @@ TEST(MakeTime, Normalization) { EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 18, 30, 90), tz)); // second } -// NOTE: Run this with --copt=-ftrapv to detect overflow problems. +// NOTE: Run this with -ftrapv to detect overflow problems. TEST(MakeTime, SysSecondsLimits) { const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez"; const time_zone utc = utc_time_zone(); - const time_zone east = fixed_time_zone(hours(14)); - const time_zone west = fixed_time_zone(-hours(14)); - time_point tp; + const time_zone east = fixed_time_zone(chrono::hours(14)); + const time_zone west = fixed_time_zone(-chrono::hours(14)); + time_point tp; - // Approach the maximal time_point value from below. + // Approach the maximal time_point 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::max(), tp); + EXPECT_EQ(time_point::max(), tp); tp = convert(civil_second(292277026596, 12, 4, 15, 30, 8), utc); - EXPECT_EQ(time_point::max(), tp); + EXPECT_EQ(time_point::max(), tp); tp = convert(civil_second::max(), utc); - EXPECT_EQ(time_point::max(), tp); + EXPECT_EQ(time_point::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::max(), tp); + EXPECT_EQ(time_point::max(), tp); tp = convert(civil_second(292277026596, 12, 5, 5, 30, 8), east); - EXPECT_EQ(time_point::max(), tp); + EXPECT_EQ(time_point::max(), tp); tp = convert(civil_second::max(), east); - EXPECT_EQ(time_point::max(), tp); + EXPECT_EQ(time_point::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::max(), tp); + EXPECT_EQ(time_point::max(), tp); tp = convert(civil_second(292277026596, 12, 4, 7, 30, 8), west); - EXPECT_EQ(time_point::max(), tp); + EXPECT_EQ(time_point::max(), tp); tp = convert(civil_second::max(), west); - EXPECT_EQ(time_point::max(), tp); + EXPECT_EQ(time_point::max(), tp); - // Approach the minimal time_point value from above. + // Approach the minimal time_point 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::min(), tp); + EXPECT_EQ(time_point::min(), tp); tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 51), utc); - EXPECT_EQ(time_point::min(), tp); + EXPECT_EQ(time_point::min(), tp); tp = convert(civil_second::min(), utc); - EXPECT_EQ(time_point::min(), tp); + EXPECT_EQ(time_point::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::min(), tp); + EXPECT_EQ(time_point::min(), tp); tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 51), east); - EXPECT_EQ(time_point::min(), tp); + EXPECT_EQ(time_point::min(), tp); tp = convert(civil_second::min(), east); - EXPECT_EQ(time_point::min(), tp); + EXPECT_EQ(time_point::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::min(), tp); + EXPECT_EQ(time_point::min(), tp); tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 51), west); - EXPECT_EQ(time_point::min(), tp); + EXPECT_EQ(time_point::min(), tp); tp = convert(civil_second::min(), west); - EXPECT_EQ(time_point::min(), tp); + EXPECT_EQ(time_point::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 utc = LoadZone("libc:UTC"); + const year_t max_tm_year = year_t{std::numeric_limits::max()} + 1900; + tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), utc); + EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, utc)); + const year_t min_tm_year = year_t{std::numeric_limits::min()} + 1900; + tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), utc); + EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, utc)); +#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__) + 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 trans; + for (auto tp = zi.lookup(civil_second()).trans; + zi.next_transition(tp, &trans); + tp = zi.lookup(trans.to).trans) { + const auto fcl = zi.lookup(trans.from); + const auto tcl = zi.lookup(trans.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(trans.from, trans.to); + const auto trans = fcl.trans; + const auto tal = zi.lookup(trans); + const auto tprev = trans - absl::time_internal::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 = trans.to; + } else { + ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind); + ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind); + cs = trans.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::min(); + EXPECT_FALSE(tz.next_transition(tp, &trans)); + + tp = time_point::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::max(); + EXPECT_FALSE(tz.prev_transition(tp, &trans)); + + tp = time_point::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::max(); + EXPECT_FALSE(tz.next_transition(tp, &trans)); + + tp = time_point::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::min(); + EXPECT_FALSE(tz.prev_transition(tp, &trans)); + + tp = time_point::max(); + EXPECT_TRUE(tz.prev_transition(tp, &trans)); + // We have a transition but we don't know which one. } TEST(TimeZoneEdgeCase, AmericaNewYork) { @@ -989,13 +1164,13 @@ TEST(TimeZoneEdgeCase, AmericaNewYork) { // 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 += seconds(1); + tp += absl::time_internal::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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -5 * 3600, false, "EST"); } @@ -1005,13 +1180,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 += seconds(1); + tp += absl::time_internal::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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -8 * 3600, false, "PST"); } @@ -1021,13 +1196,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 += seconds(1); + tp += absl::time_internal::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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 2013, 11, 3, 2, 0, 0, -7 * 3600, false, "MST"); } @@ -1040,7 +1215,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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 1986, 1, 1, 0, 15, 0, 5.75 * 3600, false, "+0545"); } @@ -1053,14 +1228,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 += seconds(1); + tp += absl::time_internal::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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 2013, 9, 29, 3, 45, 0, 13.75 * 3600, true, "+1345"); } @@ -1073,14 +1248,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 += seconds(1); + tp += absl::time_internal::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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 2013, 10, 6, 2, 30, 0, 11 * 3600, true, "+11"); } @@ -1098,7 +1273,7 @@ TEST(TimeZoneEdgeCase, PacificApia) { 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(civil_day(convert(tp, tz)))); - tp += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 2011, 12, 31, 0, 0, 0, 14 * 3600, true, "+14"); EXPECT_EQ(365, get_yearday(civil_day(convert(tp, tz)))); } @@ -1106,35 +1281,31 @@ TEST(TimeZoneEdgeCase, PacificApia) { TEST(TimeZoneEdgeCase, AfricaCairo) { const time_zone tz = LoadZone("Africa/Cairo"); -#if defined(__ANDROID__) && __ANDROID_API__ < 21 - // Only Android 'L' and beyond have this tz2014c transition. -#else - // 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 += seconds(1); - ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST"); -#endif + 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 += absl::time_internal::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 defined(__ANDROID__) && __ANDROID_API__ < 26 - // Only Android 'O' and beyond have this tz2017b transition. -#else - // 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 += seconds(1); - ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT"); -#endif + 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 += absl::time_internal::cctz::seconds(1); + ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT"); + } } TEST(TimeZoneEdgeCase, AmericaJamaica) { @@ -1146,30 +1317,31 @@ TEST(TimeZoneEdgeCase, AmericaJamaica) { const time_zone tz = LoadZone("America/Jamaica"); // Before the first transition. - auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz); -#if AMERICA_JAMAICA_PRE_1913_OFFSET_FIX - // Commit 907241e: Fix off-by-1 error for Jamaica and T&C before 1913. - // Until that commit has made its way into a full release we avoid the - // expectations on the -18430 offset below. TODO: Uncomment these. - 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 += seconds(1); - ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT"); -#endif + 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 += absl::time_internal::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) - 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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST"); // After the last transition. @@ -1190,7 +1362,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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 1977, 4, 3, 2, 0, 0, 1 * 3600, true, "WEST"); // A non-existent time within the first transition. @@ -1212,12 +1384,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(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(system_clock::from_time_t(-5 * 3600), tp); + EXPECT_EQ(chrono::system_clock::from_time_t(-5 * 3600), tp); } TEST(TimeZoneEdgeCase, NegativeYear) { @@ -1226,7 +1398,7 @@ TEST(TimeZoneEdgeCase, NegativeYear) { 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(civil_day(convert(tp, tz)))); - tp -= seconds(1); + tp -= absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, -1, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC"); EXPECT_EQ(weekday::friday, get_weekday(civil_day(convert(tp, tz)))); } @@ -1240,7 +1412,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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 8, 0 * 3600, false, "UTC"); } @@ -1253,11 +1425,11 @@ 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 += seconds(1); + tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 10000, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC"); } } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_posix.cc b/absl/time/internal/cctz/src/time_zone_posix.cc index b6cf2875..960133d7 100644 --- a/absl/time/internal/cctz/src/time_zone_posix.cc +++ b/absl/time/internal/cctz/src/time_zone_posix.cc @@ -20,7 +20,7 @@ #include namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -153,5 +153,5 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_posix.h b/absl/time/internal/cctz/src/time_zone_posix.h index 91443a21..84c39afb 100644 --- a/absl/time/internal/cctz/src/time_zone_posix.h +++ b/absl/time/internal/cctz/src/time_zone_posix.h @@ -56,7 +56,7 @@ #include namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { @@ -69,28 +69,38 @@ namespace cctz { // it would take us to another day, and perhaps week, or even month. struct PosixTransition { enum DateFormat { J, N, M }; - struct { + + struct Date { + struct NonLeapDay { + std::int_fast16_t day; // day of non-leap year [1:365] + }; + struct Day { + std::int_fast16_t day; // day of year [0:365] + }; + struct MonthWeekWeekday { + std::int_fast8_t month; // month of year [1:12] + std::int_fast8_t week; // week of month [1:5] (5==last) + std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat + }; + DateFormat fmt; + union { - struct { - std::int_fast16_t day; // day of non-leap year [1:365] - } j; - struct { - std::int_fast16_t day; // day of year [0:365] - } n; - struct { - std::int_fast8_t month; // month of year [1:12] - std::int_fast8_t week; // week of month [1:5] (5==last) - std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat - } m; + NonLeapDay j; + Day n; + MonthWeekWeekday m; }; - } date; - struct { + }; + + struct Time { std::int_fast32_t offset; // seconds before/after 00:00:00 - } time; + }; + + Date date; + Time time; }; -// The entirety of a POSIX-std::string specified time-zone rule. The standard +// The entirety of a POSIX-string specified time-zone rule. The standard // abbreviation and offset are always given. If the time zone includes // daylight saving, then the daylight abbrevation is non-empty and the // remaining fields are also valid. Note that the start/end transitions @@ -114,7 +124,7 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res); } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_ diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h index 90cfc0c4..4485ba55 100644 --- a/absl/time/internal/cctz/src/tzfile.h +++ b/absl/time/internal/cctz/src/tzfile.h @@ -1,3 +1,5 @@ +/* Layout and location of TZif files. */ + #ifndef TZFILE_H #define TZFILE_H diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc index a73e1c92..61669e7a 100644 --- a/absl/time/internal/cctz/src/zone_info_source.cc +++ b/absl/time/internal/cctz/src/zone_info_source.cc @@ -15,20 +15,21 @@ #include "absl/time/internal/cctz/include/cctz/zone_info_source.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz { // Defined out-of-line to avoid emitting a weak vtable in all TUs. ZoneInfoSource::~ZoneInfoSource() {} +std::string ZoneInfoSource::Version() const { return std::string(); } } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { namespace cctz_extension { @@ -55,20 +56,28 @@ ZoneInfoSourceFactory default_factory = DefaultFactory; #if defined(_M_IX86) #pragma comment( \ linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA=?default_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA") + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA=?default_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA") #elif defined(_M_IA_64) || defined(_M_AMD64) #pragma comment( \ linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA=?default_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA") + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA=?default_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA") #else #error Unsupported MSVC platform #endif -#else +#else // _MSC_VER +#if !defined(__has_attribute) +#define __has_attribute(x) 0 +#endif +#if __has_attribute(weak) || defined(__GNUC__) ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) = DefaultFactory; +#else +// Make it a "strong" definition if we have no other choice. +ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory; +#endif #endif // _MSC_VER } // namespace cctz_extension } // namespace time_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl -- cgit v1.2.3