diff options
Diffstat (limited to 'absl/time/internal/cctz')
32 files changed, 934 insertions, 638 deletions
diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel index edeabd81..4c5ad075 100644 --- a/absl/time/internal/cctz/BUILD.bazel +++ b/absl/time/internal/cctz/BUILD.bazel @@ -53,10 +53,11 @@ cc_library( "include/cctz/time_zone.h", "include/cctz/zone_info_source.h", ], - # OS X and iOS no longer use `linkopts = ["-framework CoreFoundation"]` - # as (1) bazel adds it automatically, and (2) it caused problems when - # cross-compiling for Android. - # See https://github.com/abseil/abseil-cpp/issues/326 for details. + linkopts = select({ + "@platforms//os:osx": ["-Wl,-framework,CoreFoundation"], + "@platforms//os:ios": ["-Wl,-framework,CoreFoundation"], + "//conditions:default": [], + }), visibility = ["//visibility:public"], deps = [ ":civil_time", diff --git a/absl/time/internal/cctz/include/cctz/time_zone.h b/absl/time/internal/cctz/include/cctz/time_zone.h index 6e382dc6..b2b0cf6f 100644 --- a/absl/time/internal/cctz/include/cctz/time_zone.h +++ b/absl/time/internal/cctz/include/cctz/time_zone.h @@ -23,6 +23,7 @@ #include <chrono> #include <cstdint> #include <limits> +#include <ratio> // NOLINT: We use std::ratio in this header #include <string> #include <utility> diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc index f2b3294e..e09654ea 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.cc +++ b/absl/time/internal/cctz/src/time_zone_fixed.cc @@ -105,7 +105,7 @@ std::string FixedOffsetToName(const seconds& offset) { offset_minutes %= 60; const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; char buf[prefix_len + sizeof("-24:00:00")]; - char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf); + char* ep = std::copy_n(kFixedZonePrefix, prefix_len, buf); *ep++ = sign; ep = Format02d(ep, offset_hours); *ep++ = ':'; diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc index 2e5f5329..9b91f61c 100644 --- a/absl/time/internal/cctz/src/time_zone_format.cc +++ b/absl/time/internal/cctz/src/time_zone_format.cc @@ -13,14 +13,14 @@ // limitations under the License. #if !defined(HAS_STRPTIME) -#if !defined(_MSC_VER) && !defined(__MINGW32__) -#define HAS_STRPTIME 1 // assume everyone has strptime() except windows +#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__VXWORKS__) +#define HAS_STRPTIME 1 // Assume everyone else has strptime(). #endif #endif #if defined(HAS_STRPTIME) && HAS_STRPTIME #if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) -#define _XOPEN_SOURCE // Definedness suffices for strptime. +#define _XOPEN_SOURCE 500 // Exposes definitions for SUSv2 (UNIX 98). #endif #endif 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 f1f79a20..4a6c71f1 100644 --- a/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -64,10 +64,13 @@ const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; template <typename D> void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt, const std::string& ans) { - EXPECT_EQ(ans, format(fmt, tp, tz)) << fmt; - EXPECT_EQ("xxx " + ans, format("xxx " + fmt, tp, tz)); - EXPECT_EQ(ans + " yyy", format(fmt + " yyy", tp, tz)); - EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz)); + EXPECT_EQ(ans, absl::time_internal::cctz::format(fmt, tp, tz)) << fmt; + EXPECT_EQ("xxx " + ans, + absl::time_internal::cctz::format("xxx " + fmt, tp, tz)); + EXPECT_EQ(ans + " yyy", + absl::time_internal::cctz::format(fmt + " yyy", tp, tz)); + EXPECT_EQ("xxx " + ans + " yyy", + absl::time_internal::cctz::format("xxx " + fmt + " yyy", tp, tz)); } } // namespace @@ -83,26 +86,29 @@ TEST(Format, TimePointResolution) { chrono::system_clock::from_time_t(1420167845) + chrono::milliseconds(123) + chrono::microseconds(456) + chrono::nanoseconds(789); - EXPECT_EQ( - "03:04:05.123456789", - format(kFmt, chrono::time_point_cast<chrono::nanoseconds>(t0), utc)); - EXPECT_EQ( - "03:04:05.123456", - format(kFmt, chrono::time_point_cast<chrono::microseconds>(t0), utc)); - EXPECT_EQ( - "03:04:05.123", - format(kFmt, chrono::time_point_cast<chrono::milliseconds>(t0), utc)); + EXPECT_EQ("03:04:05.123456789", + absl::time_internal::cctz::format( + kFmt, chrono::time_point_cast<chrono::nanoseconds>(t0), utc)); + EXPECT_EQ("03:04:05.123456", + absl::time_internal::cctz::format( + kFmt, chrono::time_point_cast<chrono::microseconds>(t0), utc)); + EXPECT_EQ("03:04:05.123", + absl::time_internal::cctz::format( + kFmt, chrono::time_point_cast<chrono::milliseconds>(t0), utc)); EXPECT_EQ("03:04:05", - format(kFmt, chrono::time_point_cast<chrono::seconds>(t0), utc)); + absl::time_internal::cctz::format( + kFmt, chrono::time_point_cast<chrono::seconds>(t0), utc)); EXPECT_EQ( "03:04:05", - format(kFmt, - chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), - utc)); + absl::time_internal::cctz::format( + kFmt, chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), + utc)); EXPECT_EQ("03:04:00", - format(kFmt, chrono::time_point_cast<chrono::minutes>(t0), utc)); + absl::time_internal::cctz::format( + kFmt, chrono::time_point_cast<chrono::minutes>(t0), utc)); EXPECT_EQ("03:00:00", - format(kFmt, chrono::time_point_cast<chrono::hours>(t0), utc)); + absl::time_internal::cctz::format( + kFmt, chrono::time_point_cast<chrono::hours>(t0), utc)); } TEST(Format, TimePointExtendedResolution) { @@ -137,24 +143,28 @@ TEST(Format, Basics) { time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); // Starts with a couple basic edge cases. - EXPECT_EQ("", format("", tp, tz)); - EXPECT_EQ(" ", format(" ", tp, tz)); - EXPECT_EQ(" ", format(" ", tp, tz)); - EXPECT_EQ("xxx", format("xxx", tp, tz)); + EXPECT_EQ("", absl::time_internal::cctz::format("", tp, tz)); + EXPECT_EQ(" ", absl::time_internal::cctz::format(" ", tp, tz)); + EXPECT_EQ(" ", absl::time_internal::cctz::format(" ", tp, tz)); + EXPECT_EQ("xxx", absl::time_internal::cctz::format("xxx", tp, tz)); std::string big(128, 'x'); - EXPECT_EQ(big, format(big, tp, tz)); + EXPECT_EQ(big, absl::time_internal::cctz::format(big, tp, tz)); // Cause the 1024-byte buffer to grow. std::string bigger(100000, 'x'); - EXPECT_EQ(bigger, format(bigger, tp, tz)); + EXPECT_EQ(bigger, absl::time_internal::cctz::format(bigger, tp, tz)); tp += chrono::hours(13) + chrono::minutes(4) + chrono::seconds(5); tp += chrono::milliseconds(6) + chrono::microseconds(7) + chrono::nanoseconds(8); - EXPECT_EQ("1970-01-01", format("%Y-%m-%d", tp, tz)); - EXPECT_EQ("13:04:05", format("%H:%M:%S", tp, tz)); - EXPECT_EQ("13:04:05.006", format("%H:%M:%E3S", tp, tz)); - EXPECT_EQ("13:04:05.006007", format("%H:%M:%E6S", tp, tz)); - EXPECT_EQ("13:04:05.006007008", format("%H:%M:%E9S", tp, tz)); + EXPECT_EQ("1970-01-01", + absl::time_internal::cctz::format("%Y-%m-%d", tp, tz)); + EXPECT_EQ("13:04:05", absl::time_internal::cctz::format("%H:%M:%S", tp, tz)); + EXPECT_EQ("13:04:05.006", + absl::time_internal::cctz::format("%H:%M:%E3S", tp, tz)); + EXPECT_EQ("13:04:05.006007", + absl::time_internal::cctz::format("%H:%M:%E6S", tp, tz)); + EXPECT_EQ("13:04:05.006007008", + absl::time_internal::cctz::format("%H:%M:%E9S", tp, tz)); } TEST(Format, PosixConversions) { @@ -211,7 +221,8 @@ TEST(Format, LocaleSpecific) { TestFormatSpecifier(tp, tz, "%B", "January"); // %c should at least produce the numeric year and time-of-day. - const std::string s = format("%c", tp, utc_time_zone()); + const std::string s = + absl::time_internal::cctz::format("%c", tp, utc_time_zone()); EXPECT_THAT(s, testing::HasSubstr("1970")); EXPECT_THAT(s, testing::HasSubstr("00:00:00")); @@ -277,49 +288,61 @@ TEST(Format, ExtendedSeconds) { // No subseconds. time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); tp += chrono::seconds(5); - EXPECT_EQ("05", format("%E*S", tp, tz)); - EXPECT_EQ("05", format("%E0S", tp, tz)); - EXPECT_EQ("05.0", format("%E1S", tp, tz)); - EXPECT_EQ("05.00", format("%E2S", tp, tz)); - EXPECT_EQ("05.000", format("%E3S", tp, tz)); - EXPECT_EQ("05.0000", format("%E4S", tp, tz)); - EXPECT_EQ("05.00000", format("%E5S", tp, tz)); - EXPECT_EQ("05.000000", format("%E6S", tp, tz)); - EXPECT_EQ("05.0000000", format("%E7S", tp, tz)); - EXPECT_EQ("05.00000000", format("%E8S", tp, tz)); - EXPECT_EQ("05.000000000", format("%E9S", tp, tz)); - EXPECT_EQ("05.0000000000", format("%E10S", tp, tz)); - EXPECT_EQ("05.00000000000", format("%E11S", tp, tz)); - EXPECT_EQ("05.000000000000", format("%E12S", tp, tz)); - EXPECT_EQ("05.0000000000000", format("%E13S", tp, tz)); - EXPECT_EQ("05.00000000000000", format("%E14S", tp, tz)); - EXPECT_EQ("05.000000000000000", format("%E15S", tp, tz)); + EXPECT_EQ("05", absl::time_internal::cctz::format("%E*S", tp, tz)); + EXPECT_EQ("05", absl::time_internal::cctz::format("%E0S", tp, tz)); + EXPECT_EQ("05.0", absl::time_internal::cctz::format("%E1S", tp, tz)); + EXPECT_EQ("05.00", absl::time_internal::cctz::format("%E2S", tp, tz)); + EXPECT_EQ("05.000", absl::time_internal::cctz::format("%E3S", tp, tz)); + EXPECT_EQ("05.0000", absl::time_internal::cctz::format("%E4S", tp, tz)); + EXPECT_EQ("05.00000", absl::time_internal::cctz::format("%E5S", tp, tz)); + EXPECT_EQ("05.000000", absl::time_internal::cctz::format("%E6S", tp, tz)); + EXPECT_EQ("05.0000000", absl::time_internal::cctz::format("%E7S", tp, tz)); + EXPECT_EQ("05.00000000", absl::time_internal::cctz::format("%E8S", tp, tz)); + EXPECT_EQ("05.000000000", absl::time_internal::cctz::format("%E9S", tp, tz)); + EXPECT_EQ("05.0000000000", + absl::time_internal::cctz::format("%E10S", tp, tz)); + EXPECT_EQ("05.00000000000", + absl::time_internal::cctz::format("%E11S", tp, tz)); + EXPECT_EQ("05.000000000000", + absl::time_internal::cctz::format("%E12S", tp, tz)); + EXPECT_EQ("05.0000000000000", + absl::time_internal::cctz::format("%E13S", tp, tz)); + EXPECT_EQ("05.00000000000000", + absl::time_internal::cctz::format("%E14S", tp, tz)); + EXPECT_EQ("05.000000000000000", + absl::time_internal::cctz::format("%E15S", tp, tz)); // With subseconds. tp += chrono::milliseconds(6) + chrono::microseconds(7) + chrono::nanoseconds(8); - EXPECT_EQ("05.006007008", format("%E*S", tp, tz)); - EXPECT_EQ("05", format("%E0S", tp, tz)); - EXPECT_EQ("05.0", format("%E1S", tp, tz)); - EXPECT_EQ("05.00", format("%E2S", tp, tz)); - EXPECT_EQ("05.006", format("%E3S", tp, tz)); - EXPECT_EQ("05.0060", format("%E4S", tp, tz)); - EXPECT_EQ("05.00600", format("%E5S", tp, tz)); - EXPECT_EQ("05.006007", format("%E6S", tp, tz)); - EXPECT_EQ("05.0060070", format("%E7S", tp, tz)); - EXPECT_EQ("05.00600700", format("%E8S", tp, tz)); - EXPECT_EQ("05.006007008", format("%E9S", tp, tz)); - EXPECT_EQ("05.0060070080", format("%E10S", tp, tz)); - EXPECT_EQ("05.00600700800", format("%E11S", tp, tz)); - EXPECT_EQ("05.006007008000", format("%E12S", tp, tz)); - EXPECT_EQ("05.0060070080000", format("%E13S", tp, tz)); - EXPECT_EQ("05.00600700800000", format("%E14S", tp, tz)); - EXPECT_EQ("05.006007008000000", format("%E15S", tp, tz)); + EXPECT_EQ("05.006007008", absl::time_internal::cctz::format("%E*S", tp, tz)); + EXPECT_EQ("05", absl::time_internal::cctz::format("%E0S", tp, tz)); + EXPECT_EQ("05.0", absl::time_internal::cctz::format("%E1S", tp, tz)); + EXPECT_EQ("05.00", absl::time_internal::cctz::format("%E2S", tp, tz)); + EXPECT_EQ("05.006", absl::time_internal::cctz::format("%E3S", tp, tz)); + EXPECT_EQ("05.0060", absl::time_internal::cctz::format("%E4S", tp, tz)); + EXPECT_EQ("05.00600", absl::time_internal::cctz::format("%E5S", tp, tz)); + EXPECT_EQ("05.006007", absl::time_internal::cctz::format("%E6S", tp, tz)); + EXPECT_EQ("05.0060070", absl::time_internal::cctz::format("%E7S", tp, tz)); + EXPECT_EQ("05.00600700", absl::time_internal::cctz::format("%E8S", tp, tz)); + EXPECT_EQ("05.006007008", absl::time_internal::cctz::format("%E9S", tp, tz)); + EXPECT_EQ("05.0060070080", + absl::time_internal::cctz::format("%E10S", tp, tz)); + EXPECT_EQ("05.00600700800", + absl::time_internal::cctz::format("%E11S", tp, tz)); + EXPECT_EQ("05.006007008000", + absl::time_internal::cctz::format("%E12S", tp, tz)); + EXPECT_EQ("05.0060070080000", + absl::time_internal::cctz::format("%E13S", tp, tz)); + EXPECT_EQ("05.00600700800000", + absl::time_internal::cctz::format("%E14S", tp, tz)); + EXPECT_EQ("05.006007008000000", + absl::time_internal::cctz::format("%E15S", tp, tz)); // Times before the Unix epoch. tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1); EXPECT_EQ("1969-12-31 23:59:59.999999", - format("%Y-%m-%d %H:%M:%E*S", tp, tz)); + absl::time_internal::cctz::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 @@ -327,10 +350,10 @@ TEST(Format, ExtendedSeconds) { 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)); + absl::time_internal::cctz::format("%Y-%m-%d %H:%M:%E*S", tp, tz)); tp += chrono::microseconds(1); EXPECT_EQ("2014-03-17 02:47:07.333305", - format("%Y-%m-%d %H:%M:%E*S", tp, tz)); + absl::time_internal::cctz::format("%Y-%m-%d %H:%M:%E*S", tp, tz)); } TEST(Format, ExtendedSubeconds) { @@ -339,60 +362,69 @@ TEST(Format, ExtendedSubeconds) { // No subseconds. time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); tp += chrono::seconds(5); - EXPECT_EQ("0", format("%E*f", tp, tz)); - EXPECT_EQ("", format("%E0f", tp, tz)); - EXPECT_EQ("0", format("%E1f", tp, tz)); - EXPECT_EQ("00", format("%E2f", tp, tz)); - EXPECT_EQ("000", format("%E3f", tp, tz)); - EXPECT_EQ("0000", format("%E4f", tp, tz)); - EXPECT_EQ("00000", format("%E5f", tp, tz)); - EXPECT_EQ("000000", format("%E6f", tp, tz)); - EXPECT_EQ("0000000", format("%E7f", tp, tz)); - EXPECT_EQ("00000000", format("%E8f", tp, tz)); - EXPECT_EQ("000000000", format("%E9f", tp, tz)); - EXPECT_EQ("0000000000", format("%E10f", tp, tz)); - EXPECT_EQ("00000000000", format("%E11f", tp, tz)); - EXPECT_EQ("000000000000", format("%E12f", tp, tz)); - EXPECT_EQ("0000000000000", format("%E13f", tp, tz)); - EXPECT_EQ("00000000000000", format("%E14f", tp, tz)); - EXPECT_EQ("000000000000000", format("%E15f", tp, tz)); + EXPECT_EQ("0", absl::time_internal::cctz::format("%E*f", tp, tz)); + EXPECT_EQ("", absl::time_internal::cctz::format("%E0f", tp, tz)); + EXPECT_EQ("0", absl::time_internal::cctz::format("%E1f", tp, tz)); + EXPECT_EQ("00", absl::time_internal::cctz::format("%E2f", tp, tz)); + EXPECT_EQ("000", absl::time_internal::cctz::format("%E3f", tp, tz)); + EXPECT_EQ("0000", absl::time_internal::cctz::format("%E4f", tp, tz)); + EXPECT_EQ("00000", absl::time_internal::cctz::format("%E5f", tp, tz)); + EXPECT_EQ("000000", absl::time_internal::cctz::format("%E6f", tp, tz)); + EXPECT_EQ("0000000", absl::time_internal::cctz::format("%E7f", tp, tz)); + EXPECT_EQ("00000000", absl::time_internal::cctz::format("%E8f", tp, tz)); + EXPECT_EQ("000000000", absl::time_internal::cctz::format("%E9f", tp, tz)); + EXPECT_EQ("0000000000", absl::time_internal::cctz::format("%E10f", tp, tz)); + EXPECT_EQ("00000000000", absl::time_internal::cctz::format("%E11f", tp, tz)); + EXPECT_EQ("000000000000", absl::time_internal::cctz::format("%E12f", tp, tz)); + EXPECT_EQ("0000000000000", + absl::time_internal::cctz::format("%E13f", tp, tz)); + EXPECT_EQ("00000000000000", + absl::time_internal::cctz::format("%E14f", tp, tz)); + EXPECT_EQ("000000000000000", + absl::time_internal::cctz::format("%E15f", tp, tz)); // With subseconds. tp += chrono::milliseconds(6) + chrono::microseconds(7) + chrono::nanoseconds(8); - EXPECT_EQ("006007008", format("%E*f", tp, tz)); - EXPECT_EQ("", format("%E0f", tp, tz)); - EXPECT_EQ("0", format("%E1f", tp, tz)); - EXPECT_EQ("00", format("%E2f", tp, tz)); - EXPECT_EQ("006", format("%E3f", tp, tz)); - EXPECT_EQ("0060", format("%E4f", tp, tz)); - EXPECT_EQ("00600", format("%E5f", tp, tz)); - EXPECT_EQ("006007", format("%E6f", tp, tz)); - EXPECT_EQ("0060070", format("%E7f", tp, tz)); - EXPECT_EQ("00600700", format("%E8f", tp, tz)); - EXPECT_EQ("006007008", format("%E9f", tp, tz)); - EXPECT_EQ("0060070080", format("%E10f", tp, tz)); - EXPECT_EQ("00600700800", format("%E11f", tp, tz)); - EXPECT_EQ("006007008000", format("%E12f", tp, tz)); - EXPECT_EQ("0060070080000", format("%E13f", tp, tz)); - EXPECT_EQ("00600700800000", format("%E14f", tp, tz)); - EXPECT_EQ("006007008000000", format("%E15f", tp, tz)); + EXPECT_EQ("006007008", absl::time_internal::cctz::format("%E*f", tp, tz)); + EXPECT_EQ("", absl::time_internal::cctz::format("%E0f", tp, tz)); + EXPECT_EQ("0", absl::time_internal::cctz::format("%E1f", tp, tz)); + EXPECT_EQ("00", absl::time_internal::cctz::format("%E2f", tp, tz)); + EXPECT_EQ("006", absl::time_internal::cctz::format("%E3f", tp, tz)); + EXPECT_EQ("0060", absl::time_internal::cctz::format("%E4f", tp, tz)); + EXPECT_EQ("00600", absl::time_internal::cctz::format("%E5f", tp, tz)); + EXPECT_EQ("006007", absl::time_internal::cctz::format("%E6f", tp, tz)); + EXPECT_EQ("0060070", absl::time_internal::cctz::format("%E7f", tp, tz)); + EXPECT_EQ("00600700", absl::time_internal::cctz::format("%E8f", tp, tz)); + EXPECT_EQ("006007008", absl::time_internal::cctz::format("%E9f", tp, tz)); + EXPECT_EQ("0060070080", absl::time_internal::cctz::format("%E10f", tp, tz)); + EXPECT_EQ("00600700800", absl::time_internal::cctz::format("%E11f", tp, tz)); + EXPECT_EQ("006007008000", absl::time_internal::cctz::format("%E12f", tp, tz)); + EXPECT_EQ("0060070080000", + absl::time_internal::cctz::format("%E13f", tp, tz)); + EXPECT_EQ("00600700800000", + absl::time_internal::cctz::format("%E14f", tp, tz)); + EXPECT_EQ("006007008000000", + absl::time_internal::cctz::format("%E15f", tp, tz)); // Times before the Unix epoch. tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(-1); - EXPECT_EQ("1969-12-31 23:59:59.999999", - format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); + EXPECT_EQ( + "1969-12-31 23:59:59.999999", + absl::time_internal::cctz::format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); // Here is a "%E*S" case we got wrong for a while. While the first // instant below is correctly rendered as "...:07.333304", the second // one used to appear as "...:07.33330499999999999". tp = chrono::system_clock::from_time_t(0) + chrono::microseconds(1395024427333304); - EXPECT_EQ("2014-03-17 02:47:07.333304", - format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); + EXPECT_EQ( + "2014-03-17 02:47:07.333304", + absl::time_internal::cctz::format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); tp += chrono::microseconds(1); - EXPECT_EQ("2014-03-17 02:47:07.333305", - format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); + EXPECT_EQ( + "2014-03-17 02:47:07.333305", + absl::time_internal::cctz::format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz)); } TEST(Format, CompareExtendSecondsVsSubseconds) { @@ -408,15 +440,17 @@ TEST(Format, CompareExtendSecondsVsSubseconds) { time_point<chrono::nanoseconds> tp = chrono::system_clock::from_time_t(0); tp += chrono::seconds(5); // ... %E*S and %S.%E*f are different. - EXPECT_EQ("05", format(fmt_A("*"), tp, tz)); - EXPECT_EQ("05.0", format(fmt_B("*"), tp, tz)); + EXPECT_EQ("05", absl::time_internal::cctz::format(fmt_A("*"), tp, tz)); + EXPECT_EQ("05.0", absl::time_internal::cctz::format(fmt_B("*"), tp, tz)); // ... %E0S and %S.%E0f are different. - EXPECT_EQ("05", format(fmt_A("0"), tp, tz)); - EXPECT_EQ("05.", format(fmt_B("0"), tp, tz)); + EXPECT_EQ("05", absl::time_internal::cctz::format(fmt_A("0"), tp, tz)); + EXPECT_EQ("05.", absl::time_internal::cctz::format(fmt_B("0"), tp, tz)); // ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15]. for (int prec = 1; prec <= 15; ++prec) { - const std::string a = format(fmt_A(std::to_string(prec)), tp, tz); - const std::string b = format(fmt_B(std::to_string(prec)), tp, tz); + const std::string a = + absl::time_internal::cctz::format(fmt_A(std::to_string(prec)), tp, tz); + const std::string b = + absl::time_internal::cctz::format(fmt_B(std::to_string(prec)), tp, tz); EXPECT_EQ(a, b) << "prec=" << prec; } @@ -424,15 +458,19 @@ TEST(Format, CompareExtendSecondsVsSubseconds) { // ... %E*S and %S.%E*f are the same. tp += chrono::milliseconds(6) + chrono::microseconds(7) + chrono::nanoseconds(8); - EXPECT_EQ("05.006007008", format(fmt_A("*"), tp, tz)); - EXPECT_EQ("05.006007008", format(fmt_B("*"), tp, tz)); + EXPECT_EQ("05.006007008", + absl::time_internal::cctz::format(fmt_A("*"), tp, tz)); + EXPECT_EQ("05.006007008", + absl::time_internal::cctz::format(fmt_B("*"), tp, tz)); // ... %E0S and %S.%E0f are different. - EXPECT_EQ("05", format(fmt_A("0"), tp, tz)); - EXPECT_EQ("05.", format(fmt_B("0"), tp, tz)); + EXPECT_EQ("05", absl::time_internal::cctz::format(fmt_A("0"), tp, tz)); + EXPECT_EQ("05.", absl::time_internal::cctz::format(fmt_B("0"), tp, tz)); // ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15]. for (int prec = 1; prec <= 15; ++prec) { - const std::string a = format(fmt_A(std::to_string(prec)), tp, tz); - const std::string b = format(fmt_B(std::to_string(prec)), tp, tz); + const std::string a = + absl::time_internal::cctz::format(fmt_A(std::to_string(prec)), tp, tz); + const std::string b = + absl::time_internal::cctz::format(fmt_B(std::to_string(prec)), tp, tz); EXPECT_EQ(a, b) << "prec=" << prec; } } @@ -605,31 +643,31 @@ TEST(Format, ExtendedYears) { // %E4Y zero-pads the year to produce at least 4 chars, including the sign. auto tp = convert(civil_second(-999, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-9991127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("-9991127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(-99, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-0991127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("-0991127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(-9, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-0091127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("-0091127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(-1, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-0011127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("-0011127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(0, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("00001127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("00001127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(1, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("00011127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("00011127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(9, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("00091127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("00091127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(99, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("00991127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("00991127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(999, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("09991127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("09991127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(9999, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("99991127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("99991127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); // When the year is outside [-999:9999], more than 4 chars are produced. tp = convert(civil_second(-1000, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("-10001127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("-10001127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); tp = convert(civil_second(10000, 11, 27, 0, 0, 0), utc); - EXPECT_EQ("100001127", format(e4y_fmt, tp, utc)); + EXPECT_EQ("100001127", absl::time_internal::cctz::format(e4y_fmt, tp, utc)); } TEST(Format, RFC3339Format) { @@ -638,45 +676,64 @@ TEST(Format, RFC3339Format) { time_point<chrono::nanoseconds> tp = convert(civil_second(1977, 6, 28, 9, 8, 7), tz); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); tp += chrono::milliseconds(100); - EXPECT_EQ("1977-06-28T09:08:07.1-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07.1-07:00", + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); tp += chrono::milliseconds(20); - EXPECT_EQ("1977-06-28T09:08:07.12-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07.12-07:00", + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); tp += chrono::milliseconds(3); - EXPECT_EQ("1977-06-28T09:08:07.123-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07.123-07:00", + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); tp += chrono::microseconds(400); - EXPECT_EQ("1977-06-28T09:08:07.1234-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07.1234-07:00", + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); tp += chrono::microseconds(50); - EXPECT_EQ("1977-06-28T09:08:07.12345-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07.12345-07:00", + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); tp += chrono::microseconds(6); - EXPECT_EQ("1977-06-28T09:08:07.123456-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07.123456-07:00", + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); tp += chrono::nanoseconds(700); - EXPECT_EQ("1977-06-28T09:08:07.1234567-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07.1234567-07:00", + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); tp += chrono::nanoseconds(80); - EXPECT_EQ("1977-06-28T09:08:07.12345678-07:00", format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07.12345678-07:00", + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); tp += chrono::nanoseconds(9); EXPECT_EQ("1977-06-28T09:08:07.123456789-07:00", - format(RFC3339_full, tp, tz)); - EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz)); + absl::time_internal::cctz::format(RFC3339_full, tp, tz)); + EXPECT_EQ("1977-06-28T09:08:07-07:00", + absl::time_internal::cctz::format(RFC3339_sec, tp, tz)); } TEST(Format, RFC1123Format) { // locale specific @@ -684,36 +741,50 @@ TEST(Format, RFC1123Format) { // locale specific EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); auto tp = convert(civil_second(1977, 6, 28, 9, 8, 7), tz); - EXPECT_EQ("Tue, 28 Jun 1977 09:08:07 -0700", format(RFC1123_full, tp, tz)); - EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz)); + EXPECT_EQ("Tue, 28 Jun 1977 09:08:07 -0700", + absl::time_internal::cctz::format(RFC1123_full, tp, tz)); + EXPECT_EQ("28 Jun 1977 09:08:07 -0700", + absl::time_internal::cctz::format(RFC1123_no_wday, tp, tz)); } TEST(Format, Week) { const time_zone utc = utc_time_zone(); auto tp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc); - EXPECT_EQ("2017-01-7", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2017-00-0", format("%Y-%W-%w", tp, utc)); + EXPECT_EQ("2017-01-7", + absl::time_internal::cctz::format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2017-00-0", + absl::time_internal::cctz::format("%Y-%W-%w", tp, utc)); tp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc); - EXPECT_EQ("2017-53-7", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2017-52-0", format("%Y-%W-%w", tp, utc)); + EXPECT_EQ("2017-53-7", + absl::time_internal::cctz::format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2017-52-0", + absl::time_internal::cctz::format("%Y-%W-%w", tp, utc)); tp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc); - EXPECT_EQ("2018-00-1", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2018-01-1", format("%Y-%W-%w", tp, utc)); + EXPECT_EQ("2018-00-1", + absl::time_internal::cctz::format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2018-01-1", + absl::time_internal::cctz::format("%Y-%W-%w", tp, utc)); tp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc); - EXPECT_EQ("2018-52-1", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2018-53-1", format("%Y-%W-%w", tp, utc)); + EXPECT_EQ("2018-52-1", + absl::time_internal::cctz::format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2018-53-1", + absl::time_internal::cctz::format("%Y-%W-%w", tp, utc)); tp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc); - EXPECT_EQ("2019-00-2", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2019-00-2", format("%Y-%W-%w", tp, utc)); + EXPECT_EQ("2019-00-2", + absl::time_internal::cctz::format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2019-00-2", + absl::time_internal::cctz::format("%Y-%W-%w", tp, utc)); tp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); - EXPECT_EQ("2019-52-2", format("%Y-%U-%u", tp, utc)); - EXPECT_EQ("2019-52-2", format("%Y-%W-%w", tp, utc)); + EXPECT_EQ("2019-52-2", + absl::time_internal::cctz::format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2019-52-2", + absl::time_internal::cctz::format("%Y-%W-%w", tp, utc)); } // @@ -726,39 +797,46 @@ TEST(Parse, TimePointResolution) { time_point<chrono::nanoseconds> tp_ns; EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_ns)); - EXPECT_EQ("03:04:05.123456789", format(kFmt, tp_ns, utc)); + EXPECT_EQ("03:04:05.123456789", + absl::time_internal::cctz::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)); + EXPECT_EQ("03:04:05.123456", + absl::time_internal::cctz::format(kFmt, tp_ns, utc)); time_point<chrono::microseconds> tp_us; EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_us)); - EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc)); + EXPECT_EQ("03:04:05.123456", + absl::time_internal::cctz::format(kFmt, tp_us, utc)); EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_us)); - EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc)); + EXPECT_EQ("03:04:05.123456", + absl::time_internal::cctz::format(kFmt, tp_us, utc)); EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_us)); - EXPECT_EQ("03:04:05.123", format(kFmt, tp_us, utc)); + EXPECT_EQ("03:04:05.123", + absl::time_internal::cctz::format(kFmt, tp_us, utc)); time_point<chrono::milliseconds> tp_ms; EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ms)); - EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc)); + EXPECT_EQ("03:04:05.123", + absl::time_internal::cctz::format(kFmt, tp_ms, utc)); EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_ms)); - EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc)); + EXPECT_EQ("03:04:05.123", + absl::time_internal::cctz::format(kFmt, tp_ms, utc)); EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_ms)); - EXPECT_EQ("03:04:05", format(kFmt, tp_ms, utc)); + EXPECT_EQ("03:04:05", absl::time_internal::cctz::format(kFmt, tp_ms, utc)); time_point<chrono::seconds> tp_s; EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_s)); - EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc)); + EXPECT_EQ("03:04:05", absl::time_internal::cctz::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)); + EXPECT_EQ("03:04:05", absl::time_internal::cctz::format(kFmt, tp_s, utc)); time_point<chrono::minutes> tp_m; EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_m)); - EXPECT_EQ("03:04:00", format(kFmt, tp_m, utc)); + EXPECT_EQ("03:04:00", absl::time_internal::cctz::format(kFmt, tp_m, utc)); time_point<chrono::hours> tp_h; EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_h)); - EXPECT_EQ("03:00:00", format(kFmt, tp_h, utc)); + EXPECT_EQ("03:00:00", absl::time_internal::cctz::format(kFmt, tp_h, utc)); } TEST(Parse, TimePointExtendedResolution) { @@ -1550,7 +1628,7 @@ TEST(Parse, TimePointOverflow) { parse(RFC3339_full, "2262-04-11T23:47:16.8547758079+00:00", utc, &tp)); EXPECT_EQ(tp, time_point<D>::max()); EXPECT_EQ("2262-04-11T23:47:16.854775807+00:00", - format(RFC3339_full, tp, utc)); + absl::time_internal::cctz::format(RFC3339_full, tp, utc)); #if 0 // TODO(#199): Will fail until cctz::parse() properly detects overflow. EXPECT_FALSE( @@ -1559,7 +1637,7 @@ TEST(Parse, TimePointOverflow) { parse(RFC3339_full, "1677-09-21T00:12:43.1452241920+00:00", utc, &tp)); EXPECT_EQ(tp, time_point<D>::min()); EXPECT_EQ("1677-09-21T00:12:43.145224192+00:00", - format(RFC3339_full, tp, utc)); + absl::time_internal::cctz::format(RFC3339_full, tp, utc)); EXPECT_FALSE( parse(RFC3339_full, "1677-09-21T00:12:43.1452241919+00:00", utc, &tp)); #endif @@ -1569,12 +1647,14 @@ TEST(Parse, TimePointOverflow) { EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T00:02:07.9+00:00", utc, &stp)); EXPECT_EQ(stp, time_point<DS>::max()); - EXPECT_EQ("1970-01-01T00:02:07+00:00", format(RFC3339_full, stp, utc)); + EXPECT_EQ("1970-01-01T00:02:07+00:00", + absl::time_internal::cctz::format(RFC3339_full, stp, utc)); EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T00:02:08+00:00", utc, &stp)); EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T23:57:52+00:00", utc, &stp)); EXPECT_EQ(stp, time_point<DS>::min()); - EXPECT_EQ("1969-12-31T23:57:52+00:00", format(RFC3339_full, stp, utc)); + EXPECT_EQ("1969-12-31T23:57:52+00:00", + absl::time_internal::cctz::format(RFC3339_full, stp, utc)); EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T23:57:51.9+00:00", utc, &stp)); using DM = chrono::duration<std::int8_t, chrono::minutes::period>; @@ -1582,12 +1662,14 @@ TEST(Parse, TimePointOverflow) { EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T02:07:59+00:00", utc, &mtp)); EXPECT_EQ(mtp, time_point<DM>::max()); - EXPECT_EQ("1970-01-01T02:07:00+00:00", format(RFC3339_full, mtp, utc)); + EXPECT_EQ("1970-01-01T02:07:00+00:00", + absl::time_internal::cctz::format(RFC3339_full, mtp, utc)); EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T02:08:00+00:00", utc, &mtp)); EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T21:52:00+00:00", utc, &mtp)); EXPECT_EQ(mtp, time_point<DM>::min()); - EXPECT_EQ("1969-12-31T21:52:00+00:00", format(RFC3339_full, mtp, utc)); + EXPECT_EQ("1969-12-31T21:52:00+00:00", + absl::time_internal::cctz::format(RFC3339_full, mtp, utc)); EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T21:51:59+00:00", utc, &mtp)); } @@ -1601,7 +1683,7 @@ TEST(Parse, TimePointOverflowFloor) { parse(RFC3339_full, "294247-01-10T04:00:54.7758079+00:00", utc, &tp)); EXPECT_EQ(tp, time_point<D>::max()); EXPECT_EQ("294247-01-10T04:00:54.775807+00:00", - format(RFC3339_full, tp, utc)); + absl::time_internal::cctz::format(RFC3339_full, tp, utc)); #if 0 // TODO(#199): Will fail until cctz::parse() properly detects overflow. EXPECT_FALSE( @@ -1610,7 +1692,7 @@ TEST(Parse, TimePointOverflowFloor) { parse(RFC3339_full, "-290308-12-21T19:59:05.2241920+00:00", utc, &tp)); EXPECT_EQ(tp, time_point<D>::min()); EXPECT_EQ("-290308-12-21T19:59:05.224192+00:00", - format(RFC3339_full, tp, utc)); + absl::time_internal::cctz::format(RFC3339_full, tp, utc)); EXPECT_FALSE( parse(RFC3339_full, "-290308-12-21T19:59:05.2241919+00:00", utc, &tp)); #endif @@ -1629,7 +1711,8 @@ TEST(FormatParse, RoundTrip) { // RFC3339, which renders subseconds. { time_point<chrono::nanoseconds> out; - const std::string s = format(RFC3339_full, in + subseconds, lax); + const std::string s = + absl::time_internal::cctz::format(RFC3339_full, in + subseconds, lax); EXPECT_TRUE(parse(RFC3339_full, s, lax, &out)) << s; EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez } @@ -1637,7 +1720,8 @@ TEST(FormatParse, RoundTrip) { // RFC1123, which only does whole seconds. { time_point<chrono::nanoseconds> out; - const std::string s = format(RFC1123_full, in, lax); + const std::string s = + absl::time_internal::cctz::format(RFC1123_full, in, lax); EXPECT_TRUE(parse(RFC1123_full, s, lax, &out)) << s; EXPECT_EQ(in, out); // RFC1123_full includes %z } @@ -1655,7 +1739,7 @@ TEST(FormatParse, RoundTrip) { { time_point<chrono::nanoseconds> out; time_zone utc = utc_time_zone(); - const std::string s = format("%c", in, utc); + const std::string s = absl::time_internal::cctz::format("%c", in, utc); EXPECT_TRUE(parse("%c", s, utc, &out)) << s; EXPECT_EQ(in, out); } @@ -1666,7 +1750,8 @@ TEST(FormatParse, RoundTripDistantFuture) { const time_zone utc = utc_time_zone(); const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::max(); - const std::string s = format(RFC3339_full, in, utc); + const std::string s = + absl::time_internal::cctz::format(RFC3339_full, in, utc); time_point<absl::time_internal::cctz::seconds> out; EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s; EXPECT_EQ(in, out); @@ -1676,7 +1761,8 @@ TEST(FormatParse, RoundTripDistantPast) { const time_zone utc = utc_time_zone(); const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::min(); - const std::string s = format(RFC3339_full, in, utc); + const std::string s = + absl::time_internal::cctz::format(RFC3339_full, in, utc); time_point<absl::time_internal::cctz::seconds> out; EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s; EXPECT_EQ(in, out); diff --git a/absl/time/internal/cctz/src/time_zone_if.cc b/absl/time/internal/cctz/src/time_zone_if.cc index 0319b2f9..0e65cd9e 100644 --- a/absl/time/internal/cctz/src/time_zone_if.cc +++ b/absl/time/internal/cctz/src/time_zone_if.cc @@ -23,17 +23,19 @@ ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { -std::unique_ptr<TimeZoneIf> TimeZoneIf::Load(const std::string& name) { +std::unique_ptr<TimeZoneIf> TimeZoneIf::UTC() { return TimeZoneInfo::UTC(); } + +std::unique_ptr<TimeZoneIf> TimeZoneIf::Make(const std::string& name) { // Support "libc:localtime" and "libc:*" to access the legacy // localtime and UTC support respectively from the C library. + // NOTE: The "libc:*" zones are internal, test-only interfaces, and + // are subject to change/removal without notice. Do not use them. if (name.compare(0, 5, "libc:") == 0) { - return std::unique_ptr<TimeZoneIf>(new TimeZoneLibC(name.substr(5))); + return TimeZoneLibC::Make(name.substr(5)); } - // Otherwise use the "zoneinfo" implementation by default. - std::unique_ptr<TimeZoneInfo> tz(new TimeZoneInfo); - if (!tz->Load(name)) tz.reset(); - return std::unique_ptr<TimeZoneIf>(tz.release()); + // Otherwise use the "zoneinfo" implementation. + return TimeZoneInfo::Make(name); } // Defined out-of-line to avoid emitting a weak vtable in all TUs. diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h index 7d3e42d3..bec9beb5 100644 --- a/absl/time/internal/cctz/src/time_zone_if.h +++ b/absl/time/internal/cctz/src/time_zone_if.h @@ -33,8 +33,9 @@ namespace cctz { // Subclasses implement the functions for civil-time conversions in the zone. class TimeZoneIf { public: - // A factory function for TimeZoneIf implementations. - static std::unique_ptr<TimeZoneIf> Load(const std::string& name); + // Factory functions for TimeZoneIf implementations. + static std::unique_ptr<TimeZoneIf> UTC(); // never fails + static std::unique_ptr<TimeZoneIf> Make(const std::string& name); virtual ~TimeZoneIf(); @@ -51,7 +52,9 @@ class TimeZoneIf { virtual std::string Description() const = 0; protected: - TimeZoneIf() {} + TimeZoneIf() = default; + TimeZoneIf(const TimeZoneIf&) = delete; + TimeZoneIf& operator=(const TimeZoneIf&) = delete; }; // Convert between time_point<seconds> and a count of seconds since the diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc index f34e3aec..aadbb77d 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.cc +++ b/absl/time/internal/cctz/src/time_zone_impl.cc @@ -99,11 +99,13 @@ void time_zone::Impl::ClearTimeZoneMapTestOnly() { } } +time_zone::Impl::Impl() : name_("UTC"), zone_(TimeZoneIf::UTC()) {} + time_zone::Impl::Impl(const std::string& name) - : name_(name), zone_(TimeZoneIf::Load(name_)) {} + : name_(name), zone_(TimeZoneIf::Make(name_)) {} const time_zone::Impl* time_zone::Impl::UTCImpl() { - static const Impl* utc_impl = new Impl("UTC"); // never fails + static const Impl* utc_impl = new Impl; return utc_impl; } diff --git a/absl/time/internal/cctz/src/time_zone_impl.h b/absl/time/internal/cctz/src/time_zone_impl.h index 7d747ba9..8308a3b4 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.h +++ b/absl/time/internal/cctz/src/time_zone_impl.h @@ -78,7 +78,11 @@ class time_zone::Impl { std::string Description() const { return zone_->Description(); } private: + Impl(); explicit Impl(const std::string& name); + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + static const Impl* UTCImpl(); const std::string name_; diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc index 787426f7..f46198ff 100644 --- a/absl/time/internal/cctz/src/time_zone_info.cc +++ b/absl/time/internal/cctz/src/time_zone_info.cc @@ -45,6 +45,7 @@ #include <sstream> #include <string> #include <utility> +#include <vector> #include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" @@ -134,6 +135,49 @@ std::int_fast64_t Decode64(const char* cp) { return static_cast<std::int_fast64_t>(v - s64maxU - 1) - s64max - 1; } +struct Header { // counts of: + std::size_t timecnt; // transition times + std::size_t typecnt; // transition types + std::size_t charcnt; // zone abbreviation characters + std::size_t leapcnt; // leap seconds (we expect none) + std::size_t ttisstdcnt; // UTC/local indicators (unused) + std::size_t ttisutcnt; // standard/wall indicators (unused) + + bool Build(const tzhead& tzh); + std::size_t DataLength(std::size_t time_len) const; +}; + +// Builds the in-memory header using the raw bytes from the file. +bool Header::Build(const tzhead& tzh) { + std::int_fast32_t v; + if ((v = Decode32(tzh.tzh_timecnt)) < 0) return false; + timecnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_typecnt)) < 0) return false; + typecnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_charcnt)) < 0) return false; + charcnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_leapcnt)) < 0) return false; + leapcnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false; + ttisstdcnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_ttisutcnt)) < 0) return false; + ttisutcnt = static_cast<std::size_t>(v); + return true; +} + +// How many bytes of data are associated with this header. The result +// depends upon whether this is a section with 4-byte or 8-byte times. +std::size_t Header::DataLength(std::size_t time_len) const { + std::size_t len = 0; + len += (time_len + 1) * timecnt; // unix_time + type_index + len += (4 + 1 + 1) * typecnt; // utc_offset + is_dst + abbr_index + len += 1 * charcnt; // abbreviations + len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC + len += 1 * ttisstdcnt; // UTC/local indicators + len += 1 * ttisutcnt; // standard/wall indicators + return len; +} + // Does the rule for future transitions call for year-round daylight time? // See tz/zic.c:stringzone() for the details on how such rules are encoded. bool AllYearDST(const PosixTimeZone& posix) { @@ -217,98 +261,6 @@ 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 seconds& offset) { - transition_types_.resize(1); - TransitionType& tt(transition_types_.back()); - tt.utc_offset = static_cast<std::int_least32_t>(offset.count()); - tt.is_dst = false; - tt.abbr_index = 0; - - // We temporarily add some redundant, contemporary (2015 through 2025) - // 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), // a "first half" transition - 1420070400LL, // 2015-01-01T00:00:00+00:00 - 1451606400LL, // 2016-01-01T00:00:00+00:00 - 1483228800LL, // 2017-01-01T00:00:00+00:00 - 1514764800LL, // 2018-01-01T00:00:00+00:00 - 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 - 1704067200LL, // 2024-01-01T00:00:00+00:00 - 1735689600LL, // 2025-01-01T00:00:00+00:00 - }) { - Transition& tr(*transitions_.emplace(transitions_.end())); - tr.unix_time = unix_time; - tr.type_index = 0; - tr.civil_sec = LocalTime(tr.unix_time, tt).cs; - tr.prev_civil_sec = tr.civil_sec - 1; - } - - default_transition_type_ = 0; - abbreviations_ = FixedOffsetToAbbr(offset); - abbreviations_.append(1, '\0'); - future_spec_.clear(); // never needed for a fixed-offset zone - extended_ = false; - - tt.civil_max = LocalTime(seconds::max().count(), tt).cs; - tt.civil_min = LocalTime(seconds::min().count(), tt).cs; - - transitions_.shrink_to_fit(); - return true; -} - -// Builds the in-memory header using the raw bytes from the file. -bool TimeZoneInfo::Header::Build(const tzhead& tzh) { - std::int_fast32_t v; - if ((v = Decode32(tzh.tzh_timecnt)) < 0) return false; - timecnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_typecnt)) < 0) return false; - typecnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_charcnt)) < 0) return false; - charcnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_leapcnt)) < 0) return false; - leapcnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false; - ttisstdcnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_ttisutcnt)) < 0) return false; - ttisutcnt = static_cast<std::size_t>(v); - return true; -} - -// How many bytes of data are associated with this header. The result -// depends upon whether this is a section with 4-byte or 8-byte times. -std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const { - std::size_t len = 0; - len += (time_len + 1) * timecnt; // unix_time + type_index - len += (4 + 1 + 1) * typecnt; // utc_offset + is_dst + abbr_index - len += 1 * charcnt; // abbreviations - len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC - len += 1 * ttisstdcnt; // UTC/local indicators - len += 1 * ttisutcnt; // standard/wall indicators - return len; -} - -// zic(8) can generate no-op transitions when a zone changes rules at an -// instant when there is actually no discontinuity. So we check whether -// two transitions have equivalent types (same offset/is_dst/abbr). -bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index, - std::uint_fast8_t tt2_index) const { - if (tt1_index == tt2_index) return true; - const TransitionType& tt1(transition_types_[tt1_index]); - const TransitionType& tt2(transition_types_[tt2_index]); - if (tt1.utc_offset != tt2.utc_offset) return false; - if (tt1.is_dst != tt2.is_dst) return false; - if (tt1.abbr_index != tt2.abbr_index) return false; - return true; -} - // Find/make a transition type with these attributes. bool TimeZoneInfo::GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, const std::string& abbr, @@ -341,6 +293,20 @@ bool TimeZoneInfo::GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, return true; } +// zic(8) can generate no-op transitions when a zone changes rules at an +// instant when there is actually no discontinuity. So we check whether +// two transitions have equivalent types (same offset/is_dst/abbr). +bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index, + std::uint_fast8_t tt2_index) const { + if (tt1_index == tt2_index) return true; + const TransitionType& tt1(transition_types_[tt1_index]); + const TransitionType& tt2(transition_types_[tt2_index]); + if (tt1.utc_offset != tt2.utc_offset) return false; + if (tt1.is_dst != tt2.is_dst) return false; + if (tt1.abbr_index != tt2.abbr_index) return false; + return true; +} + // Use the POSIX-TZ-environment-variable-style string to handle times // in years after the last transition stored in the zoneinfo data. bool TimeZoneInfo::ExtendTransitions() { @@ -372,11 +338,13 @@ bool TimeZoneInfo::ExtendTransitions() { return EquivTransitions(transitions_.back().type_index, dst_ti); } - // Extend the transitions for an additional 400 years using the - // future specification. Years beyond those can be handled by - // mapping back to a cycle-equivalent year within that range. - // We may need two additional transitions for the current year. - transitions_.reserve(transitions_.size() + 400 * 2 + 2); + // Extend the transitions for an additional 401 years using the future + // specification. Years beyond those can be handled by mapping back to + // a cycle-equivalent year within that range. Note that we need 401 + // (well, at least the first transition in the 401st year) so that the + // end of the 400th year is mapped back to an extended year. And first + // we may also need two additional transitions for the current year. + transitions_.reserve(transitions_.size() + 2 + 401 * 2); extended_ = true; const Transition& last(transitions_.back()); @@ -390,7 +358,7 @@ bool TimeZoneInfo::ExtendTransitions() { Transition dst = {0, dst_ti, civil_second(), civil_second()}; Transition std = {0, std_ti, civil_second(), civil_second()}; - for (const year_t limit = last_year_ + 400;; ++last_year_) { + for (const year_t limit = last_year_ + 401;; ++last_year_) { auto dst_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_start); auto std_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_end); dst.unix_time = jan1_time + dst_trans_off - posix.std_offset; @@ -410,193 +378,6 @@ bool TimeZoneInfo::ExtendTransitions() { return true; } -bool TimeZoneInfo::Load(ZoneInfoSource* zip) { - // Read and validate the header. - tzhead tzh; - if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false; - if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) - return false; - Header hdr; - if (!hdr.Build(tzh)) return false; - std::size_t time_len = 4; - if (tzh.tzh_version[0] != '\0') { - // Skip the 4-byte data. - if (zip->Skip(hdr.DataLength(time_len)) != 0) return false; - // Read and validate the header for the 8-byte data. - if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false; - if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) - return false; - if (tzh.tzh_version[0] == '\0') return false; - if (!hdr.Build(tzh)) return false; - time_len = 8; - } - if (hdr.typecnt == 0) return false; - if (hdr.leapcnt != 0) { - // This code assumes 60-second minutes so we do not want - // the leap-second encoded zoneinfo. We could reverse the - // compensation, but the "right" encoding is rarely used - // so currently we simply reject such data. - return false; - } - if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false; - if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false; - - // Read the data into a local buffer. - std::size_t len = hdr.DataLength(time_len); - std::vector<char> tbuf(len); - if (zip->Read(tbuf.data(), len) != len) return false; - const char* bp = tbuf.data(); - - // Decode and validate the transitions. - transitions_.reserve(hdr.timecnt + 2); - transitions_.resize(hdr.timecnt); - for (std::size_t i = 0; i != hdr.timecnt; ++i) { - transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp); - bp += time_len; - if (i != 0) { - // Check that the transitions are ordered by time (as zic guarantees). - if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i])) - return false; // out of order - } - } - bool seen_type_0 = false; - for (std::size_t i = 0; i != hdr.timecnt; ++i) { - transitions_[i].type_index = Decode8(bp++); - if (transitions_[i].type_index >= hdr.typecnt) return false; - if (transitions_[i].type_index == 0) seen_type_0 = true; - } - - // Decode and validate the transition types. - transition_types_.reserve(hdr.typecnt + 2); - transition_types_.resize(hdr.typecnt); - for (std::size_t i = 0; i != hdr.typecnt; ++i) { - transition_types_[i].utc_offset = - static_cast<std::int_least32_t>(Decode32(bp)); - if (transition_types_[i].utc_offset >= kSecsPerDay || - transition_types_[i].utc_offset <= -kSecsPerDay) - return false; - bp += 4; - transition_types_[i].is_dst = (Decode8(bp++) != 0); - transition_types_[i].abbr_index = Decode8(bp++); - if (transition_types_[i].abbr_index >= hdr.charcnt) return false; - } - - // Determine the before-first-transition type. - default_transition_type_ = 0; - if (seen_type_0 && hdr.timecnt != 0) { - std::uint_fast8_t index = 0; - if (transition_types_[0].is_dst) { - index = transitions_[0].type_index; - while (index != 0 && transition_types_[index].is_dst) --index; - } - while (index != hdr.typecnt && transition_types_[index].is_dst) ++index; - if (index != hdr.typecnt) default_transition_type_ = index; - } - - // Copy all the abbreviations. - abbreviations_.reserve(hdr.charcnt + 10); - abbreviations_.assign(bp, hdr.charcnt); - bp += hdr.charcnt; - - // Skip the unused portions. We've already dispensed with leap-second - // encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when - // interpreting a POSIX spec that does not include start/end rules, and - // that isn't the case here (see "zic -p"). - bp += (time_len + 4) * hdr.leapcnt; // leap-time + TAI-UTC - bp += 1 * hdr.ttisstdcnt; // UTC/local indicators - bp += 1 * hdr.ttisutcnt; // standard/wall indicators - assert(bp == tbuf.data() + tbuf.size()); - - future_spec_.clear(); - if (tzh.tzh_version[0] != '\0') { - // Snarf up the NL-enclosed future POSIX spec. Note - // that version '3' files utilize an extended format. - auto get_char = [](ZoneInfoSource* azip) -> int { - unsigned char ch; // all non-EOF results are positive - return (azip->Read(&ch, 1) == 1) ? ch : EOF; - }; - if (get_char(zip) != '\n') return false; - for (int c = get_char(zip); c != '\n'; c = get_char(zip)) { - if (c == EOF) return false; - future_spec_.push_back(static_cast<char>(c)); - } - } - - // 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 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) or to avoid bugs in old readers. For us, they just - // get in the way when we do future_spec_ extension. - while (hdr.timecnt > 1) { - if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index, - transitions_[hdr.timecnt - 2].type_index)) { - break; - } - hdr.timecnt -= 1; - } - transitions_.resize(hdr.timecnt); - - // Ensure that there is always a transition in the first half of the - // time line (the second half is handled below) so that the signed - // difference between a civil_second and the civil_second of its - // previous transition is always representable, without overflow. - if (transitions_.empty() || transitions_.front().unix_time >= 0) { - Transition& tr(*transitions_.emplace(transitions_.begin())); - tr.unix_time = -(1LL << 59); // -18267312070-10-26T17:01:52+00:00 - tr.type_index = default_transition_type_; - } - - // Extend the transitions using the future specification. - if (!ExtendTransitions()) return false; - - // Ensure that there is always a transition in the second half of the - // time line (the first half is handled above) so that the signed - // difference between a civil_second and the civil_second of its - // previous transition is always representable, without overflow. - const Transition& last(transitions_.back()); - if (last.unix_time < 0) { - const std::uint_fast8_t type_index = last.type_index; - Transition& tr(*transitions_.emplace(transitions_.end())); - tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00 - tr.type_index = type_index; - } - - // Compute the local civil time for each transition and the preceding - // second. These will be used for reverse conversions in MakeTime(). - const TransitionType* ttp = &transition_types_[default_transition_type_]; - for (std::size_t i = 0; i != transitions_.size(); ++i) { - Transition& tr(transitions_[i]); - tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1; - ttp = &transition_types_[tr.type_index]; - tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs; - if (i != 0) { - // Check that the transitions are ordered by civil time. Essentially - // this means that an offset change cannot cross another such change. - // No one does this in practice, and we depend on it in MakeTime(). - if (!Transition::ByCivilTime()(transitions_[i - 1], tr)) - return false; // out of order - } - } - - // Compute the maximum/minimum civil times that can be converted to a - // time_point<seconds> for each of the zone's transition types. - for (auto& tt : transition_types_) { - tt.civil_max = LocalTime(seconds::max().count(), tt).cs; - tt.civil_min = LocalTime(seconds::min().count(), tt).cs; - } - - transitions_.shrink_to_fit(); - return true; -} - namespace { using FilePtr = std::unique_ptr<FILE, int (*)(FILE*)>; @@ -795,6 +576,240 @@ std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open( } // namespace +// What (no leap-seconds) UTC+seconds zoneinfo would look like. +bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { + transition_types_.resize(1); + TransitionType& tt(transition_types_.back()); + tt.utc_offset = static_cast<std::int_least32_t>(offset.count()); + tt.is_dst = false; + tt.abbr_index = 0; + + // We temporarily add some redundant, contemporary (2015 through 2025) + // 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), // a "first half" transition + 1420070400LL, // 2015-01-01T00:00:00+00:00 + 1451606400LL, // 2016-01-01T00:00:00+00:00 + 1483228800LL, // 2017-01-01T00:00:00+00:00 + 1514764800LL, // 2018-01-01T00:00:00+00:00 + 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 + 1704067200LL, // 2024-01-01T00:00:00+00:00 + 1735689600LL, // 2025-01-01T00:00:00+00:00 + }) { + Transition& tr(*transitions_.emplace(transitions_.end())); + tr.unix_time = unix_time; + tr.type_index = 0; + tr.civil_sec = LocalTime(tr.unix_time, tt).cs; + tr.prev_civil_sec = tr.civil_sec - 1; + } + + default_transition_type_ = 0; + abbreviations_ = FixedOffsetToAbbr(offset); + abbreviations_.append(1, '\0'); + future_spec_.clear(); // never needed for a fixed-offset zone + extended_ = false; + + tt.civil_max = LocalTime(seconds::max().count(), tt).cs; + tt.civil_min = LocalTime(seconds::min().count(), tt).cs; + + transitions_.shrink_to_fit(); + return true; +} + +bool TimeZoneInfo::Load(ZoneInfoSource* zip) { + // Read and validate the header. + tzhead tzh; + if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false; + if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) + return false; + Header hdr; + if (!hdr.Build(tzh)) return false; + std::size_t time_len = 4; + if (tzh.tzh_version[0] != '\0') { + // Skip the 4-byte data. + if (zip->Skip(hdr.DataLength(time_len)) != 0) return false; + // Read and validate the header for the 8-byte data. + if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false; + if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) + return false; + if (tzh.tzh_version[0] == '\0') return false; + if (!hdr.Build(tzh)) return false; + time_len = 8; + } + if (hdr.typecnt == 0) return false; + if (hdr.leapcnt != 0) { + // This code assumes 60-second minutes so we do not want + // the leap-second encoded zoneinfo. We could reverse the + // compensation, but the "right" encoding is rarely used + // so currently we simply reject such data. + return false; + } + if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false; + if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false; + + // Read the data into a local buffer. + std::size_t len = hdr.DataLength(time_len); + std::vector<char> tbuf(len); + if (zip->Read(tbuf.data(), len) != len) return false; + const char* bp = tbuf.data(); + + // Decode and validate the transitions. + transitions_.reserve(hdr.timecnt + 2); + transitions_.resize(hdr.timecnt); + for (std::size_t i = 0; i != hdr.timecnt; ++i) { + transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp); + bp += time_len; + if (i != 0) { + // Check that the transitions are ordered by time (as zic guarantees). + if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i])) + return false; // out of order + } + } + bool seen_type_0 = false; + for (std::size_t i = 0; i != hdr.timecnt; ++i) { + transitions_[i].type_index = Decode8(bp++); + if (transitions_[i].type_index >= hdr.typecnt) return false; + if (transitions_[i].type_index == 0) seen_type_0 = true; + } + + // Decode and validate the transition types. + transition_types_.reserve(hdr.typecnt + 2); + transition_types_.resize(hdr.typecnt); + for (std::size_t i = 0; i != hdr.typecnt; ++i) { + transition_types_[i].utc_offset = + static_cast<std::int_least32_t>(Decode32(bp)); + if (transition_types_[i].utc_offset >= kSecsPerDay || + transition_types_[i].utc_offset <= -kSecsPerDay) + return false; + bp += 4; + transition_types_[i].is_dst = (Decode8(bp++) != 0); + transition_types_[i].abbr_index = Decode8(bp++); + if (transition_types_[i].abbr_index >= hdr.charcnt) return false; + } + + // Determine the before-first-transition type. + default_transition_type_ = 0; + if (seen_type_0 && hdr.timecnt != 0) { + std::uint_fast8_t index = 0; + if (transition_types_[0].is_dst) { + index = transitions_[0].type_index; + while (index != 0 && transition_types_[index].is_dst) --index; + } + while (index != hdr.typecnt && transition_types_[index].is_dst) ++index; + if (index != hdr.typecnt) default_transition_type_ = index; + } + + // Copy all the abbreviations. + abbreviations_.reserve(hdr.charcnt + 10); + abbreviations_.assign(bp, hdr.charcnt); + bp += hdr.charcnt; + + // Skip the unused portions. We've already dispensed with leap-second + // encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when + // interpreting a POSIX spec that does not include start/end rules, and + // that isn't the case here (see "zic -p"). + bp += (time_len + 4) * hdr.leapcnt; // leap-time + TAI-UTC + bp += 1 * hdr.ttisstdcnt; // UTC/local indicators + bp += 1 * hdr.ttisutcnt; // standard/wall indicators + assert(bp == tbuf.data() + tbuf.size()); + + future_spec_.clear(); + if (tzh.tzh_version[0] != '\0') { + // Snarf up the NL-enclosed future POSIX spec. Note + // that version '3' files utilize an extended format. + auto get_char = [](ZoneInfoSource* azip) -> int { + unsigned char ch; // all non-EOF results are positive + return (azip->Read(&ch, 1) == 1) ? ch : EOF; + }; + if (get_char(zip) != '\n') return false; + for (int c = get_char(zip); c != '\n'; c = get_char(zip)) { + if (c == EOF) return false; + future_spec_.push_back(static_cast<char>(c)); + } + } + + // 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 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) or to avoid bugs in old readers. For us, they just + // get in the way when we do future_spec_ extension. + while (hdr.timecnt > 1) { + if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index, + transitions_[hdr.timecnt - 2].type_index)) { + break; + } + hdr.timecnt -= 1; + } + transitions_.resize(hdr.timecnt); + + // Ensure that there is always a transition in the first half of the + // time line (the second half is handled below) so that the signed + // difference between a civil_second and the civil_second of its + // previous transition is always representable, without overflow. + if (transitions_.empty() || transitions_.front().unix_time >= 0) { + Transition& tr(*transitions_.emplace(transitions_.begin())); + tr.unix_time = -(1LL << 59); // -18267312070-10-26T17:01:52+00:00 + tr.type_index = default_transition_type_; + } + + // Extend the transitions using the future specification. + if (!ExtendTransitions()) return false; + + // Ensure that there is always a transition in the second half of the + // time line (the first half is handled above) so that the signed + // difference between a civil_second and the civil_second of its + // previous transition is always representable, without overflow. + const Transition& last(transitions_.back()); + if (last.unix_time < 0) { + const std::uint_fast8_t type_index = last.type_index; + Transition& tr(*transitions_.emplace(transitions_.end())); + tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00 + tr.type_index = type_index; + } + + // Compute the local civil time for each transition and the preceding + // second. These will be used for reverse conversions in MakeTime(). + const TransitionType* ttp = &transition_types_[default_transition_type_]; + for (std::size_t i = 0; i != transitions_.size(); ++i) { + Transition& tr(transitions_[i]); + tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1; + ttp = &transition_types_[tr.type_index]; + tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs; + if (i != 0) { + // Check that the transitions are ordered by civil time. Essentially + // this means that an offset change cannot cross another such change. + // No one does this in practice, and we depend on it in MakeTime(). + if (!Transition::ByCivilTime()(transitions_[i - 1], tr)) + return false; // out of order + } + } + + // Compute the maximum/minimum civil times that can be converted to a + // time_point<seconds> for each of the zone's transition types. + for (auto& tt : transition_types_) { + tt.civil_max = LocalTime(seconds::max().count(), tt).cs; + tt.civil_min = LocalTime(seconds::min().count(), tt).cs; + } + + transitions_.shrink_to_fit(); + return true; +} + bool TimeZoneInfo::Load(const std::string& name) { // We can ensure that the loading of UTC or any other fixed-offset // zone never fails because the simple, fixed-offset state can be @@ -816,6 +831,18 @@ bool TimeZoneInfo::Load(const std::string& name) { return zip != nullptr && Load(zip.get()); } +std::unique_ptr<TimeZoneInfo> TimeZoneInfo::UTC() { + auto tz = std::unique_ptr<TimeZoneInfo>(new TimeZoneInfo); + tz->ResetToBuiltinUTC(seconds::zero()); + return tz; +} + +std::unique_ptr<TimeZoneInfo> TimeZoneInfo::Make(const std::string& name) { + auto tz = std::unique_ptr<TimeZoneInfo>(new TimeZoneInfo); + if (!tz->Load(name)) tz.reset(); // fallback to UTC + return tz; +} + // BreakTime() translation for a particular transition type. time_zone::absolute_lookup TimeZoneInfo::LocalTime( std::int_fast64_t unix_time, const TransitionType& tt) const { diff --git a/absl/time/internal/cctz/src/time_zone_info.h b/absl/time/internal/cctz/src/time_zone_info.h index 2467ff55..689df6f9 100644 --- a/absl/time/internal/cctz/src/time_zone_info.h +++ b/absl/time/internal/cctz/src/time_zone_info.h @@ -18,6 +18,7 @@ #include <atomic> #include <cstddef> #include <cstdint> +#include <memory> #include <string> #include <vector> @@ -64,12 +65,9 @@ struct TransitionType { // A time zone backed by the IANA Time Zone Database (zoneinfo). class TimeZoneInfo : public TimeZoneIf { public: - TimeZoneInfo() = default; - TimeZoneInfo(const TimeZoneInfo&) = delete; - TimeZoneInfo& operator=(const TimeZoneInfo&) = delete; - - // Loads the zoneinfo for the given name, returning true if successful. - bool Load(const std::string& name); + // Factories. + static std::unique_ptr<TimeZoneInfo> UTC(); // never fails + static std::unique_ptr<TimeZoneInfo> Make(const std::string& name); // TimeZoneIf implementations. time_zone::absolute_lookup BreakTime( @@ -83,17 +81,9 @@ class TimeZoneInfo : public TimeZoneIf { std::string Description() const override; private: - struct Header { // counts of: - std::size_t timecnt; // transition times - std::size_t typecnt; // transition types - std::size_t charcnt; // zone abbreviation characters - std::size_t leapcnt; // leap seconds (we expect none) - std::size_t ttisstdcnt; // UTC/local indicators (unused) - std::size_t ttisutcnt; // standard/wall indicators (unused) - - bool Build(const tzhead& tzh); - std::size_t DataLength(std::size_t time_len) const; - }; + TimeZoneInfo() = default; + TimeZoneInfo(const TimeZoneInfo&) = delete; + TimeZoneInfo& operator=(const TimeZoneInfo&) = delete; bool GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, const std::string& abbr, std::uint_least8_t* index); @@ -102,6 +92,7 @@ class TimeZoneInfo : public TimeZoneIf { bool ExtendTransitions(); bool ResetToBuiltinUTC(const seconds& offset); + bool Load(const std::string& name); bool Load(ZoneInfoSource* zip); // Helpers for BreakTime() and MakeTime(). diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc index 887dd097..d0146122 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.cc +++ b/absl/time/internal/cctz/src/time_zone_libc.cc @@ -62,7 +62,7 @@ auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) { } #elif defined(__native_client__) || defined(__myriad2__) || \ defined(__EMSCRIPTEN__) -// Uses the globals: 'timezone' and 'tzname'. +// Uses the globals: '_timezone' and 'tzname'. auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) { const bool is_dst = tm.tm_isdst > 0; return _timezone + (is_dst ? 60 * 60 : 0); @@ -71,6 +71,16 @@ auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) { const bool is_dst = tm.tm_isdst > 0; return tzname[is_dst]; } +#elif defined(__VXWORKS__) +// Uses the globals: 'timezone' and 'tzname'. +auto tm_gmtoff(const std::tm& tm) -> decltype(timezone + 0) { + const bool is_dst = tm.tm_isdst > 0; + return timezone + (is_dst ? 60 * 60 : 0); +} +auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) { + const bool is_dst = tm.tm_isdst > 0; + return tzname[is_dst]; +} #else // Adapt to different spellings of the struct std::tm extension fields. #if defined(tm_gmtoff) @@ -108,6 +118,7 @@ auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) { } #endif // tm_zone #endif +using tm_gmtoff_t = decltype(tm_gmtoff(std::tm{})); inline std::tm* gm_time(const std::time_t* timep, std::tm* result) { #if defined(_WIN32) || defined(_WIN64) @@ -125,37 +136,36 @@ inline std::tm* local_time(const std::time_t* timep, std::tm* result) { #endif } -// Converts a civil second and "dst" flag into a time_t and UTC offset. +// Converts a civil second and "dst" flag into a time_t and a struct tm. // 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<int>(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); +bool make_time(const civil_second& cs, int is_dst, std::time_t* t, + std::tm* tm) { + tm->tm_year = static_cast<int>(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) { + 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 = static_cast<int>(tm_gmtoff(tm)); 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::time_t find_trans(std::time_t lo, std::time_t hi, tm_gmtoff_t offset) { std::tm tm; while (lo + 1 != hi) { const std::time_t mid = lo + (hi - lo) / 2; @@ -183,8 +193,9 @@ std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) { } // namespace -TimeZoneLibC::TimeZoneLibC(const std::string& name) - : local_(name == "localtime") {} +std::unique_ptr<TimeZoneLibC> TimeZoneLibC::Make(const std::string& name) { + return std::unique_ptr<TimeZoneLibC>(new TimeZoneLibC(name)); +} time_zone::absolute_lookup TimeZoneLibC::BreakTime( const time_point<seconds>& tp) const { @@ -254,33 +265,37 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { // 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. + // We are also subject to the vagaries of mktime() implementations. For + // example, some implementations treat "tm_isdst" as a demand (useless), + // and some as a disambiguator (useful). std::time_t t0, t1; - int offset0, offset1; - if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) { - if (t0 == t1) { + std::tm tm0, tm1; + if (make_time(cs, 0, &t0, &tm0) && make_time(cs, 1, &t1, &tm1)) { + if (tm0.tm_isdst == tm1.tm_isdst) { // The civil time was singular (pre == trans == post). - const time_point<seconds> tp = FromUnixSeconds(t0); + const time_point<seconds> tp = FromUnixSeconds(tm0.tm_isdst ? t1 : t0); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } - if (t0 > t1) { + tm_gmtoff_t offset = tm_gmtoff(tm0); + if (t0 < t1) { // negative DST std::swap(t0, t1); - std::swap(offset0, offset1); + offset = tm_gmtoff(tm1); } - const std::time_t tt = find_trans(t0, t1, offset1); + + const std::time_t tt = find_trans(t1, t0, offset); const time_point<seconds> trans = FromUnixSeconds(tt); - if (offset0 < offset1) { + if (tm0.tm_isdst) { // The civil time did not exist (pre >= trans > post). - const time_point<seconds> pre = FromUnixSeconds(t1); - const time_point<seconds> post = FromUnixSeconds(t0); + const time_point<seconds> pre = FromUnixSeconds(t0); + const time_point<seconds> post = FromUnixSeconds(t1); return {time_zone::civil_lookup::SKIPPED, pre, trans, post}; } // The civil time was ambiguous (pre < trans <= post). - const time_point<seconds> pre = FromUnixSeconds(t0); - const time_point<seconds> post = FromUnixSeconds(t1); + const time_point<seconds> pre = FromUnixSeconds(t1); + const time_point<seconds> post = FromUnixSeconds(t0); return {time_zone::civil_lookup::REPEATED, pre, trans, post}; } @@ -309,6 +324,9 @@ std::string TimeZoneLibC::Description() const { return local_ ? "localtime" : "UTC"; } +TimeZoneLibC::TimeZoneLibC(const std::string& name) + : local_(name == "localtime") {} + } // namespace cctz } // namespace time_internal ABSL_NAMESPACE_END diff --git a/absl/time/internal/cctz/src/time_zone_libc.h b/absl/time/internal/cctz/src/time_zone_libc.h index 1da9039a..ae210737 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.h +++ b/absl/time/internal/cctz/src/time_zone_libc.h @@ -15,6 +15,7 @@ #ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_ #define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_ +#include <memory> #include <string> #include "absl/base/config.h" @@ -27,10 +28,10 @@ namespace cctz { // A time zone backed by gmtime_r(3), localtime_r(3), and mktime(3), // and which therefore only supports UTC and the local time zone. -// TODO: Add support for fixed offsets from UTC. class TimeZoneLibC : public TimeZoneIf { public: - explicit TimeZoneLibC(const std::string& name); + // Factory. + static std::unique_ptr<TimeZoneLibC> Make(const std::string& name); // TimeZoneIf implementations. time_zone::absolute_lookup BreakTime( @@ -44,6 +45,10 @@ class TimeZoneLibC : public TimeZoneIf { std::string Description() const override; private: + explicit TimeZoneLibC(const std::string& name); + TimeZoneLibC(const TimeZoneLibC&) = delete; + TimeZoneLibC& operator=(const TimeZoneLibC&) = delete; + const bool local_; // localtime or UTC }; diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc index f6983aeb..d22691bd 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup.cc @@ -35,6 +35,24 @@ #include <zircon/types.h> #endif +#if defined(_WIN32) +#include <sdkddkver.h> +// Include only when the SDK is for Windows 10 (and later), and the binary is +// targeted for Windows XP and later. +// Note: The Windows SDK added windows.globalization.h file for Windows 10, but +// MinGW did not add it until NTDDI_WIN10_NI (SDK version 10.0.22621.0). +#if ((defined(_WIN32_WINNT_WIN10) && !defined(__MINGW32__)) || \ + (defined(NTDDI_WIN10_NI) && NTDDI_VERSION >= NTDDI_WIN10_NI)) && \ + (_WIN32_WINNT >= _WIN32_WINNT_WINXP) +#define USE_WIN32_LOCAL_TIME_ZONE +#include <roapi.h> +#include <tchar.h> +#include <wchar.h> +#include <windows.globalization.h> +#include <windows.h> +#endif +#endif + #include <cstdlib> #include <cstring> #include <string> @@ -47,8 +65,8 @@ ABSL_NAMESPACE_BEGIN namespace time_internal { namespace cctz { -#if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21 namespace { +#if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21 // Android 'L' removes __system_property_get() from the NDK, however // it is still a hidden symbol in libc so we use dlsym() to access it. // See Chromium's base/sys_info_android.cc for a similar example. @@ -72,9 +90,84 @@ int __system_property_get(const char* name, char* value) { static property_get_func system_property_get = LoadSystemPropertyGet(); return system_property_get ? system_property_get(name, value) : -1; } +#endif -} // namespace +#if defined(USE_WIN32_LOCAL_TIME_ZONE) +// Calls the WinRT Calendar.GetTimeZone method to obtain the IANA ID of the +// local time zone. Returns an empty vector in case of an error. +std::string win32_local_time_zone(const HMODULE combase) { + std::string result; + const auto ro_activate_instance = + reinterpret_cast<decltype(&RoActivateInstance)>( + GetProcAddress(combase, "RoActivateInstance")); + if (!ro_activate_instance) { + return result; + } + const auto windows_create_string_reference = + reinterpret_cast<decltype(&WindowsCreateStringReference)>( + GetProcAddress(combase, "WindowsCreateStringReference")); + if (!windows_create_string_reference) { + return result; + } + const auto windows_delete_string = + reinterpret_cast<decltype(&WindowsDeleteString)>( + GetProcAddress(combase, "WindowsDeleteString")); + if (!windows_delete_string) { + return result; + } + const auto windows_get_string_raw_buffer = + reinterpret_cast<decltype(&WindowsGetStringRawBuffer)>( + GetProcAddress(combase, "WindowsGetStringRawBuffer")); + if (!windows_get_string_raw_buffer) { + return result; + } + + // The string returned by WindowsCreateStringReference doesn't need to be + // deleted. + HSTRING calendar_class_id; + HSTRING_HEADER calendar_class_id_header; + HRESULT hr = windows_create_string_reference( + RuntimeClass_Windows_Globalization_Calendar, + sizeof(RuntimeClass_Windows_Globalization_Calendar) / sizeof(wchar_t) - 1, + &calendar_class_id_header, &calendar_class_id); + if (FAILED(hr)) { + return result; + } + + IInspectable* calendar; + hr = ro_activate_instance(calendar_class_id, &calendar); + if (FAILED(hr)) { + return result; + } + + ABI::Windows::Globalization::ITimeZoneOnCalendar* time_zone; + hr = calendar->QueryInterface(IID_PPV_ARGS(&time_zone)); + if (FAILED(hr)) { + calendar->Release(); + return result; + } + + HSTRING tz_hstr; + hr = time_zone->GetTimeZone(&tz_hstr); + if (SUCCEEDED(hr)) { + UINT32 wlen; + const PCWSTR tz_wstr = windows_get_string_raw_buffer(tz_hstr, &wlen); + if (tz_wstr) { + const int size = + WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen), + nullptr, 0, nullptr, nullptr); + result.resize(static_cast<size_t>(size)); + WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen), + &result[0], size, nullptr, nullptr); + } + windows_delete_string(tz_hstr); + } + time_zone->Release(); + calendar->Release(); + return result; +} #endif +} // namespace std::string time_zone::name() const { return effective_impl().Name(); } @@ -190,6 +283,39 @@ time_zone local_time_zone() { zone = primary_tz.c_str(); } #endif +#if defined(USE_WIN32_LOCAL_TIME_ZONE) + // Use the WinRT Calendar class to get the local time zone. This feature is + // available on Windows 10 and later. The library is dynamically linked to + // maintain binary compatibility with Windows XP - Windows 7. On Windows 8, + // The combase.dll API functions are available but the RoActivateInstance + // call will fail for the Calendar class. + std::string winrt_tz; + const HMODULE combase = + LoadLibraryEx(_T("combase.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (combase) { + const auto ro_initialize = reinterpret_cast<decltype(&::RoInitialize)>( + GetProcAddress(combase, "RoInitialize")); + const auto ro_uninitialize = reinterpret_cast<decltype(&::RoUninitialize)>( + GetProcAddress(combase, "RoUninitialize")); + if (ro_initialize && ro_uninitialize) { + const HRESULT hr = ro_initialize(RO_INIT_MULTITHREADED); + // RPC_E_CHANGED_MODE means that a previous RoInitialize call specified + // a different concurrency model. The WinRT runtime is initialized and + // should work for our purpose here, but we should *not* call + // RoUninitialize because it's a failure. + if (SUCCEEDED(hr) || hr == RPC_E_CHANGED_MODE) { + winrt_tz = win32_local_time_zone(combase); + if (SUCCEEDED(hr)) { + ro_uninitialize(); + } + } + } + FreeLibrary(combase); + } + if (!winrt_tz.empty()) { + zone = winrt_tz.c_str(); + } +#endif // Allow ${TZ} to override to default zone. char* tz_env = nullptr; 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 ab461f04..4884c32e 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -135,6 +135,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan", "America/Cayman", "America/Chicago", "America/Chihuahua", + "America/Ciudad_Juarez", "America/Coral_Harbour", "America/Cordoba", "America/Costa_Rica", @@ -734,6 +735,10 @@ TEST(TimeZone, UTC) { time_zone loaded_utc0; EXPECT_TRUE(load_time_zone("UTC0", &loaded_utc0)); EXPECT_EQ(loaded_utc0, utc); + + time_zone loaded_bad; + EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &loaded_bad)); + EXPECT_EQ(loaded_bad, utc); } TEST(TimeZone, NamedTimeZones) { @@ -911,19 +916,19 @@ TEST(MakeTime, TimePointResolution) { const time_zone utc = utc_time_zone(); const time_point<chrono::nanoseconds> tp_ns = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); - EXPECT_EQ("04:05", format("%M:%E*S", tp_ns, utc)); + EXPECT_EQ("04:05", absl::time_internal::cctz::format("%M:%E*S", tp_ns, utc)); const time_point<chrono::microseconds> tp_us = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); - EXPECT_EQ("04:05", format("%M:%E*S", tp_us, utc)); + EXPECT_EQ("04:05", absl::time_internal::cctz::format("%M:%E*S", tp_us, utc)); const time_point<chrono::milliseconds> tp_ms = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); - EXPECT_EQ("04:05", format("%M:%E*S", tp_ms, utc)); + EXPECT_EQ("04:05", absl::time_internal::cctz::format("%M:%E*S", tp_ms, utc)); const time_point<chrono::seconds> tp_s = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); - EXPECT_EQ("04:05", format("%M:%E*S", tp_s, utc)); + EXPECT_EQ("04:05", absl::time_internal::cctz::format("%M:%E*S", tp_s, utc)); const time_point<absl::time_internal::cctz::seconds> tp_s64 = convert(civil_second(2015, 1, 2, 3, 4, 5), utc); - EXPECT_EQ("04:05", format("%M:%E*S", tp_s64, utc)); + EXPECT_EQ("04:05", absl::time_internal::cctz::format("%M:%E*S", tp_s64, utc)); // These next two require chrono::time_point_cast because the conversion // from a resolution of seconds (the return value of convert()) to a @@ -931,10 +936,10 @@ TEST(MakeTime, TimePointResolution) { const time_point<chrono::minutes> tp_m = chrono::time_point_cast<chrono::minutes>( convert(civil_second(2015, 1, 2, 3, 4, 5), utc)); - EXPECT_EQ("04:00", format("%M:%E*S", tp_m, utc)); + EXPECT_EQ("04:00", absl::time_internal::cctz::format("%M:%E*S", tp_m, utc)); const time_point<chrono::hours> tp_h = chrono::time_point_cast<chrono::hours>( convert(civil_second(2015, 1, 2, 3, 4, 5), utc)); - EXPECT_EQ("00:00", format("%M:%E*S", tp_h, utc)); + EXPECT_EQ("00:00", absl::time_internal::cctz::format("%M:%E*S", tp_h, utc)); } TEST(MakeTime, Normalization) { @@ -960,9 +965,11 @@ TEST(MakeTime, SysSecondsLimits) { // Approach the maximal time_point<cctz::seconds> value from below. tp = convert(civil_second(292277026596, 12, 4, 15, 30, 6), utc); - EXPECT_EQ("292277026596-12-04T15:30:06+00:00", format(RFC3339, tp, utc)); + EXPECT_EQ("292277026596-12-04T15:30:06+00:00", + absl::time_internal::cctz::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("292277026596-12-04T15:30:07+00:00", + absl::time_internal::cctz::format(RFC3339, tp, utc)); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp); tp = convert(civil_second(292277026596, 12, 4, 15, 30, 8), utc); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp); @@ -971,7 +978,8 @@ TEST(MakeTime, SysSecondsLimits) { // 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("292277026596-12-05T05:30:07+14:00", + absl::time_internal::cctz::format(RFC3339, tp, east)); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp); tp = convert(civil_second(292277026596, 12, 5, 5, 30, 8), east); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp); @@ -980,7 +988,8 @@ TEST(MakeTime, SysSecondsLimits) { // 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("292277026596-12-04T01:30:07-14:00", + absl::time_internal::cctz::format(RFC3339, tp, west)); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp); tp = convert(civil_second(292277026596, 12, 4, 7, 30, 8), west); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::max(), tp); @@ -989,9 +998,11 @@ TEST(MakeTime, SysSecondsLimits) { // Approach the minimal time_point<cctz::seconds> value from above. tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 53), utc); - EXPECT_EQ("-292277022657-01-27T08:29:53+00:00", format(RFC3339, tp, utc)); + EXPECT_EQ("-292277022657-01-27T08:29:53+00:00", + absl::time_internal::cctz::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("-292277022657-01-27T08:29:52+00:00", + absl::time_internal::cctz::format(RFC3339, tp, utc)); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 51), utc); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); @@ -1000,7 +1011,8 @@ TEST(MakeTime, SysSecondsLimits) { // 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("-292277022657-01-27T22:29:52+14:00", + absl::time_internal::cctz::format(RFC3339, tp, east)); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 51), east); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); @@ -1009,7 +1021,8 @@ TEST(MakeTime, SysSecondsLimits) { // 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("-292277022657-01-26T18:29:52-14:00", + absl::time_internal::cctz::format(RFC3339, tp, west)); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 51), west); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); @@ -1026,17 +1039,19 @@ TEST(MakeTime, SysSecondsLimits) { const time_zone cut = LoadZone("libc:UTC"); const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900; tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut); -#if defined(__FreeBSD__) || defined(__OpenBSD__) - // The BSD gmtime_r() fails on extreme positive tm_year values. +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) + // Some gmtime_r() impls fail on extreme positive values. #else - EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, cut)); + EXPECT_EQ("2147485547-12-31T23:59:59+00:00", + absl::time_internal::cctz::format(RFC3339, tp, cut)); #endif const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900; tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut); -#if defined(__Fuchsia__) - // Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527). +#if defined(__Fuchsia__) || defined(__EMSCRIPTEN__) + // Some gmtime_r() impls fail on extreme negative values (fxbug.dev/78527). #else - EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut)); + EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", + absl::time_internal::cctz::format(RFC3339, tp, cut)); #endif #endif } @@ -1062,7 +1077,7 @@ TEST(MakeTime, LocalTimeLibC) { tp = zi.lookup(transition.to).trans) { const auto fcl = zi.lookup(transition.from); const auto tcl = zi.lookup(transition.to); - civil_second cs; // compare cs in zi and lc + civil_second cs, us; // compare cs and us 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. @@ -1078,12 +1093,14 @@ TEST(MakeTime, LocalTimeLibC) { } ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind); cs = transition.to; + us = transition.from; } else { ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind); ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind); cs = transition.from; + us = transition.to; } - if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t) + if (us.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 @@ -1115,6 +1132,13 @@ TEST(MakeTime, LocalTimeLibC) { EXPECT_EQ(cl_zi.pre, cl_lc.pre); EXPECT_EQ(cl_zi.trans, cl_lc.trans); EXPECT_EQ(cl_zi.post, cl_lc.post); + const auto ucl_zi = zi.lookup(us); + const auto ucl_lc = lc.lookup(us); + SCOPED_TRACE(testing::Message() << "For " << us << " in " << *np); + EXPECT_EQ(ucl_zi.kind, ucl_lc.kind); + EXPECT_EQ(ucl_zi.pre, ucl_lc.pre); + EXPECT_EQ(ucl_zi.trans, ucl_lc.trans); + EXPECT_EQ(ucl_zi.post, ucl_lc.post); } } if (ep == nullptr) { diff --git a/absl/time/internal/cctz/src/time_zone_posix.h b/absl/time/internal/cctz/src/time_zone_posix.h index 0cf29055..7fd2b9ec 100644 --- a/absl/time/internal/cctz/src/time_zone_posix.h +++ b/absl/time/internal/cctz/src/time_zone_posix.h @@ -104,7 +104,7 @@ struct PosixTransition { // 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 +// daylight saving, then the daylight abbreviation is non-empty and the // remaining fields are also valid. Note that the start/end transitions // are not ordered---in the southern hemisphere the transition to end // daylight time occurs first in any particular year. diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h index 31e85982..9613055d 100644 --- a/absl/time/internal/cctz/src/tzfile.h +++ b/absl/time/internal/cctz/src/tzfile.h @@ -102,20 +102,24 @@ struct tzhead { */ #ifndef TZ_MAX_TIMES +/* This must be at least 242 for Europe/London with 'zic -b fat'. */ #define TZ_MAX_TIMES 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES -/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */ +/* This must be at least 18 for Europe/Vilnius with 'zic -b fat'. */ #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #endif /* !defined TZ_MAX_TYPES */ #ifndef TZ_MAX_CHARS +/* This must be at least 40 for America/Anchorage. */ #define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ /* (limited by what unsigned chars can hold) */ #endif /* !defined TZ_MAX_CHARS */ #ifndef TZ_MAX_LEAPS +/* This must be at least 27 for leap seconds from 1972 through mid-2023. + There's a plan to discontinue leap seconds by 2035. */ #define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ #endif /* !defined TZ_MAX_LEAPS */ diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc index 1b162337..9bc8197b 100644 --- a/absl/time/internal/cctz/src/zone_info_source.cc +++ b/absl/time/internal/cctz/src/zone_info_source.cc @@ -67,41 +67,41 @@ extern ZoneInfoSourceFactory zone_info_source_factory; extern ZoneInfoSourceFactory default_factory; ZoneInfoSourceFactory default_factory = DefaultFactory; #if defined(_M_IX86) || defined(_M_ARM) -#pragma comment( \ - linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@@ZA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@@ZA") +#pragma comment( \ + linker, \ + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZA") #elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64) -#pragma comment( \ - linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@@ZEA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ - "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ - "@@ZEA") +#pragma comment( \ + linker, \ + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZEA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ + "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ + "@@ZEA") #else #error Unsupported MSVC platform #endif // _M_<PLATFORM> diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version index b74fa117..7daa77e0 100644 --- a/absl/time/internal/cctz/testdata/version +++ b/absl/time/internal/cctz/testdata/version @@ -1 +1 @@ -2022g +2023c diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo Binary files differindex ea38c970..1e6d48d1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca Binary files differindex 0263c90b..240ebb2b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun Binary files differindex 772e23c4..909c5f96 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab Binary files differindex 79d7a454..00b57bb1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk Binary files differindex 79d7a454..00b57bb1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife b/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife Binary files differindex ff3eb878..645ee945 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza Binary files differindex bed968e7..7e833898 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron Binary files differindex 3ce1bac6..fcf923bd 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Egypt b/absl/time/internal/cctz/testdata/zoneinfo/Egypt Binary files differindex ea38c970..1e6d48d1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Egypt +++ b/absl/time/internal/cctz/testdata/zoneinfo/Egypt diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov Binary files differindex d1c93c54..bfac5611 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd Binary files differindex c5170026..0715d58b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd diff --git a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab index 911af5e8..be3348d1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab +++ b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab @@ -238,7 +238,7 @@ SY Syria SZ Eswatini (Swaziland) TC Turks & Caicos Is TD Chad -TF French Southern Territories +TF French S. Terr. TG Togo TH Thailand TJ Tajikistan diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab index a9b36d36..1f1cecb8 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab +++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab @@ -18,7 +18,10 @@ # Please see the theory.html file for how these names are chosen. # If multiple timezones overlap a country, each has a row in the # table, with each column 1 containing the country code. -# 4. Comments; present if and only if a country has multiple timezones. +# 4. Comments; present if and only if countries have multiple timezones, +# and useful only for those countries. For example, the comments +# for the row with countries CH,DE,LI and name Europe/Zurich +# are useful only for DE, since CH and LI have no other timezones. # # If a timezone covers multiple countries, the most-populous city is used, # and that country is listed first in column 1; any other countries @@ -34,7 +37,7 @@ #country- #codes coordinates TZ comments AD +4230+00131 Europe/Andorra -AE,OM,RE,SC,TF +2518+05518 Asia/Dubai UAE, Oman, Réunion, Seychelles, Crozet, Scattered Is +AE,OM,RE,SC,TF +2518+05518 Asia/Dubai Crozet, Scattered Is AF +3431+06912 Asia/Kabul AL +4120+01950 Europe/Tirane AM +4011+04430 Asia/Yerevan @@ -45,7 +48,7 @@ AQ -6448-06406 Antarctica/Palmer Palmer AQ -6734-06808 Antarctica/Rothera Rothera AQ -720041+0023206 Antarctica/Troll Troll AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) -AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF) +AR -3124-06411 America/Argentina/Cordoba most areas: CB, CC, CN, ER, FM, MN, SE, SF AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) AR -2649-06513 America/Argentina/Tucuman Tucumán (TM) @@ -56,7 +59,7 @@ AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) AR -3319-06621 America/Argentina/San_Luis San Luis (SL) AR -5138-06913 America/Argentina/Rio_Gallegos Santa Cruz (SC) AR -5448-06818 America/Argentina/Ushuaia Tierra del Fuego (TF) -AS,UM -1416-17042 Pacific/Pago_Pago Samoa, Midway +AS,UM -1416-17042 Pacific/Pago_Pago Midway AT +4813+01620 Europe/Vienna AU -3133+15905 Australia/Lord_Howe Lord Howe Island AU -5430+15857 Antarctica/Macquarie Macquarie Island @@ -101,26 +104,25 @@ CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) -CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas), Bahamas +CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas) CA +6344-06828 America/Iqaluit Eastern - NU (most areas) CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) CA +5024-10439 America/Regina CST - SK (most areas) CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) +CA +5333-11328 America/Edmonton Mountain - AB; BC (E); NT (E); SK (W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) -CA +6227-11421 America/Yellowknife Mountain - NT (central) CA +682059-1334300 America/Inuvik Mountain - NT (west) CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) CA +6043-13503 America/Whitehorse MST - Yukon (east) CA +6404-13925 America/Dawson MST - Yukon (west) CA +4916-12307 America/Vancouver Pacific - BC (most areas) -CH,DE,LI +4723+00832 Europe/Zurich Swiss time +CH,DE,LI +4723+00832 Europe/Zurich Büsingen CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan CK -2114-15946 Pacific/Rarotonga -CL -3327-07040 America/Santiago Chile (most areas) +CL -3327-07040 America/Santiago most of Chile CL -5309-07055 America/Punta_Arenas Region of Magallanes CL -2709-10926 Pacific/Easter Easter Island CN +3114+12128 Asia/Shanghai Beijing Time @@ -129,10 +131,10 @@ CO +0436-07405 America/Bogota CR +0956-08405 America/Costa_Rica CU +2308-08222 America/Havana CV +1455-02331 Atlantic/Cape_Verde -CY +3510+03322 Asia/Nicosia Cyprus (most areas) +CY +3510+03322 Asia/Nicosia most of Cyprus CY +3507+03357 Asia/Famagusta Northern Cyprus CZ,SK +5005+01426 Europe/Prague -DE,DK,NO,SE,SJ +5230+01322 Europe/Berlin Germany (most areas), Scandinavia +DE,DK,NO,SE,SJ +5230+01322 Europe/Berlin most of Germany DO +1828-06954 America/Santo_Domingo DZ +3647+00303 Africa/Algiers EC -0210-07950 America/Guayaquil Ecuador (mainland) @@ -153,7 +155,7 @@ GB,GG,IM,JE +513030-0000731 Europe/London GE +4143+04449 Asia/Tbilisi GF +0456-05220 America/Cayenne GI +3608-00521 Europe/Gibraltar -GL +6411-05144 America/Nuuk Greenland (most areas) +GL +6411-05144 America/Nuuk most of Greenland GL +7646-01840 America/Danmarkshavn National Park (east coast) GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit GL +7634-06847 America/Thule Thule/Pituffik @@ -183,12 +185,12 @@ JO +3157+03556 Asia/Amman JP +353916+1394441 Asia/Tokyo KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT -0117+03649 Africa/Nairobi KG +4254+07436 Asia/Bishkek -KI,MH,TV,UM,WF +0125+17300 Pacific/Tarawa Gilberts, Marshalls, Tuvalu, Wallis & Futuna, Wake +KI,MH,TV,UM,WF +0125+17300 Pacific/Tarawa Gilberts, Marshalls, Wake KI -0247-17143 Pacific/Kanton Phoenix Islands KI +0152-15720 Pacific/Kiritimati Line Islands KP +3901+12545 Asia/Pyongyang KR +3733+12658 Asia/Seoul -KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) +KZ +4315+07657 Asia/Almaty most of Kazakhstan KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay KZ +5017+05710 Asia/Aqtobe Aqtöbe/Aktobe @@ -205,14 +207,14 @@ MA +3339-00735 Africa/Casablanca MD +4700+02850 Europe/Chisinau MH +0905+16720 Pacific/Kwajalein Kwajalein MM,CC +1647+09610 Asia/Yangon -MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas) +MN +4755+10653 Asia/Ulaanbaatar most of Mongolia MN +4801+09139 Asia/Hovd Bayan-Ölgii, Govi-Altai, Hovd, Uvs, Zavkhan MN +4804+11430 Asia/Choibalsan Dornod, Sükhbaatar MO +221150+1133230 Asia/Macau MQ +1436-06105 America/Martinique MT +3554+01431 Europe/Malta MU -2010+05730 Indian/Mauritius -MV,TF +0410+07330 Indian/Maldives Maldives, Kerguelen, St Paul I, Amsterdam I +MV,TF +0410+07330 Indian/Maldives Kerguelen, St Paul I, Amsterdam I MX +1924-09909 America/Mexico_City Central Mexico MX +2105-08646 America/Cancun Quintana Roo MX +2058-08937 America/Merida Campeche, Yucatán @@ -225,7 +227,7 @@ MX +2313-10625 America/Mazatlan Baja California Sur, Nayarit (most areas), Sinal MX +2048-10515 America/Bahia_Banderas Bahía de Banderas MX +2904-11058 America/Hermosillo Sonora MX +3232-11701 America/Tijuana Baja California -MY,BN +0133+11020 Asia/Kuching Sabah, Sarawak, Brunei +MY,BN +0133+11020 Asia/Kuching Sabah, Sarawak MZ,BI,BW,CD,MW,RW,ZM,ZW -2558+03235 Africa/Maputo Central Africa Time NA -2234+01706 Africa/Windhoek NC -2216+16627 Pacific/Noumea @@ -237,7 +239,7 @@ NR -0031+16655 Pacific/Nauru NU -1901-16955 Pacific/Niue NZ,AQ -3652+17446 Pacific/Auckland New Zealand time NZ -4357-17633 Pacific/Chatham Chatham Islands -PA,CA,KY +0858-07932 America/Panama EST - Panama, Cayman, ON (Atikokan), NU (Coral H) +PA,CA,KY +0858-07932 America/Panama EST - ON (Atikokan), NU (Coral H) PE -1203-07703 America/Lima PF -1732-14934 Pacific/Tahiti Society Islands PF -0900-13930 Pacific/Marquesas Marquesas Islands @@ -285,13 +287,13 @@ RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky RU +5934+15048 Asia/Magadan MSK+08 - Magadan RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island -RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); N Kuril Is RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea -SA,AQ,KW,YE +2438+04643 Asia/Riyadh Arabia, Syowa -SB,FM -0932+16012 Pacific/Guadalcanal Solomons, Pohnpei +SA,AQ,KW,YE +2438+04643 Asia/Riyadh Syowa +SB,FM -0932+16012 Pacific/Guadalcanal Pohnpei SD +1536+03232 Africa/Khartoum -SG,MY +0117+10351 Asia/Singapore Singapore, peninsular Malaysia +SG,MY +0117+10351 Asia/Singapore peninsular Malaysia SR +0550-05510 America/Paramaribo SS +0451+03137 Africa/Juba ST +0020+00644 Africa/Sao_Tome @@ -299,7 +301,7 @@ SV +1342-08912 America/El_Salvador SY +3330+03618 Asia/Damascus TC +2128-07108 America/Grand_Turk TD +1207+01503 Africa/Ndjamena -TH,CX,KH,LA,VN +1345+10031 Asia/Bangkok Indochina (most areas) +TH,CX,KH,LA,VN +1345+10031 Asia/Bangkok north Vietnam TJ +3835+06848 Asia/Dushanbe TK -0922-17114 Pacific/Fakaofo TL -0833+12535 Asia/Dili @@ -308,7 +310,7 @@ TN +3648+01011 Africa/Tunis TO -210800-1751200 Pacific/Tongatapu TR +4101+02858 Europe/Istanbul TW +2503+12130 Asia/Taipei -UA +5026+03031 Europe/Kyiv Ukraine (most areas) +UA +5026+03031 Europe/Kyiv most of Ukraine US +404251-0740023 America/New_York Eastern (most areas) US +421953-0830245 America/Detroit Eastern - MI (most areas) US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area) @@ -328,7 +330,7 @@ US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) US +394421-1045903 America/Denver Mountain (most areas) US +433649-1161209 America/Boise Mountain - ID (south); OR (east) -US,CA +332654-1120424 America/Phoenix MST - Arizona (except Navajo), Creston BC +US,CA +332654-1120424 America/Phoenix MST - AZ (most areas), Creston BC US +340308-1181434 America/Los_Angeles Pacific US +611305-1495401 America/Anchorage Alaska (most areas) US +581807-1342511 America/Juneau Alaska - Juneau area @@ -336,13 +338,13 @@ US +571035-1351807 America/Sitka Alaska - Sitka area US +550737-1313435 America/Metlakatla Alaska - Annette Island US +593249-1394338 America/Yakutat Alaska - Yakutat US +643004-1652423 America/Nome Alaska (west) -US +515248-1763929 America/Adak Aleutian Islands -US,UM +211825-1575130 Pacific/Honolulu Hawaii +US +515248-1763929 America/Adak Alaska - western Aleutians +US +211825-1575130 Pacific/Honolulu Hawaii UY -345433-0561245 America/Montevideo UZ +3940+06648 Asia/Samarkand Uzbekistan (west) UZ +4120+06918 Asia/Tashkent Uzbekistan (east) VE +1030-06656 America/Caracas -VN +1045+10640 Asia/Ho_Chi_Minh Vietnam (south) +VN +1045+10640 Asia/Ho_Chi_Minh south Vietnam VU -1740+16825 Pacific/Efate WS -1350-17144 Pacific/Apia ZA,LS,SZ -2615+02800 Africa/Johannesburg |