New Upstream Snapshot - cctz
Ready changes
Summary
Merged new upstream version: 2.3+git20220321.1.455b5f5 (was: 2.3+dfsg1).
Resulting package
Built on 2022-03-31T10:54 (took 19m50s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-snapshots libcctz-devapt install -t fresh-snapshots libcctz-docapt install -t fresh-snapshots libcctz2-dbgsymapt install -t fresh-snapshots libcctz2
Lintian Result
- cctz_2.3+git20220321.1.455b5f5-1~jan+nus1.dsc
- cctz_2.3+git20220321.1.455b5f5-1~jan+nus1_amd64.buildinfo
- cctz_2.3+git20220321.1.455b5f5-1~jan+nus1_amd64.changes
- libcctz-dev_2.3+git20220321.1.455b5f5-1~jan+nus1_amd64.deb
- libcctz-doc_2.3+git20220321.1.455b5f5-1~jan+nus1_all.deb
- libcctz2-dbgsym_2.3+git20220321.1.455b5f5-1~jan+nus1_amd64.deb
- libcctz2_2.3+git20220321.1.455b5f5-1~jan+nus1_amd64.deb
Diff
diff --git a/BUILD b/BUILD
index 765cb87..cd0ca3e 100644
--- a/BUILD
+++ b/BUILD
@@ -12,19 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-licenses(["notice"]) # Apache License
+licenses(["notice"])
config_setting(
name = "osx",
constraint_values = [
- "@bazel_tools//platforms:osx"
+ "@platforms//os:osx",
],
)
config_setting(
name = "ios",
constraint_values = [
- "@bazel_tools//platforms:ios",
+ "@platforms//os:ios",
],
)
@@ -65,6 +65,7 @@ cc_library(
"include/cctz/time_zone.h",
"include/cctz/zone_info_source.h",
],
+ includes = ["include"],
linkopts = select({
"//:osx": [
"-framework Foundation",
@@ -74,13 +75,17 @@ cc_library(
],
"//conditions:default": [],
}),
- includes = ["include"],
visibility = ["//visibility:public"],
deps = [":civil_time"],
)
### tests
+test_suite(
+ name = "all_tests",
+ visibility = ["//visibility:public"],
+)
+
cc_test(
name = "civil_time_test",
size = "small",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e8b2f34..19cce31 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,9 @@
cmake_minimum_required(VERSION 2.8.12)
+if (POLICY CMP0025)
+ cmake_policy(SET CMP0025 NEW)
+endif()
+
project(cctz)
set(CMAKE_MODULE_PATH
@@ -96,7 +100,9 @@ target_include_directories(cctz PUBLIC
set_target_properties(cctz PROPERTIES
PUBLIC_HEADER "${CCTZ_HDRS}"
)
-target_link_libraries(cctz PUBLIC $<$<PLATFORM_ID:Darwin>:${CoreFoundation}>)
+if(APPLE)
+ target_link_libraries(cctz PUBLIC ${CoreFoundation})
+endif()
add_library(cctz::cctz ALIAS cctz)
if (BUILD_TOOLS)
@@ -159,13 +165,14 @@ include(GNUInstallDirs)
install(TARGETS cctz
EXPORT ${PROJECT_NAME}-targets
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cctz
+ RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
if (BUILD_TOOLS)
install(TARGETS time_tool
EXPORT ${PROJECT_NAME}-targets
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
set(CMAKE_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
@@ -177,4 +184,10 @@ install(FILES cmake/${PROJECT_NAME}-config.cmake
DESTINATION ${CMAKE_INSTALL_CONFIGDIR}
)
-feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
+if (CMAKE_VERSION VERSION_LESS "3.8")
+ set(quiet_on_empty "")
+else()
+ set(quiet_on_empty QUIET_ON_EMPTY)
+endif()
+
+feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES ${quiet_on_empty})
diff --git a/WORKSPACE b/WORKSPACE
index 6676288..16d3f2e 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -5,13 +5,23 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# GoogleTest/GoogleMock framework. Used by most unit-tests.
http_archive(
name = "com_google_googletest",
- urls = ["https://github.com/google/googletest/archive/master.zip"],
- strip_prefix = "googletest-master",
+ sha256 = "353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a",
+ strip_prefix = "googletest-release-1.11.0",
+ urls = ["https://github.com/google/googletest/archive/release-1.11.0.zip"],
)
# Google Benchmark library.
http_archive(
name = "com_github_google_benchmark",
- urls = ["https://github.com/google/benchmark/archive/master.zip"],
- strip_prefix = "benchmark-master",
+ sha256 = "30f2e5156de241789d772dd8b130c1cb5d33473cc2f29e4008eab680df7bd1f0",
+ strip_prefix = "benchmark-1.5.5",
+ urls = ["https://github.com/google/benchmark/archive/v1.5.5.zip"],
+)
+
+# Bazel platform rules.
+http_archive(
+ name = "platforms",
+ sha256 = "b601beaf841244de5c5a50d2b2eddd34839788000fa1be4260ce6603ca0d8eb7",
+ strip_prefix = "platforms-98939346da932eef0b54cf808622f5bb0928f00b",
+ urls = ["https://github.com/bazelbuild/platforms/archive/98939346da932eef0b54cf808622f5bb0928f00b.zip"],
)
diff --git a/debian/changelog b/debian/changelog
index 5fdb2f5..0da5b08 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+cctz (2.3+git20220321.1.455b5f5-1) UNRELEASED; urgency=low
+
+ * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk> Thu, 31 Mar 2022 10:50:23 -0000
+
cctz (2.3+dfsg1-3) unstable; urgency=medium
* [143a67c] Do not run benchmark tests on mips
diff --git a/debian/patches/0001-Compile-shared-lib-and-install-it.patch b/debian/patches/0001-Compile-shared-lib-and-install-it.patch
index d4ad8ef..314ec25 100644
--- a/debian/patches/0001-Compile-shared-lib-and-install-it.patch
+++ b/debian/patches/0001-Compile-shared-lib-and-install-it.patch
@@ -6,11 +6,11 @@ Subject: [PATCH 1/2] Compile shared lib and install it
CMakeLists.txt | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
-Index: cctz-2.3/CMakeLists.txt
+Index: cctz/CMakeLists.txt
===================================================================
---- cctz-2.3.orig/CMakeLists.txt
-+++ cctz-2.3/CMakeLists.txt
-@@ -68,7 +68,7 @@ set(CCTZ_HDRS
+--- cctz.orig/CMakeLists.txt
++++ cctz/CMakeLists.txt
+@@ -72,7 +72,7 @@ set(CCTZ_HDRS
include/cctz/zone_info_source.h
include/cctz/civil_time.h
)
@@ -19,9 +19,9 @@ Index: cctz-2.3/CMakeLists.txt
src/civil_time_detail.cc
src/time_zone_fixed.cc
src/time_zone_fixed.h
-@@ -98,6 +98,14 @@ set_target_properties(cctz PROPERTIES
- )
- target_link_libraries(cctz PUBLIC $<$<PLATFORM_ID:Darwin>:${CoreFoundation}>)
+@@ -104,6 +104,14 @@ if(APPLE)
+ target_link_libraries(cctz PUBLIC ${CoreFoundation})
+ endif()
add_library(cctz::cctz ALIAS cctz)
+set_target_properties(cctz PROPERTIES
+ OUTPUT_NAME "cctz"
diff --git a/debian/patches/0002-Enable-tests-for-Debian.patch b/debian/patches/0002-Enable-tests-for-Debian.patch
index b154e58..0a48d83 100644
--- a/debian/patches/0002-Enable-tests-for-Debian.patch
+++ b/debian/patches/0002-Enable-tests-for-Debian.patch
@@ -10,7 +10,7 @@ Index: cctz/CMakeLists.txt
===================================================================
--- cctz.orig/CMakeLists.txt
+++ cctz/CMakeLists.txt
-@@ -21,19 +21,15 @@ if (BUILD_TESTING)
+@@ -25,19 +25,15 @@ if (BUILD_TESTING)
URL "https://github.com/google/benchmark"
)
@@ -38,7 +38,7 @@ Index: cctz/CMakeLists.txt
find_package(Threads)
set_package_properties(Threads PROPERTIES
-@@ -118,7 +114,7 @@ if (BUILD_EXAMPLES)
+@@ -124,7 +120,7 @@ if (BUILD_EXAMPLES)
endif()
if (BUILD_TESTING)
@@ -47,7 +47,7 @@ Index: cctz/CMakeLists.txt
cctz_target_set_cxx_standard(civil_time_test)
target_include_directories(civil_time_test PRIVATE ${GTEST_INCLUDE_DIRS})
target_link_libraries(civil_time_test
-@@ -128,7 +124,9 @@ if (BUILD_TESTING)
+@@ -134,7 +130,9 @@ if (BUILD_TESTING)
)
add_test(civil_time_test civil_time_test)
@@ -58,7 +58,7 @@ Index: cctz/CMakeLists.txt
cctz_target_set_cxx_standard(time_zone_lookup_test)
target_include_directories(time_zone_lookup_test PRIVATE ${GTEST_INCLUDE_DIRS})
target_link_libraries(time_zone_lookup_test
-@@ -138,12 +136,14 @@ if (BUILD_TESTING)
+@@ -144,12 +142,14 @@ if (BUILD_TESTING)
)
add_test(time_zone_lookup_test time_zone_lookup_test)
@@ -75,7 +75,7 @@ Index: cctz/CMakeLists.txt
)
add_test(time_zone_format_test time_zone_format_test)
-@@ -160,6 +160,10 @@ if (BUILD_TESTING)
+@@ -166,6 +166,10 @@ if (BUILD_TESTING)
add_executable(cctz_benchmark src/cctz_benchmark.cc)
cctz_target_set_cxx_standard(cctz_benchmark)
target_link_libraries(cctz_benchmark cctz::cctz benchmark::benchmark_main)
diff --git a/debian/patches/use_system_zoneinfo.patch b/debian/patches/use_system_zoneinfo.patch
index a6564d6..48ba29f 100644
--- a/debian/patches/use_system_zoneinfo.patch
+++ b/debian/patches/use_system_zoneinfo.patch
@@ -2,11 +2,11 @@ Description: use zoneinfo shipped with the system
Author: Anton Gladky <gladk@debian.org>
Last-Update: 2017-11-15
-Index: cctz-2.3/CMakeLists.txt
+Index: cctz/CMakeLists.txt
===================================================================
---- cctz-2.3.orig/CMakeLists.txt
-+++ cctz-2.3/CMakeLists.txt
-@@ -154,7 +154,7 @@ if (BUILD_TESTING)
+--- cctz.orig/CMakeLists.txt
++++ cctz/CMakeLists.txt
+@@ -160,7 +160,7 @@ if (BUILD_TESTING)
time_zone_format_test
time_zone_lookup_test
PROPERTY
diff --git a/include/cctz/civil_time.h b/include/cctz/civil_time.h
index 7366ccc..57bd86c 100644
--- a/include/cctz/civil_time.h
+++ b/include/cctz/civil_time.h
@@ -148,7 +148,7 @@ namespace cctz {
//
// All civil-time types have accessors for all six of the civil-time fields:
// year, month, day, hour, minute, and second. Recall that fields inferior to
-// the type's aligment will be set to their minimum valid value.
+// the type's alignment will be set to their minimum valid value.
//
// civil_day d(2015, 6, 28);
// // d.year() == 2015
@@ -277,7 +277,7 @@ using civil_second = detail::civil_second;
//
using detail::weekday;
-// Returns the weekday for the given civil_day.
+// Returns the weekday for the given civil-time value.
//
// civil_day a(2015, 8, 13);
// weekday wd = get_weekday(a); // wd == weekday::thursday
@@ -304,14 +304,14 @@ using detail::get_weekday;
//
// civil_day d = ...
// // Gets the following Thursday if d is not already Thursday
-// civil_day thurs1 = prev_weekday(d, weekday::thursday) + 7;
+// civil_day thurs1 = next_weekday(d - 1, weekday::thursday);
// // Gets the previous Thursday if d is not already Thursday
-// civil_day thurs2 = next_weekday(d, weekday::thursday) - 7;
+// civil_day thurs2 = prev_weekday(d + 1, weekday::thursday);
//
using detail::next_weekday;
using detail::prev_weekday;
-// Returns the day-of-year for the given civil_day.
+// Returns the day-of-year for the given civil-time value.
//
// civil_day a(2015, 1, 1);
// int yd_jan_1 = get_yearday(a); // yd_jan_1 = 1
diff --git a/include/cctz/civil_time_detail.h b/include/cctz/civil_time_detail.h
index decc5f2..2dab664 100644
--- a/include/cctz/civil_time_detail.h
+++ b/include/cctz/civil_time_detail.h
@@ -21,7 +21,7 @@
#include <type_traits>
// Disable constexpr support unless we are in C++14 mode.
-#if __cpp_constexpr >= 201304 || _MSC_VER >= 1910
+#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910)
#define CONSTEXPR_D constexpr // data
#define CONSTEXPR_F constexpr // function
#define CONSTEXPR_M constexpr // member
@@ -79,14 +79,13 @@ CONSTEXPR_F bool is_leap_year(year_t y) noexcept {
return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}
CONSTEXPR_F int year_index(year_t y, month_t m) noexcept {
- return (static_cast<int>((y + (m > 2)) % 400) + 400) % 400;
+ const int yi = static_cast<int>((y + (m > 2)) % 400);
+ return yi < 0 ? yi + 400 : yi;
}
-CONSTEXPR_F int days_per_century(year_t y, month_t m) noexcept {
- const int yi = year_index(y, m);
+CONSTEXPR_F int days_per_century(int yi) noexcept {
return 36524 + (yi == 0 || yi > 300);
}
-CONSTEXPR_F int days_per_4years(year_t y, month_t m) noexcept {
- const int yi = year_index(y, m);
+CONSTEXPR_F int days_per_4years(int yi) noexcept {
return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96);
}
CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept {
@@ -101,54 +100,69 @@ CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept {
CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd,
hour_t hh, minute_t mm, second_t ss) noexcept {
- y += (cd / 146097) * 400;
+ year_t ey = y % 400;
+ const year_t oey = ey;
+ ey += (cd / 146097) * 400;
cd %= 146097;
if (cd < 0) {
- y -= 400;
+ ey -= 400;
cd += 146097;
}
- y += (d / 146097) * 400;
+ ey += (d / 146097) * 400;
d = d % 146097 + cd;
if (d > 0) {
if (d > 146097) {
- y += 400;
+ ey += 400;
d -= 146097;
}
} else {
if (d > -365) {
// We often hit the previous year when stepping a civil time backwards,
// so special case it to avoid counting up by 100/4/1-year chunks.
- y -= 1;
- d += days_per_year(y, m);
+ ey -= 1;
+ d += days_per_year(ey, m);
} else {
- y -= 400;
+ ey -= 400;
d += 146097;
}
}
if (d > 365) {
- for (int n = days_per_century(y, m); d > n; n = days_per_century(y, m)) {
+ int yi = year_index(ey, m); // Index into Gregorian 400 year cycle.
+ for (;;) {
+ int n = days_per_century(yi);
+ if (d <= n) break;
d -= n;
- y += 100;
+ ey += 100;
+ yi += 100;
+ if (yi >= 400) yi -= 400;
}
- for (int n = days_per_4years(y, m); d > n; n = days_per_4years(y, m)) {
+ for (;;) {
+ int n = days_per_4years(yi);
+ if (d <= n) break;
d -= n;
- y += 4;
+ ey += 4;
+ yi += 4;
+ if (yi >= 400) yi -= 400;
}
- for (int n = days_per_year(y, m); d > n; n = days_per_year(y, m)) {
+ for (;;) {
+ int n = days_per_year(ey, m);
+ if (d <= n) break;
d -= n;
- ++y;
+ ++ey;
}
}
if (d > 28) {
- for (int n = days_per_month(y, m); d > n; n = days_per_month(y, m)) {
+ for (;;) {
+ int n = days_per_month(ey, m);
+ if (d <= n) break;
d -= n;
if (++m > 12) {
- ++y;
+ ++ey;
m = 1;
}
}
}
- return fields(y, m, static_cast<day_t>(d), hh, mm, ss);
+ return fields(y + (ey - oey), m, static_cast<day_t>(d), hh, mm, ss);
}
CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd,
hour_t hh, minute_t mm, second_t ss) noexcept {
@@ -372,16 +386,10 @@ class civil_time {
// Assigning arithmetic.
CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept {
- f_ = step(T{}, f_, n);
- return *this;
+ return *this = *this + n;
}
CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept {
- if (n != (std::numeric_limits<diff_t>::min)()) {
- f_ = step(T{}, f_, -n);
- } else {
- f_ = step(T{}, step(T{}, f_, -(n + 1)), 1);
- }
- return *this;
+ return *this = *this - n;
}
CONSTEXPR_M civil_time& operator++() noexcept {
return *this += 1;
@@ -402,13 +410,15 @@ class civil_time {
// Binary arithmetic operators.
friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept {
- return a += n;
+ return civil_time(step(T{}, a.f_, n));
}
friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept {
- return a += n;
+ return a + n;
}
friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept {
- return a -= n;
+ return n != (std::numeric_limits<diff_t>::min)()
+ ? civil_time(step(T{}, a.f_, -n))
+ : civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1));
}
friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept {
return difference(T{}, lhs.f_, rhs.f_);
@@ -497,7 +507,7 @@ enum class weekday {
sunday,
};
-CONSTEXPR_F weekday get_weekday(const civil_day& cd) noexcept {
+CONSTEXPR_F weekday get_weekday(const civil_second& cs) noexcept {
CONSTEXPR_D weekday k_weekday_by_mon_off[13] = {
weekday::monday, weekday::tuesday, weekday::wednesday,
weekday::thursday, weekday::friday, weekday::saturday,
@@ -508,30 +518,60 @@ CONSTEXPR_F weekday get_weekday(const civil_day& cd) noexcept {
CONSTEXPR_D int k_weekday_offsets[1 + 12] = {
-1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4,
};
- year_t wd = 2400 + (cd.year() % 400) - (cd.month() < 3);
+ year_t wd = 2400 + (cs.year() % 400) - (cs.month() < 3);
wd += wd / 4 - wd / 100 + wd / 400;
- wd += k_weekday_offsets[cd.month()] + cd.day();
+ wd += k_weekday_offsets[cs.month()] + cs.day();
return k_weekday_by_mon_off[wd % 7 + 6];
}
////////////////////////////////////////////////////////////////////////
CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept {
- do { cd += 1; } while (get_weekday(cd) != wd);
- return cd;
+ CONSTEXPR_D weekday k_weekdays_forw[14] = {
+ weekday::monday, weekday::tuesday, weekday::wednesday,
+ weekday::thursday, weekday::friday, weekday::saturday,
+ weekday::sunday, weekday::monday, weekday::tuesday,
+ weekday::wednesday, weekday::thursday, weekday::friday,
+ weekday::saturday, weekday::sunday,
+ };
+ weekday base = get_weekday(cd);
+ for (int i = 0;; ++i) {
+ if (base == k_weekdays_forw[i]) {
+ for (int j = i + 1;; ++j) {
+ if (wd == k_weekdays_forw[j]) {
+ return cd + (j - i);
+ }
+ }
+ }
+ }
}
CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept {
- do { cd -= 1; } while (get_weekday(cd) != wd);
- return cd;
+ CONSTEXPR_D weekday k_weekdays_back[14] = {
+ weekday::sunday, weekday::saturday, weekday::friday,
+ weekday::thursday, weekday::wednesday, weekday::tuesday,
+ weekday::monday, weekday::sunday, weekday::saturday,
+ weekday::friday, weekday::thursday, weekday::wednesday,
+ weekday::tuesday, weekday::monday,
+ };
+ weekday base = get_weekday(cd);
+ for (int i = 0;; ++i) {
+ if (base == k_weekdays_back[i]) {
+ for (int j = i + 1;; ++j) {
+ if (wd == k_weekdays_back[j]) {
+ return cd - (j - i);
+ }
+ }
+ }
+ }
}
-CONSTEXPR_F int get_yearday(const civil_day& cd) noexcept {
+CONSTEXPR_F int get_yearday(const civil_second& cs) noexcept {
CONSTEXPR_D int k_month_offsets[1 + 12] = {
-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
};
- const int feb29 = (cd.month() > 2 && impl::is_leap_year(cd.year()));
- return k_month_offsets[cd.month()] + feb29 + cd.day();
+ const int feb29 = (cs.month() > 2 && impl::is_leap_year(cs.year()));
+ return k_month_offsets[cs.month()] + feb29 + cs.day();
}
////////////////////////////////////////////////////////////////////////
diff --git a/include/cctz/time_zone.h b/include/cctz/time_zone.h
index f97ea26..55005f8 100644
--- a/include/cctz/time_zone.h
+++ b/include/cctz/time_zone.h
@@ -22,6 +22,7 @@
#include <chrono>
#include <cstdint>
+#include <limits>
#include <string>
#include <utility>
@@ -37,20 +38,9 @@ using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead.
namespace detail {
template <typename D>
-inline std::pair<time_point<seconds>, D>
-split_seconds(const time_point<D>& tp) {
- auto sec = std::chrono::time_point_cast<seconds>(tp);
- auto sub = tp - sec;
- if (sub.count() < 0) {
- sec -= seconds(1);
- sub += seconds(1);
- }
- return {sec, std::chrono::duration_cast<D>(sub)};
-}
-inline std::pair<time_point<seconds>, seconds>
-split_seconds(const time_point<seconds>& tp) {
- return {tp, seconds::zero()};
-}
+std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp);
+std::pair<time_point<seconds>, seconds> split_seconds(
+ const time_point<seconds>& tp);
} // namespace detail
// cctz::time_zone is an opaque, small, value-type class representing a
@@ -274,6 +264,20 @@ std::string format(const std::string&, const time_point<seconds>&,
const femtoseconds&, const time_zone&);
bool parse(const std::string&, const std::string&, const time_zone&,
time_point<seconds>*, femtoseconds*, std::string* err = nullptr);
+template <typename Rep, std::intmax_t Denom>
+bool join_seconds(
+ const time_point<seconds>& sec, const femtoseconds& fs,
+ time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp);
+template <typename Rep, std::intmax_t Num>
+bool join_seconds(
+ const time_point<seconds>& sec, const femtoseconds& fs,
+ time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp);
+template <typename Rep>
+bool join_seconds(
+ const time_point<seconds>& sec, const femtoseconds& fs,
+ time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp);
+bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
+ time_point<seconds>* tpp);
} // namespace detail
// Formats the given time_point in the given cctz::time_zone according to
@@ -287,6 +291,7 @@ bool parse(const std::string&, const std::string&, const time_zone&,
// - %E#f - Fractional seconds with # digits of precision
// - %E*f - Fractional seconds with full precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
+// - %ET - The RFC3339 "date-time" separator "T"
//
// Note that %E0S behaves like %S, and %E0f produces no characters. In
// contrast %E*f always produces at least one digit, which may be '0'.
@@ -316,7 +321,8 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
// returns the corresponding time_point. Uses strftime()-like formatting
// options, with the same extensions as cctz::format(), but with the
// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
-// and %E*z also accept the same inputs.
+// and %E*z also accept the same inputs, which (along with %z) includes
+// 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'.
//
// %Y consumes as many numeric characters as it can, so the matching data
// should always be terminated with a non-numeric. %E4Y always consumes
@@ -362,15 +368,84 @@ inline bool parse(const std::string& fmt, const std::string& input,
const time_zone& tz, time_point<D>* tpp) {
time_point<seconds> sec;
detail::femtoseconds fs;
- const bool b = detail::parse(fmt, input, tz, &sec, &fs);
- if (b) {
- // TODO: Return false if unrepresentable as a time_point<D>.
- *tpp = std::chrono::time_point_cast<D>(sec);
- *tpp += std::chrono::duration_cast<D>(fs);
+ return detail::parse(fmt, input, tz, &sec, &fs) &&
+ detail::join_seconds(sec, fs, tpp);
+}
+
+namespace detail {
+
+// Split a time_point<D> into a time_point<seconds> and a D subseconds.
+// Undefined behavior if time_point<seconds> is not of sufficient range.
+// Note that this means it is UB to call cctz::time_zone::lookup(tp) or
+// cctz::format(fmt, tp, tz) with a time_point that is outside the range
+// of a 64-bit std::time_t.
+template <typename D>
+std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp) {
+ auto sec = std::chrono::time_point_cast<seconds>(tp);
+ auto sub = tp - sec;
+ if (sub.count() < 0) {
+ sec -= seconds(1);
+ sub += seconds(1);
}
- return b;
+ return {sec, std::chrono::duration_cast<D>(sub)};
+}
+
+inline std::pair<time_point<seconds>, seconds> split_seconds(
+ const time_point<seconds>& tp) {
+ return {tp, seconds::zero()};
+}
+
+// Join a time_point<seconds> and femto subseconds into a time_point<D>.
+// Floors to the resolution of time_point<D>. Returns false if time_point<D>
+// is not of sufficient range.
+template <typename Rep, std::intmax_t Denom>
+bool join_seconds(
+ const time_point<seconds>& sec, const femtoseconds& fs,
+ time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp) {
+ using D = std::chrono::duration<Rep, std::ratio<1, Denom>>;
+ // TODO(#199): Return false if result unrepresentable as a time_point<D>.
+ *tpp = std::chrono::time_point_cast<D>(sec);
+ *tpp += std::chrono::duration_cast<D>(fs);
+ return true;
}
+template <typename Rep, std::intmax_t Num>
+bool join_seconds(
+ const time_point<seconds>& sec, const femtoseconds&,
+ time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp) {
+ using D = std::chrono::duration<Rep, std::ratio<Num, 1>>;
+ auto count = sec.time_since_epoch().count();
+ if (count >= 0 || count % Num == 0) {
+ count /= Num;
+ } else {
+ count /= Num;
+ count -= 1;
+ }
+ if (count > (std::numeric_limits<Rep>::max)()) return false;
+ if (count < (std::numeric_limits<Rep>::min)()) return false;
+ *tpp = time_point<D>() + D{static_cast<Rep>(count)};
+ return true;
+}
+
+template <typename Rep>
+bool join_seconds(
+ const time_point<seconds>& sec, const femtoseconds&,
+ time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp) {
+ using D = std::chrono::duration<Rep, std::ratio<1, 1>>;
+ auto count = sec.time_since_epoch().count();
+ if (count > (std::numeric_limits<Rep>::max)()) return false;
+ if (count < (std::numeric_limits<Rep>::min)()) return false;
+ *tpp = time_point<D>() + D{static_cast<Rep>(count)};
+ return true;
+}
+
+inline bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
+ time_point<seconds>* tpp) {
+ *tpp = sec;
+ return true;
+}
+
+} // namespace detail
} // namespace cctz
#endif // CCTZ_TIME_ZONE_H_
diff --git a/src/cctz_benchmark.cc b/src/cctz_benchmark.cc
index 179ae50..9725415 100644
--- a/src/cctz_benchmark.cc
+++ b/src/cctz_benchmark.cc
@@ -45,8 +45,58 @@ void BM_Step_Days(benchmark::State& state) {
}
BENCHMARK(BM_Step_Days);
-const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
-const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
+void BM_GetWeekday(benchmark::State& state) {
+ const cctz::civil_day c(2014, 8, 22);
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(cctz::get_weekday(c));
+ }
+}
+BENCHMARK(BM_GetWeekday);
+
+void BM_NextWeekday(benchmark::State& state) {
+ const cctz::civil_day kStart(2014, 8, 22);
+ const cctz::civil_day kDays[7] = {
+ kStart + 0, kStart + 1, kStart + 2, kStart + 3,
+ kStart + 4, kStart + 5, kStart + 6,
+ };
+ const cctz::weekday kWeekdays[7] = {
+ cctz::weekday::monday, cctz::weekday::tuesday, cctz::weekday::wednesday,
+ cctz::weekday::thursday, cctz::weekday::friday, cctz::weekday::saturday,
+ cctz::weekday::sunday,
+ };
+ while (state.KeepRunningBatch(7 * 7)) {
+ for (const auto from : kDays) {
+ for (const auto to : kWeekdays) {
+ benchmark::DoNotOptimize(cctz::next_weekday(from, to));
+ }
+ }
+ }
+}
+BENCHMARK(BM_NextWeekday);
+
+void BM_PrevWeekday(benchmark::State& state) {
+ const cctz::civil_day kStart(2014, 8, 22);
+ const cctz::civil_day kDays[7] = {
+ kStart + 0, kStart + 1, kStart + 2, kStart + 3,
+ kStart + 4, kStart + 5, kStart + 6,
+ };
+ const cctz::weekday kWeekdays[7] = {
+ cctz::weekday::monday, cctz::weekday::tuesday, cctz::weekday::wednesday,
+ cctz::weekday::thursday, cctz::weekday::friday, cctz::weekday::saturday,
+ cctz::weekday::sunday,
+ };
+ while (state.KeepRunningBatch(7 * 7)) {
+ for (const auto from : kDays) {
+ for (const auto to : kWeekdays) {
+ benchmark::DoNotOptimize(cctz::prev_weekday(from, to));
+ }
+ }
+ }
+}
+BENCHMARK(BM_PrevWeekday);
+
+const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
+const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
@@ -229,6 +279,7 @@ const char* const kTimeZoneNames[] = {
"America/North_Dakota/Beulah",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
+ "America/Nuuk",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
@@ -596,6 +647,7 @@ const char* const kTimeZoneNames[] = {
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Johnston",
+ "Pacific/Kanton",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
@@ -940,12 +992,12 @@ void BM_Time_FromCivilDay0_Libc(benchmark::State& state) {
BENCHMARK(BM_Time_FromCivilDay0_Libc);
const char* const kFormats[] = {
- RFC1123_full, // 0
- RFC1123_no_wday, // 1
- RFC3339_full, // 2
- RFC3339_sec, // 3
- "%Y-%m-%dT%H:%M:%S", // 4
- "%Y-%m-%d", // 5
+ RFC1123_full, // 0
+ RFC1123_no_wday, // 1
+ RFC3339_full, // 2
+ RFC3339_sec, // 3
+ "%Y-%m-%d%ET%H:%M:%S", // 4
+ "%Y-%m-%d", // 5
};
const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
diff --git a/src/civil_time_test.cc b/src/civil_time_test.cc
index 999f7d4..5ec0ca8 100644
--- a/src/civil_time_test.cc
+++ b/src/civil_time_test.cc
@@ -35,7 +35,7 @@ std::string Format(const T& t) {
} // namespace
-#if __cpp_constexpr >= 201304 || _MSC_VER >= 1910
+#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910)
// Construction constexpr tests
TEST(CivilTime, Normal) {
@@ -230,6 +230,16 @@ TEST(CivilTime, Difference) {
static_assert(diff == 365, "Difference");
}
+// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
+TEST(CivilTime, ConstructionWithHugeYear) {
+ constexpr civil_hour h(-9223372036854775807, 1, 1, -1);
+ static_assert(h.year() == -9223372036854775807 - 1,
+ "ConstructionWithHugeYear");
+ static_assert(h.month() == 12, "ConstructionWithHugeYear");
+ static_assert(h.day() == 31, "ConstructionWithHugeYear");
+ static_assert(h.hour() == 23, "ConstructionWithHugeYear");
+}
+
// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
TEST(CivilTime, DifferenceWithHugeYear) {
{
@@ -317,7 +327,7 @@ TEST(CivilTime, YearDay) {
constexpr int yd = get_yearday(cd);
static_assert(yd == 28, "YearDay");
}
-#endif // __cpp_constexpr >= 201304 || _MSC_VER >= 1910
+#endif // __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910)
// The remaining tests do not use constexpr.
@@ -819,6 +829,8 @@ TEST(CivilTime, Properties) {
EXPECT_EQ(4, ss.hour());
EXPECT_EQ(5, ss.minute());
EXPECT_EQ(6, ss.second());
+ EXPECT_EQ(weekday::tuesday, get_weekday(ss));
+ EXPECT_EQ(34, get_yearday(ss));
civil_minute mm(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, mm.year());
@@ -827,6 +839,8 @@ TEST(CivilTime, Properties) {
EXPECT_EQ(4, mm.hour());
EXPECT_EQ(5, mm.minute());
EXPECT_EQ(0, mm.second());
+ EXPECT_EQ(weekday::tuesday, get_weekday(mm));
+ EXPECT_EQ(34, get_yearday(mm));
civil_hour hh(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, hh.year());
@@ -835,6 +849,8 @@ TEST(CivilTime, Properties) {
EXPECT_EQ(4, hh.hour());
EXPECT_EQ(0, hh.minute());
EXPECT_EQ(0, hh.second());
+ EXPECT_EQ(weekday::tuesday, get_weekday(hh));
+ EXPECT_EQ(34, get_yearday(hh));
civil_day d(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, d.year());
@@ -853,6 +869,8 @@ TEST(CivilTime, Properties) {
EXPECT_EQ(0, m.hour());
EXPECT_EQ(0, m.minute());
EXPECT_EQ(0, m.second());
+ EXPECT_EQ(weekday::sunday, get_weekday(m));
+ EXPECT_EQ(32, get_yearday(m));
civil_year y(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, y.year());
@@ -861,6 +879,8 @@ TEST(CivilTime, Properties) {
EXPECT_EQ(0, y.hour());
EXPECT_EQ(0, y.minute());
EXPECT_EQ(0, y.second());
+ EXPECT_EQ(weekday::thursday, get_weekday(y));
+ EXPECT_EQ(1, get_yearday(y));
}
TEST(CivilTime, OutputStream) {
@@ -1033,7 +1053,7 @@ TEST(CivilTime, LeapYears) {
TEST(CivilTime, FirstThursdayInMonth) {
const civil_day nov1(2014, 11, 1);
- const civil_day thursday = prev_weekday(nov1, weekday::thursday) + 7;
+ const civil_day thursday = next_weekday(nov1 - 1, weekday::thursday);
EXPECT_EQ("2014-11-06", Format(thursday));
// Bonus: Date of Thanksgiving in the United States
diff --git a/src/time_tool.cc b/src/time_tool.cc
index 63a0db6..31ce2ba 100644
--- a/src/time_tool.cc
+++ b/src/time_tool.cc
@@ -20,6 +20,7 @@
#include <cstring>
#include <ctime>
#include <iomanip>
+#include <ios>
#include <iostream>
#include <limits>
#include <sstream>
@@ -37,9 +38,9 @@ using seconds = cctz::seconds;
// parse() specifiers for command-line time arguments.
const char* const kFormats[] = {
"%Y %m %d %H %M %E*S",
- "%Y - %m - %d T %H : %M : %E*S",
+ "%Y - %m - %d %ET %H : %M : %E*S",
"%Y - %m - %d %H : %M : %E*S",
- "%Y - %m - %d T %H : %M",
+ "%Y - %m - %d %ET %H : %M",
"%Y - %m - %d %H : %M",
"%Y - %m - %d",
"%a %b %d %H : %M : %E*S %Z %Y",
@@ -60,7 +61,7 @@ const char* const kFormats[] = {
bool ParseTimeSpec(const std::string& args, time_point<seconds>* when) {
const cctz::time_zone ignored{};
- for (const char* const* fmt = kFormats; *fmt != NULL; ++fmt) {
+ for (const char* const* fmt = kFormats; *fmt != nullptr; ++fmt) {
const std::string format = std::string(*fmt) + " %E*z";
time_point<seconds> tp;
if (cctz::parse(format, args, ignored, &tp)) {
@@ -73,7 +74,7 @@ bool ParseTimeSpec(const std::string& args, time_point<seconds>* when) {
bool ParseCivilSpec(const std::string& args, cctz::civil_second* when) {
const cctz::time_zone utc = cctz::utc_time_zone();
- for (const char* const* fmt = kFormats; *fmt != NULL; ++fmt) {
+ for (const char* const* fmt = kFormats; *fmt != nullptr; ++fmt) {
time_point<seconds> tp;
if (cctz::parse(*fmt, args, utc, &tp)) {
*when = cctz::convert(tp, utc);
@@ -101,10 +102,9 @@ std::string FormatTimeInZone(const std::string& fmt, time_point<seconds> when,
std::ostringstream oss;
oss << std::setw(36) << std::left << cctz::format(fmt, when, zone);
cctz::time_zone::absolute_lookup al = zone.lookup(when);
- cctz::civil_day cd(al.cs);
- oss << " [wd=" << WeekDayName(cctz::get_weekday(cd))
+ oss << " [wd=" << WeekDayName(cctz::get_weekday(al.cs))
<< " yd=" << std::setw(3) << std::setfill('0')
- << std::right << cctz::get_yearday(cd)
+ << std::right << cctz::get_yearday(al.cs)
<< " dst=" << (al.is_dst ? 'T' : 'F')
<< " off=" << std::showpos << al.offset << std::noshowpos << "]";
return oss.str();
@@ -142,7 +142,7 @@ void InstantInfo(const std::string& label, const std::string& fmt,
}
// Report everything we know about a cctz::civil_second (YMDHMS).
-void CivilInfo(const std::string& fmt, const cctz::civil_second& cs,
+void CivilInfo(const std::string& fmt, const cctz::civil_second cs,
cctz::time_zone zone) {
ZoneInfo("tz: ", zone);
cctz::time_zone::civil_lookup cl = zone.lookup(cs);
@@ -215,7 +215,7 @@ void ZoneDump(bool zdump, const std::string& fmt, cctz::time_zone zone,
std::cout << " isdst=" << (al.is_dst ? '1' : '0')
<< " gmtoff=" << al.offset << "\n";
} else {
- const char* wd = WeekDayName(get_weekday(cctz::civil_day(al.cs)));
+ const char* wd = WeekDayName(get_weekday(al.cs));
std::cout << " [wd=" << wd << " dst=" << (al.is_dst ? 'T' : 'F')
<< " off=" << al.offset << "]\n";
}
@@ -233,7 +233,7 @@ void ZoneDump(bool zdump, const std::string& fmt, cctz::time_zone zone,
}
const char* Basename(const char* p) {
- if (const char* b = strrchr(p, '/')) return ++b;
+ if (const char* b = std::strrchr(p, '/')) return ++b;
return p;
}
@@ -297,19 +297,19 @@ bool ParseYearRange(bool zdump, const std::string& args,
return false;
}
-int main(int argc, char** argv) {
+int main(int argc, const char** argv) {
const char* argv0 = (argc > 0) ? (argc--, *argv++) : (argc = 0, "time_tool");
const std::string prog = Basename(argv0);
// Escape arguments that look like negative offsets so that they
// don't look like flags.
+ std::vector<std::string> eargs;
for (int i = 0; i < argc; ++i) {
- if (strcmp(argv[i], "--") == 0) break;
+ if (std::strcmp(argv[i], "--") == 0) break;
if (LooksLikeNegOffset(argv[i])) {
- char* buf = new char[strlen(argv[i] + 2)];
- buf[0] = ' '; // will later be ignorned
- strcpy(buf + 1, argv[i]);
- argv[i] = buf;
+ eargs.push_back(" "); // space will later be ignorned
+ eargs.back().append(argv[i]);
+ argv[i] = eargs.back().c_str();
}
}
@@ -362,27 +362,27 @@ int main(int argc, char** argv) {
++optind;
break;
}
- if (strcmp(opt, "tz") == 0) {
+ if (std::strcmp(opt, "tz") == 0) {
if (optind + 1 == argc) {
std::cerr << argv0 << ": option '--tz' requires an argument\n";
++opterr;
} else {
zones = argv[++optind];
}
- } else if (strncmp(opt, "tz=", 3) == 0) {
+ } else if (std::strncmp(opt, "tz=", 3) == 0) {
zones = opt + 3;
- } else if (strcmp(opt, "fmt") == 0) {
+ } else if (std::strcmp(opt, "fmt") == 0) {
if (optind + 1 == argc) {
std::cerr << argv0 << ": option '--fmt' requires an argument\n";
++opterr;
} else {
fmt = argv[++optind];
}
- } else if (strncmp(opt, "fmt=", 4) == 0) {
+ } else if (std::strncmp(opt, "fmt=", 4) == 0) {
fmt = opt + 4;
- } else if (strcmp(opt, "zdump") == 0) {
+ } else if (std::strcmp(opt, "zdump") == 0) {
zdump = true;
- } else if (strcmp(opt, "zone_dump") == 0) {
+ } else if (std::strcmp(opt, "zone_dump") == 0) {
zone_dump = true;
} else {
std::cerr << argv0 << ": unrecognized option '--" << opt << "'\n";
@@ -433,7 +433,9 @@ int main(int argc, char** argv) {
for (const std::string& tz : StrSplit(',', zones)) {
std::cout << leader;
cctz::time_zone zone;
- if (!cctz::load_time_zone(tz, &zone)) {
+ if (tz == "localtime") {
+ zone = cctz::local_time_zone();
+ } else if (!cctz::load_time_zone(tz, &zone)) {
std::cerr << tz << ": Unrecognized time zone\n";
return 1;
}
diff --git a/src/time_zone_fixed.cc b/src/time_zone_fixed.cc
index 9da22c6..6031d7c 100644
--- a/src/time_zone_fixed.cc
+++ b/src/time_zone_fixed.cc
@@ -25,7 +25,7 @@ namespace cctz {
namespace {
// The prefix used for the internal names of fixed-offset zones.
-const char kFixedOffsetPrefix[] = "Fixed/UTC";
+const char kFixedZonePrefix[] = "Fixed/UTC";
const char kDigits[] = "0123456789";
@@ -48,16 +48,16 @@ int Parse02d(const char* p) {
} // namespace
bool FixedOffsetFromName(const std::string& name, seconds* offset) {
- if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
+ if (name == "UTC" || name == "UTC0") {
*offset = seconds::zero();
return true;
}
- const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
- const char* const ep = kFixedOffsetPrefix + prefix_len;
+ const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
+ const char* const ep = kFixedZonePrefix + prefix_len;
if (name.size() != prefix_len + 9) // <prefix>+99:99:99
return false;
- if (!std::equal(kFixedOffsetPrefix, ep, name.begin()))
+ if (!std::equal(kFixedZonePrefix, ep, name.begin()))
return false;
const char* np = name.data() + prefix_len;
if (np[0] != '+' && np[0] != '-')
@@ -86,29 +86,29 @@ std::string FixedOffsetToName(const seconds& offset) {
// offsets and to (somewhat) limit the total number of zones.
return "UTC";
}
- int seconds = static_cast<int>(offset.count());
- const char sign = (seconds < 0 ? '-' : '+');
- int minutes = seconds / 60;
- seconds %= 60;
+ int offset_seconds = static_cast<int>(offset.count());
+ const char sign = (offset_seconds < 0 ? '-' : '+');
+ int offset_minutes = offset_seconds / 60;
+ offset_seconds %= 60;
if (sign == '-') {
- if (seconds > 0) {
- seconds -= 60;
- minutes += 1;
+ if (offset_seconds > 0) {
+ offset_seconds -= 60;
+ offset_minutes += 1;
}
- seconds = -seconds;
- minutes = -minutes;
+ offset_seconds = -offset_seconds;
+ offset_minutes = -offset_minutes;
}
- int hours = minutes / 60;
- minutes %= 60;
- char buf[sizeof(kFixedOffsetPrefix) - 1 + sizeof("-24:00:00")];
- std::strcpy(buf, kFixedOffsetPrefix);
- char* ep = buf + sizeof(kFixedOffsetPrefix) - 1;
+ int offset_hours = offset_minutes / 60;
+ 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);
*ep++ = sign;
- ep = Format02d(ep, hours);
+ ep = Format02d(ep, offset_hours);
*ep++ = ':';
- ep = Format02d(ep, minutes);
+ ep = Format02d(ep, offset_minutes);
*ep++ = ':';
- ep = Format02d(ep, seconds);
+ ep = Format02d(ep, offset_seconds);
*ep++ = '\0';
assert(ep == buf + sizeof(buf));
return buf;
@@ -116,7 +116,7 @@ std::string FixedOffsetToName(const seconds& offset) {
std::string FixedOffsetToAbbr(const seconds& offset) {
std::string abbr = FixedOffsetToName(offset);
- const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
+ const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99
abbr.erase(0, prefix_len); // +99:99:99
abbr.erase(6, 1); // +99:9999
diff --git a/src/time_zone_format.cc b/src/time_zone_format.cc
index a12367e..b573713 100644
--- a/src/time_zone_format.cc
+++ b/src/time_zone_format.cc
@@ -18,8 +18,18 @@
# endif
#endif
+#if defined(HAS_STRPTIME) && HAS_STRPTIME
+# if !defined(_XOPEN_SOURCE)
+# define _XOPEN_SOURCE // Definedness suffices for strptime.
+# endif
+#endif
+
#include "cctz/time_zone.h"
+// Include time.h directly since, by C++ standards, ctime doesn't have to
+// declare strptime.
+#include <time.h>
+
#include <cctype>
#include <chrono>
#include <cstddef>
@@ -53,6 +63,48 @@ char* strptime(const char* s, const char* fmt, std::tm* tm) {
}
#endif
+// Convert a cctz::weekday to a tm_wday value (0-6, Sunday = 0).
+int ToTmWday(weekday wd) {
+ switch (wd) {
+ case weekday::sunday:
+ return 0;
+ case weekday::monday:
+ return 1;
+ case weekday::tuesday:
+ return 2;
+ case weekday::wednesday:
+ return 3;
+ case weekday::thursday:
+ return 4;
+ case weekday::friday:
+ return 5;
+ case weekday::saturday:
+ return 6;
+ }
+ return 0; /*NOTREACHED*/
+}
+
+// Convert a tm_wday value (0-6, Sunday = 0) to a cctz::weekday.
+weekday FromTmWday(int tm_wday) {
+ switch (tm_wday) {
+ case 0:
+ return weekday::sunday;
+ case 1:
+ return weekday::monday;
+ case 2:
+ return weekday::tuesday;
+ case 3:
+ return weekday::wednesday;
+ case 4:
+ return weekday::thursday;
+ case 5:
+ return weekday::friday;
+ case 6:
+ return weekday::saturday;
+ }
+ return weekday::sunday; /*NOTREACHED*/
+}
+
std::tm ToTM(const time_zone::absolute_lookup& al) {
std::tm tm{};
tm.tm_sec = al.cs.second();
@@ -70,34 +122,19 @@ std::tm ToTM(const time_zone::absolute_lookup& al) {
tm.tm_year = static_cast<int>(al.cs.year() - 1900);
}
- switch (get_weekday(civil_day(al.cs))) {
- case weekday::sunday:
- tm.tm_wday = 0;
- break;
- case weekday::monday:
- tm.tm_wday = 1;
- break;
- case weekday::tuesday:
- tm.tm_wday = 2;
- break;
- case weekday::wednesday:
- tm.tm_wday = 3;
- break;
- case weekday::thursday:
- tm.tm_wday = 4;
- break;
- case weekday::friday:
- tm.tm_wday = 5;
- break;
- case weekday::saturday:
- tm.tm_wday = 6;
- break;
- }
- tm.tm_yday = get_yearday(civil_day(al.cs)) - 1;
+ tm.tm_wday = ToTmWday(get_weekday(al.cs));
+ tm.tm_yday = get_yearday(al.cs) - 1;
tm.tm_isdst = al.is_dst ? 1 : 0;
return tm;
}
+// Returns the week of the year [0:53] given a civil day and the day on
+// which weeks are defined to start.
+int ToWeek(const civil_day& cd, weekday week_start) {
+ const civil_day d(cd.year() % 400, cd.month(), cd.day());
+ return static_cast<int>((d - prev_weekday(civil_year(d), week_start)) / 7);
+}
+
const char kDigits[] = "0123456789";
// Formats a 64-bit integer in the given field width. Note that it is up
@@ -276,6 +313,7 @@ const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
// - %E#S - Seconds with # digits of fractional precision
// - %E*S - Seconds with full fractional precision (a literal '*')
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
+// - %ET - The RFC3339 "date-time" separator "T"
//
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
// handled internally for performance reasons. strftime(3) is slow due to
@@ -340,7 +378,7 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
if (cur == end || (cur - percent) % 2 == 0) continue;
// Simple specifiers that we handle ourselves.
- if (strchr("YmdeHMSzZs%", *cur)) {
+ if (strchr("YmdeUuWwHMSzZs%", *cur)) {
if (cur - 1 != pending) {
FormatTM(&result, std::string(pending, cur - 1), tm);
}
@@ -361,6 +399,22 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
+ case 'U':
+ bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::sunday));
+ result.append(bp, static_cast<std::size_t>(ep - bp));
+ break;
+ case 'u':
+ bp = Format64(ep, 0, tm.tm_wday ? tm.tm_wday : 7);
+ result.append(bp, static_cast<std::size_t>(ep - bp));
+ break;
+ case 'W':
+ bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::monday));
+ result.append(bp, static_cast<std::size_t>(ep - bp));
+ break;
+ case 'w':
+ bp = Format64(ep, 0, tm.tm_wday);
+ result.append(bp, static_cast<std::size_t>(ep - bp));
+ break;
case 'H':
bp = Format02d(ep, al.cs.hour());
result.append(bp, static_cast<std::size_t>(ep - bp));
@@ -434,7 +488,14 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
if (*cur != 'E' || ++cur == end) continue;
// Format our extensions.
- if (*cur == 'z') {
+ if (*cur == 'T') {
+ // Formats %ET.
+ if (cur - 2 != pending) {
+ FormatTM(&result, std::string(pending, cur - 2), tm);
+ }
+ result.append("T");
+ pending = ++cur;
+ } else if (*cur == 'z') {
// Formats %Ez.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
@@ -536,7 +597,7 @@ const char* ParseOffset(const char* dp, const char* mode, int* offset) {
} else {
dp = nullptr;
}
- } else if (first == 'Z') { // Zulu
+ } else if (first == 'Z' || first == 'z') { // Zulu
*offset = 0;
} else {
dp = nullptr;
@@ -587,12 +648,32 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
return dp;
}
+// Sets year, tm_mon and tm_mday given the year, week_num, and tm_wday,
+// and the day on which weeks are defined to start. Returns false if year
+// would need to move outside its bounds.
+bool FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) {
+ const civil_year y(*year % 400);
+ civil_day cd = prev_weekday(y, week_start); // week 0
+ cd = next_weekday(cd - 1, FromTmWday(tm->tm_wday)) + (week_num * 7);
+ if (const year_t shift = cd.year() - y.year()) {
+ if (shift > 0) {
+ if (*year > std::numeric_limits<year_t>::max() - shift) return false;
+ } else {
+ if (*year < std::numeric_limits<year_t>::min() - shift) return false;
+ }
+ *year += shift;
+ }
+ tm->tm_mon = cd.month() - 1;
+ tm->tm_mday = cd.day();
+ return true;
+}
+
} // namespace
// Uses strptime(3) to parse the given input. Supports the same extended
// format specifiers as format(), although %E#S and %E*S are treated
// identically (and similarly for %E#f and %E*f). %Ez and %E*z also accept
-// the same inputs.
+// the same inputs. %ET accepts either 'T' or 't'.
//
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
// handled internally so that we can normally avoid strptime() altogether
@@ -636,6 +717,8 @@ bool parse(const std::string& format, const std::string& input,
const char* fmt = format.c_str(); // NUL terminated
bool twelve_hour = false;
bool afternoon = false;
+ int week_num = -1;
+ weekday week_start = weekday::sunday;
bool saw_percent_s = false;
std::int_fast64_t percent_s = 0;
@@ -674,10 +757,27 @@ bool parse(const std::string& format, const std::string& input,
case 'm':
data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
if (data != nullptr) tm.tm_mon -= 1;
+ week_num = -1;
continue;
case 'd':
case 'e':
data = ParseInt(data, 2, 1, 31, &tm.tm_mday);
+ week_num = -1;
+ continue;
+ case 'U':
+ data = ParseInt(data, 0, 0, 53, &week_num);
+ week_start = weekday::sunday;
+ continue;
+ case 'W':
+ data = ParseInt(data, 0, 0, 53, &week_num);
+ week_start = weekday::monday;
+ continue;
+ case 'u':
+ data = ParseInt(data, 0, 1, 7, &tm.tm_wday);
+ if (data != nullptr) tm.tm_wday %= 7;
+ continue;
+ case 'w':
+ data = ParseInt(data, 0, 0, 6, &tm.tm_wday);
continue;
case 'H':
data = ParseInt(data, 2, 0, 23, &tm.tm_hour);
@@ -728,6 +828,15 @@ bool parse(const std::string& format, const std::string& input,
data = (*data == '%' ? data + 1 : nullptr);
continue;
case 'E':
+ if (fmt[0] == 'T') {
+ if (*data == 'T' || *data == 't') {
+ ++data;
+ ++fmt;
+ } else {
+ data = nullptr;
+ }
+ continue;
+ }
if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) {
data = ParseOffset(data, ":", &offset);
if (data != nullptr) saw_offset = true;
@@ -860,6 +969,14 @@ bool parse(const std::string& format, const std::string& input,
year += 1900;
}
+ // Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number.
+ if (week_num != -1) {
+ if (!FromWeek(week_num, week_start, &year, &tm)) {
+ if (err != nullptr) *err = "Out-of-range field";
+ return false;
+ }
+ }
+
const int month = tm.tm_mon + 1;
civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
diff --git a/src/time_zone_format_test.cc b/src/time_zone_format_test.cc
index ad611cf..41c3dda 100644
--- a/src/time_zone_format_test.cc
+++ b/src/time_zone_format_test.cc
@@ -15,6 +15,7 @@
#include "cctz/time_zone.h"
#include <chrono>
+#include <cstdint>
#include <iomanip>
#include <sstream>
#include <string>
@@ -45,8 +46,8 @@ namespace {
EXPECT_STREQ(zone, al.abbr); \
} while (0)
-const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
-const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
+const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
+const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
@@ -673,6 +674,34 @@ TEST(Format, RFC1123Format) { // locale specific
EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz));
}
+TEST(Format, Week) {
+ const time_zone utc = utc_time_zone();
+
+ auto tp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc);
+ EXPECT_EQ("2017-01-7", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2017-00-0", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc);
+ EXPECT_EQ("2017-53-7", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2017-52-0", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc);
+ EXPECT_EQ("2018-00-1", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2018-01-1", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc);
+ EXPECT_EQ("2018-52-1", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2018-53-1", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc);
+ EXPECT_EQ("2019-00-2", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2019-00-2", format("%Y-%W-%w", tp, utc));
+
+ tp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
+ EXPECT_EQ("2019-52-2", format("%Y-%U-%u", tp, utc));
+ EXPECT_EQ("2019-52-2", format("%Y-%W-%w", tp, utc));
+}
+
//
// Testing parse()
//
@@ -1101,7 +1130,7 @@ TEST(Parse, ExtendedSeconds) {
// All %E<prec>S cases are treated the same as %E*S on input.
auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15"};
- for (const std::string& prec : precisions) {
+ for (const std::string prec : precisions) {
const std::string fmt = "%E" + prec + "S";
SCOPED_TRACE(fmt);
time_point<chrono::nanoseconds> tp = unix_epoch;
@@ -1183,7 +1212,7 @@ TEST(Parse, ExtendedSubeconds) {
// All %E<prec>f cases are treated the same as %E*f on input.
auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15"};
- for (const std::string& prec : precisions) {
+ for (const std::string prec : precisions) {
const std::string fmt = "%E" + prec + "f";
SCOPED_TRACE(fmt);
time_point<chrono::nanoseconds> tp = unix_epoch - chrono::seconds(1);
@@ -1246,9 +1275,9 @@ TEST(Parse, ExtendedSubecondsScan) {
const auto expected = chrono::system_clock::from_time_t(0) +
chrono::nanoseconds(micros * 1000 + ns);
for (int ps = 0; ps < 1000; ps += 250) {
- std::ostringstream oss;
+ std::ostringstream ps_oss;
oss << std::setfill('0') << std::setw(3) << ps;
- const std::string input = nanos + oss.str() + "999";
+ const std::string input = nanos + ps_oss.str() + "999";
EXPECT_TRUE(parse("%E*f", input, tz, &tp));
EXPECT_EQ(expected + chrono::nanoseconds(ps) / 1000, tp) << input;
}
@@ -1373,10 +1402,85 @@ TEST(Parse, RFC3339Format) {
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp));
ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC");
- // Check that %Ez also accepts "Z" as a synonym for "+00:00".
+ // Check that %ET also accepts "t".
time_point<chrono::nanoseconds> tp2;
- EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp2));
+ EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12t20:21:00+00:00", tz, &tp2));
EXPECT_EQ(tp, tp2);
+
+ // Check that %Ez also accepts "Z" as a synonym for "+00:00".
+ time_point<chrono::nanoseconds> tp3;
+ EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp3));
+ EXPECT_EQ(tp, tp3);
+
+ // Check that %Ez also accepts "z" as a synonym for "+00:00".
+ time_point<chrono::nanoseconds> tp4;
+ EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00z", tz, &tp4));
+ EXPECT_EQ(tp, tp4);
+}
+
+TEST(Parse, Week) {
+ const time_zone utc = utc_time_zone();
+ time_point<cctz::seconds> tp;
+
+ auto exp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2017-01-7", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2017-00-0", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2017-53-7", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2017-52-0", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2018-00-1", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2018-01-1", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2018-52-1", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2018-53-1", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2019-00-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2019-00-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2019-52-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2019-52-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+}
+
+TEST(Parse, WeekYearShift) {
+ // %U/%W conversions with week values in {0, 52, 53} can slip
+ // into the previous/following calendar years.
+ const time_zone utc = utc_time_zone();
+ time_point<cctz::seconds> tp;
+
+ auto exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2020-00-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2020-00-2", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ exp = convert(civil_second(2021, 1, 1, 0, 0, 0), utc);
+ EXPECT_TRUE(parse("%Y-%U-%u", "2020-52-5", utc, &tp));
+ EXPECT_EQ(exp, tp);
+ EXPECT_TRUE(parse("%Y-%W-%w", "2020-52-5", utc, &tp));
+ EXPECT_EQ(exp, tp);
+
+ // Slipping into the previous/following calendar years should fail when
+ // we're already at the extremes.
+ EXPECT_FALSE(parse("%Y-%U-%u", "-9223372036854775808-0-7", utc, &tp));
+ EXPECT_FALSE(parse("%Y-%U-%u", "9223372036854775807-53-7", utc, &tp));
}
TEST(Parse, MaxRange) {
@@ -1395,7 +1499,7 @@ TEST(Parse, MaxRange) {
parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp));
EXPECT_EQ(tp, time_point<cctz::seconds>::max());
EXPECT_FALSE(
- parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp));
+ parse(RFC3339_sec, "292277026596-12-04T14:30:08-01:00", utc, &tp));
// tests the lower limit using +00:00 offset
EXPECT_TRUE(
@@ -1416,10 +1520,82 @@ TEST(Parse, MaxRange) {
utc, &tp));
EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01",
utc, &tp));
+}
+
+TEST(Parse, TimePointOverflow) {
+ const time_zone utc = utc_time_zone();
- // TODO: Add tests that parsing times with fractional seconds overflow
- // appropriately. This can't be done until cctz::parse() properly detects
- // overflow when combining the chrono seconds and femto.
+ using D = chrono::duration<std::int64_t, std::nano>;
+ time_point<D> tp;
+
+ EXPECT_TRUE(
+ 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));
+#if 0
+ // TODO(#199): Will fail until cctz::parse() properly detects overflow.
+ EXPECT_FALSE(
+ parse(RFC3339_full, "2262-04-11T23:47:16.8547758080+00:00", utc, &tp));
+ EXPECT_TRUE(
+ 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));
+ EXPECT_FALSE(
+ parse(RFC3339_full, "1677-09-21T00:12:43.1452241919+00:00", utc, &tp));
+#endif
+
+ using DS = chrono::duration<std::int8_t, chrono::seconds::period>;
+ time_point<DS> stp;
+
+ 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_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_FALSE(parse(RFC3339_full, "1969-12-31T23:57:51.9+00:00", utc, &stp));
+
+ using DM = chrono::duration<std::int8_t, chrono::minutes::period>;
+ time_point<DM> mtp;
+
+ 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_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_FALSE(parse(RFC3339_full, "1969-12-31T21:51:59+00:00", utc, &mtp));
+}
+
+TEST(Parse, TimePointOverflowFloor) {
+ const time_zone utc = utc_time_zone();
+
+ using D = chrono::duration<std::int64_t, std::micro>;
+ time_point<D> tp;
+
+ EXPECT_TRUE(
+ 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));
+#if 0
+ // TODO(#199): Will fail until cctz::parse() properly detects overflow.
+ EXPECT_FALSE(
+ parse(RFC3339_full, "294247-01-10T04:00:54.7758080+00:00", utc, &tp));
+ EXPECT_TRUE(
+ 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));
+ EXPECT_FALSE(
+ parse(RFC3339_full, "-290308-12-21T19:59:05.2241919+00:00", utc, &tp));
+#endif
}
//
diff --git a/src/time_zone_if.h b/src/time_zone_if.h
index f925c6c..8e27087 100644
--- a/src/time_zone_if.h
+++ b/src/time_zone_if.h
@@ -53,7 +53,8 @@ class TimeZoneIf {
// Convert between time_point<seconds> and a count of seconds since the
// Unix epoch. We assume that the std::chrono::system_clock and the
-// Unix clock are second aligned, but not that they share an epoch.
+// Unix clock are second aligned, and that the results are representable.
+// (That is, that they share an epoch, which is required since C++20.)
inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
return (tp - std::chrono::time_point_cast<seconds>(
std::chrono::system_clock::from_time_t(0))).count();
diff --git a/src/time_zone_impl.cc b/src/time_zone_impl.cc
index 3064155..6e07750 100644
--- a/src/time_zone_impl.cc
+++ b/src/time_zone_impl.cc
@@ -14,6 +14,8 @@
#include "time_zone_impl.h"
+#include <deque>
+#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
@@ -31,7 +33,12 @@ using TimeZoneImplByName =
TimeZoneImplByName* time_zone_map = nullptr;
// Mutual exclusion for time_zone_map.
-std::mutex time_zone_mutex;
+std::mutex& TimeZoneMutex() {
+ // This mutex is intentionally "leaked" to avoid the static deinitialization
+ // order fiasco (std::mutex's destructor is not trivial on many platforms).
+ static std::mutex* time_zone_mutex = new std::mutex;
+ return *time_zone_mutex;
+}
} // namespace
@@ -40,19 +47,18 @@ time_zone time_zone::Impl::UTC() {
}
bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
- const time_zone::Impl* const utc_impl = UTCImpl();
+ const Impl* const utc_impl = UTCImpl();
- // First check for UTC (which is never a key in time_zone_map).
+ // Check for UTC (which is never a key in time_zone_map).
auto offset = seconds::zero();
if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
*tz = time_zone(utc_impl);
return true;
}
- // Then check, under a shared lock, whether the time zone has already
- // been loaded. This is the common path. TODO: Move to shared_mutex.
+ // Check whether the time zone has already been loaded.
{
- std::lock_guard<std::mutex> lock(time_zone_mutex);
+ std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map != nullptr) {
TimeZoneImplByName::const_iterator itr = time_zone_map->find(name);
if (itr != time_zone_map->end()) {
@@ -62,42 +68,40 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
}
}
- // Now check again, under an exclusive lock.
- std::lock_guard<std::mutex> lock(time_zone_mutex);
+ // Load the new time zone (outside the lock).
+ std::unique_ptr<const Impl> new_impl(new Impl(name));
+
+ // Add the new time zone to the map.
+ std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName;
const Impl*& impl = (*time_zone_map)[name];
- if (impl == nullptr) {
- // The first thread in loads the new time zone.
- Impl* new_impl = new Impl(name);
- new_impl->zone_ = TimeZoneIf::Load(new_impl->name_);
- if (new_impl->zone_ == nullptr) {
- delete new_impl; // free the nascent Impl
- impl = utc_impl; // and fallback to UTC
- } else {
- impl = new_impl; // install new time zone
- }
+ if (impl == nullptr) { // this thread won any load race
+ impl = new_impl->zone_ ? new_impl.release() : utc_impl;
}
*tz = time_zone(impl);
return impl != utc_impl;
}
void time_zone::Impl::ClearTimeZoneMapTestOnly() {
- std::lock_guard<std::mutex> lock(time_zone_mutex);
+ std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map != nullptr) {
- // Existing time_zone::Impl* entries are in the wild, so we simply
- // leak them. Future requests will result in reloading the data.
+ // Existing time_zone::Impl* entries are in the wild, so we can't delete
+ // them. Instead, we move them to a private container, where they are
+ // logically unreachable but not "leaked". Future requests will result
+ // in reloading the data.
+ static auto* cleared = new std::deque<const time_zone::Impl*>;
+ for (const auto& element : *time_zone_map) {
+ cleared->push_back(element.second);
+ }
time_zone_map->clear();
}
}
-time_zone::Impl::Impl(const std::string& name) : name_(name) {}
+time_zone::Impl::Impl(const std::string& name)
+ : name_(name), zone_(TimeZoneIf::Load(name_)) {}
const time_zone::Impl* time_zone::Impl::UTCImpl() {
- static Impl* utc_impl = [] {
- Impl* impl = new Impl("UTC");
- impl->zone_ = TimeZoneIf::Load(impl->name_); // never fails
- return impl;
- }();
+ static const Impl* utc_impl = new Impl("UTC"); // never fails
return utc_impl;
}
diff --git a/src/time_zone_info.cc b/src/time_zone_info.cc
index eb1cd8a..77a9073 100644
--- a/src/time_zone_info.cc
+++ b/src/time_zone_info.cc
@@ -39,11 +39,12 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
+#include <fstream>
#include <functional>
-#include <iostream>
#include <memory>
#include <sstream>
#include <string>
+#include <utility>
#include "cctz/civil_time.h"
#include "time_zone_fixed.h"
@@ -79,6 +80,27 @@ const std::int_least32_t kSecsPerYear[2] = {
366 * kSecsPerDay,
};
+// Convert a cctz::weekday to a POSIX TZ weekday number (0==Sun, ..., 6=Sat).
+inline int ToPosixWeekday(weekday wd) {
+ switch (wd) {
+ case weekday::sunday:
+ return 0;
+ case weekday::monday:
+ return 1;
+ case weekday::tuesday:
+ return 2;
+ case weekday::wednesday:
+ return 3;
+ case weekday::thursday:
+ return 4;
+ case weekday::friday:
+ return 5;
+ case weekday::saturday:
+ return 6;
+ }
+ return 0; /*NOTREACHED*/
+}
+
// Single-byte, unsigned numeric values are encoded directly.
inline std::uint_fast8_t Decode8(const char* cp) {
return static_cast<std::uint_fast8_t>(*cp) & 0xff;
@@ -184,15 +206,13 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
tt.is_dst = false;
tt.abbr_index = 0;
- // We temporarily add some redundant, contemporary (2013 through 2023)
+ // 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), // BIG_BANG
- 1356998400LL, // 2013-01-01T00:00:00+00:00
- 1388534400LL, // 2014-01-01T00:00:00+00:00
+ -(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
@@ -202,7 +222,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
1609459200LL, // 2021-01-01T00:00:00+00:00
1640995200LL, // 2022-01-01T00:00:00+00:00
1672531200LL, // 2023-01-01T00:00:00+00:00
- 2147483647LL, // 2^31 - 1
+ 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;
@@ -213,7 +234,7 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
default_transition_type_ = 0;
abbreviations_ = FixedOffsetToAbbr(offset);
- abbreviations_.append(1, '\0'); // add NUL
+ abbreviations_.append(1, '\0');
future_spec_.clear(); // never needed for a fixed-offset zone
extended_ = false;
@@ -237,8 +258,8 @@ bool TimeZoneInfo::Header::Build(const tzhead& tzh) {
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_ttisgmtcnt)) < 0) return false;
- ttisgmtcnt = static_cast<std::size_t>(v);
+ if ((v = Decode32(tzh.tzh_ttisutcnt)) < 0) return false;
+ ttisutcnt = static_cast<std::size_t>(v);
return true;
}
@@ -251,25 +272,10 @@ std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const {
len += 1 * charcnt; // abbreviations
len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC
len += 1 * ttisstdcnt; // UTC/local indicators
- len += 1 * ttisgmtcnt; // standard/wall indicators
+ len += 1 * ttisutcnt; // standard/wall indicators
return len;
}
-// Check that the TransitionType has the expected offset/is_dst/abbreviation.
-void TimeZoneInfo::CheckTransition(const std::string& name,
- const TransitionType& tt,
- std::int_fast32_t offset, bool is_dst,
- const std::string& abbr) const {
- if (tt.utc_offset != offset || tt.is_dst != is_dst ||
- &abbreviations_[tt.abbr_index] != abbr) {
- std::clog << name << ": Transition"
- << " offset=" << tt.utc_offset << "/"
- << (tt.is_dst ? "DST" : "STD")
- << "/abbr=" << &abbreviations_[tt.abbr_index]
- << " does not match POSIX spec '" << future_spec_ << "'\n";
- }
-}
-
// 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).
@@ -278,117 +284,108 @@ bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index,
if (tt1_index == tt2_index) return true;
const TransitionType& tt1(transition_types_[tt1_index]);
const TransitionType& tt2(transition_types_[tt2_index]);
- if (tt1.is_dst != tt2.is_dst) return false;
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,
+ std::uint_least8_t* index) {
+ std::size_t type_index = 0;
+ std::size_t abbr_index = abbreviations_.size();
+ for (; type_index != transition_types_.size(); ++type_index) {
+ const TransitionType& tt(transition_types_[type_index]);
+ const char* tt_abbr = &abbreviations_[tt.abbr_index];
+ if (tt_abbr == abbr) abbr_index = tt.abbr_index;
+ if (tt.utc_offset == utc_offset && tt.is_dst == is_dst) {
+ if (abbr_index == tt.abbr_index) break; // reuse
+ }
+ }
+ if (type_index > 255 || abbr_index > 255) {
+ // No index space (8 bits) available for a new type or abbreviation.
+ return false;
+ }
+ if (type_index == transition_types_.size()) {
+ TransitionType& tt(*transition_types_.emplace(transition_types_.end()));
+ tt.utc_offset = static_cast<std::int_least32_t>(utc_offset);
+ tt.is_dst = is_dst;
+ if (abbr_index == abbreviations_.size()) {
+ abbreviations_.append(abbr);
+ abbreviations_.append(1, '\0');
+ }
+ tt.abbr_index = static_cast<std::uint_least8_t>(abbr_index);
+ }
+ *index = static_cast<std::uint_least8_t>(type_index);
+ return true;
+}
+
// Use the POSIX-TZ-environment-variable-style string to handle times
// in years after the last transition stored in the zoneinfo data.
-void TimeZoneInfo::ExtendTransitions(const std::string& name,
- const Header& hdr) {
+bool TimeZoneInfo::ExtendTransitions() {
extended_ = false;
- bool extending = !future_spec_.empty();
+ if (future_spec_.empty()) return true; // last transition prevails
PosixTimeZone posix;
- if (extending && !ParsePosixSpec(future_spec_, &posix)) {
- std::clog << name << ": Failed to parse '" << future_spec_ << "'\n";
- extending = false;
- }
-
- if (extending && posix.dst_abbr.empty()) { // std only
- // The future specification should match the last/default transition,
- // and that means that handling the future will fall out naturally.
- std::uint_fast8_t index = default_transition_type_;
- if (hdr.timecnt != 0) index = transitions_[hdr.timecnt - 1].type_index;
- const TransitionType& tt(transition_types_[index]);
- CheckTransition(name, tt, posix.std_offset, false, posix.std_abbr);
- extending = false;
- }
-
- if (extending && hdr.timecnt < 2) {
- std::clog << name << ": Too few transitions for POSIX spec\n";
- extending = false;
- }
-
- if (!extending) {
- // Ensure that there is always a transition in the second half of the
- // time line (the BIG_BANG transition is in the first half) 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;
- }
- return; // last transition wins
+ if (!ParsePosixSpec(future_spec_, &posix)) return false;
+
+ // Find transition type for the future std specification.
+ std::uint_least8_t std_ti;
+ if (!GetTransitionType(posix.std_offset, false, posix.std_abbr, &std_ti))
+ return false;
+
+ if (posix.dst_abbr.empty()) { // std only
+ // The future specification should match the last transition, and
+ // that means that handling the future will fall out naturally.
+ return EquivTransitions(transitions_.back().type_index, std_ti);
}
+ // Find transition type for the future dst specification.
+ std::uint_least8_t dst_ti;
+ if (!GetTransitionType(posix.dst_offset, true, posix.dst_abbr, &dst_ti))
+ return false;
+
// 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.
- // zic(8) should probably do this so that we don't have to.
- // TODO: Reduce the extension by the number of compatible
- // transitions already in place.
- transitions_.reserve(hdr.timecnt + 400 * 2 + 1);
- transitions_.resize(hdr.timecnt + 400 * 2);
+ // We may need two additional transitions for the current year.
+ transitions_.reserve(transitions_.size() + 400 * 2 + 2);
extended_ = true;
- // The future specification should match the last two transitions,
- // and those transitions should have different is_dst flags. Note
- // that nothing says the UTC offset used by the is_dst transition
- // must be greater than that used by the !is_dst transition. (See
- // Europe/Dublin, for example.)
- const Transition* tr0 = &transitions_[hdr.timecnt - 1];
- const Transition* tr1 = &transitions_[hdr.timecnt - 2];
- const TransitionType* tt0 = &transition_types_[tr0->type_index];
- const TransitionType* tt1 = &transition_types_[tr1->type_index];
- const TransitionType& dst(tt0->is_dst ? *tt0 : *tt1);
- const TransitionType& std(tt0->is_dst ? *tt1 : *tt0);
- CheckTransition(name, dst, posix.dst_offset, true, posix.dst_abbr);
- CheckTransition(name, std, posix.std_offset, false, posix.std_abbr);
-
- // Add the transitions to tr1 and back to tr0 for each extra year.
- last_year_ = LocalTime(tr0->unix_time, *tt0).cs.year();
+ const Transition& last(transitions_.back());
+ const std::int_fast64_t last_time = last.unix_time;
+ const TransitionType& last_tt(transition_types_[last.type_index]);
+ last_year_ = LocalTime(last_time, last_tt).cs.year();
bool leap_year = IsLeap(last_year_);
- const civil_day jan1(last_year_, 1, 1);
- std::int_fast64_t jan1_time = civil_second(jan1) - civil_second();
- int jan1_weekday = (static_cast<int>(get_weekday(jan1)) + 1) % 7;
- Transition* tr = &transitions_[hdr.timecnt]; // next trans to fill
- if (LocalTime(tr1->unix_time, *tt1).cs.year() != last_year_) {
- // Add a single extra transition to align to a calendar year.
- transitions_.resize(transitions_.size() + 1);
- assert(tr == &transitions_[hdr.timecnt]); // no reallocation
- const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start);
- std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1);
- tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset;
- tr++->type_index = tr1->type_index;
- tr0 = &transitions_[hdr.timecnt];
- tr1 = &transitions_[hdr.timecnt - 1];
- tt0 = &transition_types_[tr0->type_index];
- tt1 = &transition_types_[tr1->type_index];
- }
- const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start);
- const PosixTransition& pt0(tt0->is_dst ? posix.dst_start : posix.dst_end);
- for (const year_t limit = last_year_ + 400; last_year_ < limit;) {
- last_year_ += 1; // an additional year of generated transitions
+ const civil_second jan1(last_year_);
+ std::int_fast64_t jan1_time = jan1 - civil_second();
+ int jan1_weekday = ToPosixWeekday(get_weekday(jan1));
+
+ 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_) {
+ 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;
+ std.unix_time = jan1_time + std_trans_off - posix.dst_offset;
+ const auto* ta = dst.unix_time < std.unix_time ? &dst : &std;
+ const auto* tb = dst.unix_time < std.unix_time ? &std : &dst;
+ if (last_time < tb->unix_time) {
+ if (last_time < ta->unix_time) transitions_.push_back(*ta);
+ transitions_.push_back(*tb);
+ }
+ if (last_year_ == limit) break;
jan1_time += kSecsPerYear[leap_year];
jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7;
- leap_year = !leap_year && IsLeap(last_year_);
- std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1);
- tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset;
- tr++->type_index = tr1->type_index;
- std::int_fast64_t tr0_offset = TransOffset(leap_year, jan1_weekday, pt0);
- tr->unix_time = jan1_time + tr0_offset - tt1->utc_offset;
- tr++->type_index = tr0->type_index;
+ leap_year = !leap_year && IsLeap(last_year_ + 1);
}
- assert(tr == &transitions_[0] + transitions_.size());
+
+ return true;
}
-bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
+bool TimeZoneInfo::Load(ZoneInfoSource* zip) {
// Read and validate the header.
tzhead tzh;
if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh))
@@ -425,7 +422,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
}
if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt)
return false;
- if (hdr.ttisgmtcnt != 0 && hdr.ttisgmtcnt != hdr.typecnt)
+ if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt)
return false;
// Read the data into a local buffer.
@@ -436,7 +433,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
const char* bp = tbuf.data();
// Decode and validate the transitions.
- transitions_.reserve(hdr.timecnt + 2); // We might add a couple.
+ 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);
@@ -457,6 +454,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
}
// 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 =
@@ -487,6 +485,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
}
// Copy all the abbreviations.
+ abbreviations_.reserve(hdr.charcnt + 10);
abbreviations_.assign(bp, hdr.charcnt);
bp += hdr.charcnt;
@@ -496,16 +495,16 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
// that isn't the case here (see "zic -p").
bp += (8 + 4) * hdr.leapcnt; // leap-time + TAI-UTC
bp += 1 * hdr.ttisstdcnt; // UTC/local indicators
- bp += 1 * hdr.ttisgmtcnt; // standard/wall 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* zip) -> int {
+ auto get_char = [](ZoneInfoSource* azip) -> int {
unsigned char ch; // all non-EOF results are positive
- return (zip->Read(&ch, 1) == 1) ? ch : EOF;
+ return (azip->Read(&ch, 1) == 1) ? ch : EOF;
};
if (get_char(zip) != '\n')
return false;
@@ -539,19 +538,29 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
transitions_.resize(hdr.timecnt);
// Ensure that there is always a transition in the first half of the
- // time line (the second half is handled in ExtendTransitions()) so that
- // the signed difference between a civil_second and the civil_second of
- // its previous transition is always representable, without overflow.
- // A contemporary zic will usually have already done this for us.
+ // 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); // see tz/zic.c "BIG_BANG"
+ tr.unix_time = -(1LL << 59); // -18267312070-10-26T17:01:52+00:00
tr.type_index = default_transition_type_;
- hdr.timecnt += 1;
}
// Extend the transitions using the future specification.
- ExtendTransitions(name, hdr);
+ 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().
@@ -583,14 +592,17 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
namespace {
+using FilePtr = std::unique_ptr<FILE, int(*)(FILE*)>;
+
// fopen(3) adaptor.
-inline FILE* FOpen(const char* path, const char* mode) {
+inline FilePtr FOpen(const char* path, const char* mode) {
#if defined(_MSC_VER)
FILE* fp;
if (fopen_s(&fp, path, mode) != 0) fp = nullptr;
- return fp;
+ return FilePtr(fp, fclose);
#else
- return fopen(path, mode); // TODO: Enable the close-on-exec flag.
+ // TODO: Enable the close-on-exec flag.
+ return FilePtr(fopen(path, mode), fclose);
#endif
}
@@ -618,22 +630,22 @@ class FileZoneInfoSource : public ZoneInfoSource {
protected:
explicit FileZoneInfoSource(
- FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max())
- : fp_(fp, fclose), len_(len) {}
+ FilePtr fp, std::size_t len = std::numeric_limits<std::size_t>::max())
+ : fp_(std::move(fp)), len_(len) {}
private:
- std::unique_ptr<FILE, int(*)(FILE*)> fp_;
+ FilePtr fp_;
std::size_t len_;
};
std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
const std::string& name) {
// Use of the "file:" prefix is intended for testing purposes only.
- if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5));
+ const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
// Map the time-zone name to a path name.
std::string path;
- if (name.empty() || name[0] != '/') {
+ if (pos == name.size() || name[pos] != '/') {
const char* tzdir = "/usr/share/zoneinfo";
char* tzdir_env = nullptr;
#if defined(_MSC_VER)
@@ -648,20 +660,12 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
free(tzdir_env);
#endif
}
- path += name;
+ path.append(name, pos, std::string::npos);
// Open the zoneinfo file.
- FILE* fp = FOpen(path.c_str(), "rb");
+ auto fp = FOpen(path.c_str(), "rb");
if (fp == nullptr) return nullptr;
- std::size_t length = 0;
- if (fseek(fp, 0, SEEK_END) == 0) {
- long pos = ftell(fp);
- if (pos >= 0) {
- length = static_cast<std::size_t>(pos);
- }
- rewind(fp);
- }
- return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length));
+ return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(std::move(fp)));
}
class AndroidZoneInfoSource : public FileZoneInfoSource {
@@ -670,22 +674,22 @@ class AndroidZoneInfoSource : public FileZoneInfoSource {
std::string Version() const override { return version_; }
private:
- explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers)
- : FileZoneInfoSource(fp, len), version_(vers) {}
+ explicit AndroidZoneInfoSource(FilePtr fp, std::size_t len,
+ std::string version)
+ : FileZoneInfoSource(std::move(fp), len), version_(std::move(version)) {}
std::string version_;
};
std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
const std::string& name) {
// Use of the "file:" prefix is intended for testing purposes only.
- if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5));
+ const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
-#if defined(__ANDROID__)
// See Android's libc/tzcode/bionic.cpp for additional information.
for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
"/system/usr/share/zoneinfo/tzdata"}) {
- std::unique_ptr<FILE, int (*)(FILE*)> fp(FOpen(tzdata, "rb"), fclose);
- if (fp.get() == nullptr) continue;
+ auto fp = FOpen(tzdata, "rb");
+ if (fp == nullptr) continue;
char hbuf[24]; // covers header.zonetab_offset too
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
@@ -708,14 +712,76 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
const std::int_fast32_t length = Decode32(ebuf + 44);
if (start < 0 || length < 0) break;
ebuf[40] = '\0'; // ensure zone name is NUL terminated
- if (strcmp(name.c_str(), ebuf) == 0) {
+ if (strcmp(name.c_str() + pos, ebuf) == 0) {
if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
- fp.release(), static_cast<std::size_t>(length), vers));
+ std::move(fp), static_cast<std::size_t>(length), vers));
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+// A zoneinfo source for use inside Fuchsia components. This attempts to
+// read zoneinfo files from one of several known paths in a component's
+// incoming namespace. [Config data][1] is preferred, but package-specific
+// resources are also supported.
+//
+// Fuchsia's implementation supports `FileZoneInfoSource::Version()`.
+//
+// [1]: https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component
+class FuchsiaZoneInfoSource : public FileZoneInfoSource {
+ public:
+ static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
+ std::string Version() const override { return version_; }
+
+ private:
+ explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version)
+ : FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {}
+ std::string version_;
+};
+
+std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open(
+ const std::string& name) {
+ // Use of the "file:" prefix is intended for testing purposes only.
+ const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
+
+ // Prefixes where a Fuchsia component might find zoneinfo files,
+ // in descending order of preference.
+ const auto kTzdataPrefixes = {
+ "/config/data/tzdata/",
+ "/pkg/data/tzdata/",
+ "/data/tzdata/",
+ };
+ const auto kEmptyPrefix = {""};
+ const bool name_absolute = (pos != name.size() && name[pos] == '/');
+ const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes;
+
+ // Fuchsia builds place zoneinfo files at "<prefix><format><name>".
+ for (const std::string prefix : prefixes) {
+ std::string path = prefix;
+ if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format
+ path.append(name, pos, std::string::npos);
+
+ auto fp = FOpen(path.c_str(), "rb");
+ if (fp == nullptr) continue;
+
+ std::string version;
+ if (!prefix.empty()) {
+ // Fuchsia builds place the version in "<prefix>revision.txt".
+ std::ifstream version_stream(prefix + "revision.txt");
+ if (version_stream.is_open()) {
+ // revision.txt should contain no newlines, but to be
+ // defensive we read just the first line.
+ std::getline(version_stream, version);
}
}
+
+ return std::unique_ptr<ZoneInfoSource>(
+ new FuchsiaZoneInfoSource(std::move(fp), std::move(version)));
}
-#endif // __ANDROID__
+
return nullptr;
}
@@ -733,12 +799,13 @@ bool TimeZoneInfo::Load(const std::string& name) {
// Find and use a ZoneInfoSource to load the named zone.
auto zip = cctz_extension::zone_info_source_factory(
- name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> {
- if (auto zip = FileZoneInfoSource::Open(name)) return zip;
- if (auto zip = AndroidZoneInfoSource::Open(name)) return zip;
+ name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> {
+ if (auto z = FileZoneInfoSource::Open(n)) return z;
+ if (auto z = AndroidZoneInfoSource::Open(n)) return z;
+ if (auto z = FuchsiaZoneInfoSource::Open(n)) return z;
return nullptr;
});
- return zip != nullptr && Load(name, zip.get());
+ return zip != nullptr && Load(zip.get());
}
// BreakTime() translation for a particular transition type.
@@ -914,8 +981,8 @@ bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size();
if (begin->unix_time <= -(1LL << 59)) {
- // Do not report the BIG_BANG found in recent zoneinfo data as it is
- // really a sentinel, not a transition. See tz/zic.c.
+ // Do not report the BIG_BANG found in some zoneinfo data as it is
+ // really a sentinel, not a transition. See pre-2018f tz/zic.c.
++begin;
}
std::int_fast64_t unix_time = ToUnixSeconds(tp);
@@ -940,8 +1007,8 @@ bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size();
if (begin->unix_time <= -(1LL << 59)) {
- // Do not report the BIG_BANG found in recent zoneinfo data as it is
- // really a sentinel, not a transition. See tz/zic.c.
+ // Do not report the BIG_BANG found in some zoneinfo data as it is
+ // really a sentinel, not a transition. See pre-2018f tz/zic.c.
++begin;
}
std::int_fast64_t unix_time = ToUnixSeconds(tp);
diff --git a/src/time_zone_info.h b/src/time_zone_info.h
index 4657a2d..431dc5b 100644
--- a/src/time_zone_info.h
+++ b/src/time_zone_info.h
@@ -86,21 +86,20 @@ class TimeZoneInfo : public TimeZoneIf {
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 ttisgmtcnt; // standard/wall 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;
};
- void CheckTransition(const std::string& name, const TransitionType& tt,
- std::int_fast32_t offset, bool is_dst,
- const std::string& abbr) const;
+ bool GetTransitionType(std::int_fast32_t utc_offset, bool is_dst,
+ const std::string& abbr, std::uint_least8_t* index);
bool EquivTransitions(std::uint_fast8_t tt1_index,
std::uint_fast8_t tt2_index) const;
- void ExtendTransitions(const std::string& name, const Header& hdr);
+ bool ExtendTransitions();
bool ResetToBuiltinUTC(const seconds& offset);
- bool Load(const std::string& name, ZoneInfoSource* zip);
+ bool Load(ZoneInfoSource* zip);
// Helpers for BreakTime() and MakeTime().
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
diff --git a/src/time_zone_libc.cc b/src/time_zone_libc.cc
index da89fef..f185b95 100644
--- a/src/time_zone_libc.cc
+++ b/src/time_zone_libc.cc
@@ -21,67 +21,90 @@
#include <chrono>
#include <ctime>
#include <limits>
-#include <tuple>
#include <utility>
#include "cctz/civil_time.h"
#include "cctz/time_zone.h"
+#if defined(_AIX)
+extern "C" {
+ extern long altzone;
+}
+#endif
+
namespace cctz {
namespace {
-// .first is seconds east of UTC; .second is the time-zone abbreviation.
-using OffsetAbbr = std::pair<int, const char*>;
-
-// Defines a function that can be called as follows:
-//
-// std::tm tm = ...;
-// OffsetAbbr off_abbr = get_offset_abbr(tm);
-//
#if defined(_WIN32) || defined(_WIN64)
// Uses the globals: '_timezone', '_dstbias' and '_tzname'.
-OffsetAbbr get_offset_abbr(const std::tm& tm) {
+auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) {
const bool is_dst = tm.tm_isdst > 0;
- const int off = _timezone + (is_dst ? _dstbias : 0);
- const char* abbr = _tzname[is_dst];
- return {off, abbr};
+ return _timezone + (is_dst ? _dstbias : 0);
}
-#elif defined(__sun)
+auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) {
+ const bool is_dst = tm.tm_isdst > 0;
+ return _tzname[is_dst];
+}
+#elif defined(__sun) || defined(_AIX)
// Uses the globals: 'timezone', 'altzone' and 'tzname'.
-OffsetAbbr get_offset_abbr(const std::tm& tm) {
+auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
+ const bool is_dst = tm.tm_isdst > 0;
+ return is_dst ? altzone : timezone;
+}
+auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
const bool is_dst = tm.tm_isdst > 0;
- const int off = is_dst ? altzone : timezone;
- const char* abbr = tzname[is_dst];
- return {off, abbr};
+ return tzname[is_dst];
}
#elif defined(__native_client__) || defined(__myriad2__) || \
defined(__EMSCRIPTEN__)
// Uses the globals: 'timezone' and 'tzname'.
-OffsetAbbr get_offset_abbr(const std::tm& tm) {
+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;
- const int off = _timezone + (is_dst ? 60 * 60 : 0);
- const char* abbr = tzname[is_dst];
- return {off, abbr};
+ return tzname[is_dst];
+}
+#else
+// Adapt to different spellings of the struct std::tm extension fields.
+#if defined(tm_gmtoff)
+auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) {
+ return tm.tm_gmtoff;
+}
+#elif defined(__tm_gmtoff)
+auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) {
+ return tm.__tm_gmtoff;
+}
+#else
+template <typename T>
+auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
+ return tm.tm_gmtoff;
+}
+template <typename T>
+auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
+ return tm.__tm_gmtoff;
+}
+#endif // tm_gmtoff
+#if defined(tm_zone)
+auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) {
+ return tm.tm_zone;
+}
+#elif defined(__tm_zone)
+auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
+ return tm.__tm_zone;
}
#else
-//
-// Returns an OffsetAbbr using std::tm fields with various spellings.
-//
-#if !defined(tm_gmtoff) && !defined(tm_zone)
template <typename T>
-OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::tm_gmtoff) = nullptr,
- decltype(&T::tm_zone) = nullptr) {
- return {tm.tm_gmtoff, tm.tm_zone};
+auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
+ return tm.tm_zone;
}
-#endif // !defined(tm_gmtoff) && !defined(tm_zone)
-#if !defined(__tm_gmtoff) && !defined(__tm_zone)
template <typename T>
-OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::__tm_gmtoff) = nullptr,
- decltype(&T::__tm_zone) = nullptr) {
- return {tm.__tm_gmtoff, tm.__tm_zone};
+auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
+ return tm.__tm_zone;
}
-#endif // !defined(__tm_gmtoff) && !defined(__tm_zone)
+#endif // tm_zone
#endif
inline std::tm* gm_time(const std::time_t *timep, std::tm *result) {
@@ -124,7 +147,7 @@ bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
return false;
}
}
- *off = get_offset_abbr(tm).first;
+ *off = static_cast<int>(tm_gmtoff(tm));
return true;
}
@@ -134,8 +157,9 @@ std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
std::tm tm;
while (lo + 1 != hi) {
const std::time_t mid = lo + (hi - lo) / 2;
- if (std::tm* tmp = local_time(&mid, &tm)) {
- if (get_offset_abbr(*tmp).first == offset) {
+ std::tm* tmp = local_time(&mid, &tm);
+ if (tmp != nullptr) {
+ if (tm_gmtoff(*tmp) == offset) {
hi = mid;
} else {
lo = mid;
@@ -144,8 +168,9 @@ std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
// If std::tm cannot hold some result we resort to a linear search,
// ignoring all failed conversions. Slow, but never really happens.
while (++lo != hi) {
- if (std::tm* tmp = local_time(&lo, &tm)) {
- if (get_offset_abbr(*tmp).first == offset) break;
+ tmp = local_time(&lo, &tm);
+ if (tmp != nullptr) {
+ if (tm_gmtoff(*tmp) == offset) break;
}
}
return lo;
@@ -191,8 +216,8 @@ time_zone::absolute_lookup TimeZoneLibC::BreakTime(
const year_t year = tmp->tm_year + year_t{1900};
al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday,
tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
- std::tie(al.offset, al.abbr) = get_offset_abbr(*tmp);
- if (!local_) al.abbr = "UTC"; // as expected by cctz
+ al.offset = static_cast<int>(tm_gmtoff(*tmp));
+ al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz
al.is_dst = tmp->tm_isdst > 0;
return al;
}
diff --git a/src/time_zone_lookup.cc b/src/time_zone_lookup.cc
index 2a38a9b..d0f367c 100644
--- a/src/time_zone_lookup.cc
+++ b/src/time_zone_lookup.cc
@@ -16,7 +16,7 @@
#if defined(__ANDROID__)
#include <sys/system_properties.h>
-#if __ANDROID_API__ >= 21
+#if defined(__ANDROID_API__) && __ANDROID_API__ >= 21
#include <dlfcn.h>
#endif
#endif
@@ -26,6 +26,13 @@
#include <vector>
#endif
+#if defined(__Fuchsia__)
+#include <fuchsia/intl/cpp/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/sys/cpp/component_context.h>
+#include <zircon/types.h>
+#endif
+
#include <cstdlib>
#include <cstring>
#include <string>
@@ -35,7 +42,7 @@
namespace cctz {
-#if defined(__ANDROID__) && __ANDROID_API__ >= 21
+#if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21
namespace {
// 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.
@@ -139,6 +146,48 @@ time_zone local_time_zone() {
}
CFRelease(tz_default);
#endif
+#if defined(__Fuchsia__)
+ std::string primary_tz;
+ [&]() {
+ // Note: We can't use the synchronous FIDL API here because it doesn't
+ // allow timeouts; if the FIDL call failed, local_time_zone() would never
+ // return.
+
+ const zx::duration kTimeout = zx::msec(500);
+
+ // Don't attach to the thread because otherwise the thread's dispatcher
+ // would be set to null when the loop is destroyed, causing any other FIDL
+ // code running on the same thread to crash.
+ async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
+ std::unique_ptr<sys::ComponentContext> context =
+ sys::ComponentContext::Create();
+
+ fuchsia::intl::PropertyProviderHandle handle;
+ zx_status_t status = context->svc()->Connect(handle.NewRequest());
+ if (status != ZX_OK) {
+ return;
+ }
+
+ fuchsia::intl::PropertyProviderPtr intl_provider;
+ status = intl_provider.Bind(std::move(handle), loop.dispatcher());
+ if (status != ZX_OK) {
+ return;
+ }
+
+ intl_provider->GetProfile(
+ [&loop, &primary_tz](fuchsia::intl::Profile profile) {
+ if (!profile.time_zones().empty()) {
+ primary_tz = profile.time_zones()[0].id;
+ }
+ loop.Quit();
+ });
+ loop.Run(zx::deadline_after(kTimeout));
+ }();
+
+ if (!primary_tz.empty()) {
+ zone = primary_tz.c_str();
+ }
+#endif
// Allow ${TZ} to override to default zone.
char* tz_env = nullptr;
diff --git a/src/time_zone_lookup_test.cc b/src/time_zone_lookup_test.cc
index 14e3f66..772e817 100644
--- a/src/time_zone_lookup_test.cc
+++ b/src/time_zone_lookup_test.cc
@@ -209,6 +209,7 @@ const char* const kTimeZoneNames[] = {
"America/North_Dakota/Beulah",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
+ "America/Nuuk",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
@@ -576,6 +577,7 @@ const char* const kTimeZoneNames[] = {
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Johnston",
+ "Pacific/Kanton",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
@@ -713,6 +715,18 @@ TEST(TimeZones, LoadZonesConcurrently) {
EXPECT_LE(failures.size(), max_failures) << testing::PrintToString(failures);
}
+TEST(TimeZone, UTC) {
+ const time_zone utc = utc_time_zone();
+
+ time_zone loaded_utc;
+ EXPECT_TRUE(load_time_zone("UTC", &loaded_utc));
+ EXPECT_EQ(loaded_utc, utc);
+
+ time_zone loaded_utc0;
+ EXPECT_TRUE(load_time_zone("UTC0", &loaded_utc0));
+ EXPECT_EQ(loaded_utc0, utc);
+}
+
TEST(TimeZone, NamedTimeZones) {
const time_zone utc = utc_time_zone();
EXPECT_EQ("UTC", utc.name());
@@ -832,7 +846,7 @@ TEST(BreakTime, LocalTimeInUTC) {
const time_zone tz = utc_time_zone();
const auto tp = chrono::system_clock::from_time_t(0);
ExpectTime(tp, tz, 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz)));
}
TEST(BreakTime, LocalTimeInUTCUnaligned) {
@@ -840,7 +854,7 @@ TEST(BreakTime, LocalTimeInUTCUnaligned) {
const auto tp =
chrono::system_clock::from_time_t(0) - chrono::milliseconds(500);
ExpectTime(tp, tz, 1969, 12, 31, 23, 59, 59, 0, false, "UTC");
- EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz)));
}
TEST(BreakTime, LocalTimePosix) {
@@ -848,7 +862,7 @@ TEST(BreakTime, LocalTimePosix) {
const time_zone tz = utc_time_zone();
const auto tp = chrono::system_clock::from_time_t(536457599);
ExpectTime(tp, tz, 1986, 12, 31, 23, 59, 59, 0, false, "UTC");
- EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz)));
}
TEST(TimeZoneImpl, LocalTimeInFixed) {
@@ -858,28 +872,28 @@ TEST(TimeZoneImpl, LocalTimeInFixed) {
const auto tp = chrono::system_clock::from_time_t(0);
ExpectTime(tp, tz, 1969, 12, 31, 15, 26, 13, offset.count(), false,
"-083347");
- EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz)));
}
TEST(BreakTime, LocalTimeInNewYork) {
const time_zone tz = LoadZone("America/New_York");
const auto tp = chrono::system_clock::from_time_t(45);
ExpectTime(tp, tz, 1969, 12, 31, 19, 0, 45, -5 * 60 * 60, false, "EST");
- EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz)));
}
TEST(BreakTime, LocalTimeInMTV) {
const time_zone tz = LoadZone("America/Los_Angeles");
const auto tp = chrono::system_clock::from_time_t(1380855729);
ExpectTime(tp, tz, 2013, 10, 3, 20, 2, 9, -7 * 60 * 60, true, "PDT");
- EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz)));
}
TEST(BreakTime, LocalTimeInSydney) {
const time_zone tz = LoadZone("Australia/Sydney");
const auto tp = chrono::system_clock::from_time_t(90);
ExpectTime(tp, tz, 1970, 1, 1, 10, 1, 30, 10 * 60 * 60, false, "AEST");
- EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz)));
}
TEST(MakeTime, TimePointResolution) {
@@ -928,7 +942,7 @@ TEST(MakeTime, Normalization) {
// NOTE: Run this with -ftrapv to detect overflow problems.
TEST(MakeTime, SysSecondsLimits) {
- const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez";
+ const char RFC3339[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
const time_zone utc = utc_time_zone();
const time_zone east = fixed_time_zone(chrono::hours(14));
const time_zone west = fixed_time_zone(-chrono::hours(14));
@@ -999,13 +1013,21 @@ TEST(MakeTime, SysSecondsLimits) {
#if defined(_WIN32) || defined(_WIN64)
// localtime_s() and gmtime_s() don't believe in years outside [1970:3000].
#else
- const time_zone utc = LoadZone("libc:UTC");
+ const 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), utc);
- EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, utc));
+ tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut);
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+ // The BSD gmtime_r() fails on extreme positive tm_year values.
+#else
+ EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, cut));
+#endif
const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
- tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), utc);
- EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, utc));
+ 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).
+#else
+ EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
+#endif
#endif
}
}
@@ -1024,17 +1046,17 @@ TEST(MakeTime, LocalTimeLibC) {
ASSERT_EQ(0, setenv("TZ", *np, 1)); // change what "localtime" means
const auto zi = local_time_zone();
const auto lc = LoadZone("libc:localtime");
- time_zone::civil_transition trans;
+ time_zone::civil_transition transition;
for (auto tp = zi.lookup(civil_second()).trans;
- zi.next_transition(tp, &trans);
- tp = zi.lookup(trans.to).trans) {
- const auto fcl = zi.lookup(trans.from);
- const auto tcl = zi.lookup(trans.to);
+ zi.next_transition(tp, &transition);
+ tp = zi.lookup(transition.to).trans) {
+ const auto fcl = zi.lookup(transition.from);
+ const auto tcl = zi.lookup(transition.to);
civil_second cs; // compare cs in zi and lc
if (fcl.kind == time_zone::civil_lookup::UNIQUE) {
if (tcl.kind == time_zone::civil_lookup::UNIQUE) {
// Both unique; must be an is_dst or abbr change.
- ASSERT_EQ(trans.from, trans.to);
+ ASSERT_EQ(transition.from, transition.to);
const auto trans = fcl.trans;
const auto tal = zi.lookup(trans);
const auto tprev = trans - cctz::seconds(1);
@@ -1045,11 +1067,11 @@ TEST(MakeTime, LocalTimeLibC) {
continue;
}
ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind);
- cs = trans.to;
+ cs = transition.to;
} else {
ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind);
ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind);
- cs = trans.from;
+ cs = transition.from;
}
if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t)
const auto cl_zi = zi.lookup(cs);
@@ -1156,6 +1178,44 @@ TEST(PrevTransition, AmericaNewYork) {
// We have a transition but we don't know which one.
}
+TEST(NextTransition, Scan) {
+ for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) {
+ time_zone tz;
+ if (!load_time_zone(*np, &tz)) {
+ continue; // tolerate kTimeZoneNames/zoneinfo skew
+ }
+ SCOPED_TRACE(testing::Message() << "In " << *np);
+
+ auto tp = time_point<cctz::seconds>::min();
+ time_zone::civil_transition trans;
+ while (tz.next_transition(tp, &trans)) {
+ time_zone::civil_lookup from_cl = tz.lookup(trans.from);
+ EXPECT_NE(from_cl.kind, time_zone::civil_lookup::REPEATED);
+ time_zone::civil_lookup to_cl = tz.lookup(trans.to);
+ EXPECT_NE(to_cl.kind, time_zone::civil_lookup::SKIPPED);
+
+ auto trans_tp = to_cl.trans;
+ time_zone::absolute_lookup trans_al = tz.lookup(trans_tp);
+ EXPECT_EQ(trans_al.cs, trans.to);
+ auto pre_trans_tp = trans_tp - cctz::seconds(1);
+ time_zone::absolute_lookup pre_trans_al = tz.lookup(pre_trans_tp);
+ EXPECT_EQ(pre_trans_al.cs + 1, trans.from);
+
+ auto offset_delta = trans_al.offset - pre_trans_al.offset;
+ EXPECT_EQ(offset_delta, trans.to - trans.from);
+ if (offset_delta == 0) {
+ // This "transition" is only an is_dst or abbr change.
+ EXPECT_EQ(to_cl.kind, time_zone::civil_lookup::UNIQUE);
+ if (trans_al.is_dst == pre_trans_al.is_dst) {
+ EXPECT_STRNE(trans_al.abbr, pre_trans_al.abbr);
+ }
+ }
+
+ tp = trans_tp; // continue scan from transition
+ }
+ }
+}
+
TEST(TimeZoneEdgeCase, AmericaNewYork) {
const time_zone tz = LoadZone("America/New_York");
@@ -1270,10 +1330,10 @@ TEST(TimeZoneEdgeCase, PacificApia) {
// 1325239200 == Sat, 31 Dec 2011 00:00:00 +1400 (+14)
auto tp = convert(civil_second(2011, 12, 29, 23, 59, 59), tz);
ExpectTime(tp, tz, 2011, 12, 29, 23, 59, 59, -10 * 3600, true, "-10");
- EXPECT_EQ(363, get_yearday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(363, get_yearday(convert(tp, tz)));
tp += cctz::seconds(1);
ExpectTime(tp, tz, 2011, 12, 31, 0, 0, 0, 14 * 3600, true, "+14");
- EXPECT_EQ(365, get_yearday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(365, get_yearday(convert(tp, tz)));
}
TEST(TimeZoneEdgeCase, AfricaCairo) {
@@ -1395,10 +1455,10 @@ TEST(TimeZoneEdgeCase, NegativeYear) {
const time_zone tz = utc_time_zone();
auto tp = convert(civil_second(0, 1, 1, 0, 0, 0), tz);
ExpectTime(tp, tz, 0, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC");
- EXPECT_EQ(weekday::saturday, get_weekday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(weekday::saturday, get_weekday(convert(tp, tz)));
tp -= cctz::seconds(1);
ExpectTime(tp, tz, -1, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC");
- EXPECT_EQ(weekday::friday, get_weekday(civil_day(convert(tp, tz))));
+ EXPECT_EQ(weekday::friday, get_weekday(convert(tp, tz)));
}
TEST(TimeZoneEdgeCase, UTC32bitLimit) {
diff --git a/src/time_zone_posix.cc b/src/time_zone_posix.cc
index 1415cb6..847db17 100644
--- a/src/time_zone_posix.cc
+++ b/src/time_zone_posix.cc
@@ -98,9 +98,9 @@ const char* ParseDateTime(const char* p, PosixTransition* res) {
int weekday = 0;
if ((p = ParseInt(p + 1, 0, 6, &weekday)) != nullptr) {
res->date.fmt = PosixTransition::M;
- res->date.m.month = static_cast<int_fast8_t>(month);
- res->date.m.week = static_cast<int_fast8_t>(week);
- res->date.m.weekday = static_cast<int_fast8_t>(weekday);
+ res->date.m.month = static_cast<std::int_fast8_t>(month);
+ res->date.m.week = static_cast<std::int_fast8_t>(week);
+ res->date.m.weekday = static_cast<std::int_fast8_t>(weekday);
}
}
}
@@ -108,13 +108,13 @@ const char* ParseDateTime(const char* p, PosixTransition* res) {
int day = 0;
if ((p = ParseInt(p + 1, 1, 365, &day)) != nullptr) {
res->date.fmt = PosixTransition::J;
- res->date.j.day = static_cast<int_fast16_t>(day);
+ res->date.j.day = static_cast<std::int_fast16_t>(day);
}
} else {
int day = 0;
if ((p = ParseInt(p, 0, 365, &day)) != nullptr) {
res->date.fmt = PosixTransition::N;
- res->date.j.day = static_cast<int_fast16_t>(day);
+ res->date.n.day = static_cast<std::int_fast16_t>(day);
}
}
}
diff --git a/src/tzfile.h b/src/tzfile.h
index 446c8d5..9c3ea4e 100644
--- a/src/tzfile.h
+++ b/src/tzfile.h
@@ -44,9 +44,9 @@
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
+ char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */
char tzh_reserved[15]; /* reserved; must be zero */
- char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
char tzh_leapcnt[4]; /* coded number of leap seconds */
char tzh_timecnt[4]; /* coded number of transition times */
@@ -69,14 +69,15 @@ struct tzhead {
** one (char [4]) total correction after above
** tzh_ttisstdcnt (char)s indexed by type; if 1, transition
** time is standard time, if 0,
-** transition time is wall clock time
-** if absent, transition times are
-** assumed to be wall clock time
-** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition
-** time is UT, if 0,
-** transition time is local time
-** if absent, transition times are
+** transition time is local (wall clock)
+** time; if absent, transition times are
** assumed to be local time
+** tzh_ttisutcnt (char)s indexed by type; if 1, transition
+** time is UT, if 0, transition time is
+** local time; if absent, transition
+** times are assumed to be local time.
+** When this is 1, the corresponding
+** std/wall indicator must also be 1.
*/
/*
diff --git a/src/zone_info_source.cc b/src/zone_info_source.cc
index 9012fe4..e061f38 100644
--- a/src/zone_info_source.cc
+++ b/src/zone_info_source.cc
@@ -40,11 +40,20 @@ std::unique_ptr<cctz::ZoneInfoSource> DefaultFactory(
// A "weak" definition for cctz_extension::zone_info_source_factory.
// The user may override this with their own "strong" definition (see
// zone_info_source.h).
-#if defined(_MSC_VER)
+#if !defined(__has_attribute)
+#define __has_attribute(x) 0
+#endif
+// MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the
+// Windows linker cannot handle that. Nor does the MinGW compiler know how to
+// pass "#pragma comment(linker, ...)" to the Windows linker.
+#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__)
+ZoneInfoSourceFactory zone_info_source_factory
+ __attribute__((weak)) = DefaultFactory;
+#elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION)
extern ZoneInfoSourceFactory zone_info_source_factory;
extern ZoneInfoSourceFactory default_factory;
ZoneInfoSourceFactory default_factory = DefaultFactory;
-#if defined(_M_IX86)
+#if defined(_M_IX86) || defined(_M_ARM)
#pragma comment( \
linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@3@@ZA=?default_factory@cctz_extension@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@3@@ZA")
@@ -54,18 +63,10 @@ ZoneInfoSourceFactory default_factory = DefaultFactory;
"/alternatename:?zone_info_source_factory@cctz_extension@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@3@@ZEA=?default_factory@cctz_extension@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@3@@ZEA")
#else
#error Unsupported MSVC platform
-#endif
-#else // _MSC_VER
-#if !defined(__has_attribute)
-#define __has_attribute(x) 0
-#endif
-#if __has_attribute(weak) || defined(__GNUC__)
-ZoneInfoSourceFactory zone_info_source_factory
- __attribute__((weak)) = DefaultFactory;
+#endif // _M_<PLATFORM>
#else
// Make it a "strong" definition if we have no other choice.
ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory;
#endif
-#endif // _MSC_VER
} // namespace cctz_extension
diff --git a/testdata/README.zoneinfo b/testdata/README.zoneinfo
new file mode 100644
index 0000000..a41c7b8
--- /dev/null
+++ b/testdata/README.zoneinfo
@@ -0,0 +1,42 @@
+testdata/zoneinfo contains time-zone data files that may be used with CCTZ.
+Install them in a location referenced by the ${TZDIR} environment variable.
+Symbolic and hard links have been eliminated for portability.
+
+On Linux systems the distribution's versions of these files can probably
+already be found in the default ${TZDIR} location, /usr/share/zoneinfo.
+
+New versions can be generated using the following shell script.
+
+ #!/bin/sh -
+ set -e
+ DESTDIR=$(mktemp -d)
+ trap "rm -fr ${DESTDIR}" 0 2 15
+ (
+ cd ${DESTDIR}
+ if [ -n "${USE_GLOBAL_TZ}" ]
+ then
+ git clone -b global-tz https://github.com/JodaOrg/global-tz.git tz
+ else
+ git clone https://github.com/eggert/tz.git
+ fi
+ make --directory=tz \
+ install DESTDIR=${DESTDIR} \
+ DATAFORM=vanguard \
+ TZDIR=/zoneinfo \
+ REDO=posix_only \
+ LOCALTIME=Factory \
+ TZDATA_TEXT= \
+ ZONETABLES=zone1970.tab
+ tar --create --dereference --hard-dereference --file tzfile.tar \
+ --directory=tz tzfile.h
+ tar --create --dereference --hard-dereference --file zoneinfo.tar \
+ --exclude=zoneinfo/posixrules zoneinfo \
+ --directory=tz version
+ )
+ tar --extract --directory src --file ${DESTDIR}/tzfile.tar
+ tar --extract --directory testdata --file ${DESTDIR}/zoneinfo.tar
+ exit 0
+
+To run the CCTZ tests using the testdata/zoneinfo files, execute:
+
+ bazel test --test_env=TZDIR=${PWD}/testdata/zoneinfo ...
diff --git a/testdata/version b/testdata/version
new file mode 100644
index 0000000..ca002de
--- /dev/null
+++ b/testdata/version
@@ -0,0 +1 @@
+2022a
diff --git a/testdata/zoneinfo/Africa/Abidjan b/testdata/zoneinfo/Africa/Abidjan
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Abidjan differ
diff --git a/testdata/zoneinfo/Africa/Accra b/testdata/zoneinfo/Africa/Accra
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Accra differ
diff --git a/testdata/zoneinfo/Africa/Addis_Ababa b/testdata/zoneinfo/Africa/Addis_Ababa
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Africa/Addis_Ababa differ
diff --git a/testdata/zoneinfo/Africa/Algiers b/testdata/zoneinfo/Africa/Algiers
new file mode 100644
index 0000000..56a4dd2
Binary files /dev/null and b/testdata/zoneinfo/Africa/Algiers differ
diff --git a/testdata/zoneinfo/Africa/Asmara b/testdata/zoneinfo/Africa/Asmara
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Africa/Asmara differ
diff --git a/testdata/zoneinfo/Africa/Asmera b/testdata/zoneinfo/Africa/Asmera
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Africa/Asmera differ
diff --git a/testdata/zoneinfo/Africa/Bamako b/testdata/zoneinfo/Africa/Bamako
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Bamako differ
diff --git a/testdata/zoneinfo/Africa/Bangui b/testdata/zoneinfo/Africa/Bangui
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Bangui differ
diff --git a/testdata/zoneinfo/Africa/Banjul b/testdata/zoneinfo/Africa/Banjul
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Banjul differ
diff --git a/testdata/zoneinfo/Africa/Bissau b/testdata/zoneinfo/Africa/Bissau
new file mode 100644
index 0000000..0da1d1e
Binary files /dev/null and b/testdata/zoneinfo/Africa/Bissau differ
diff --git a/testdata/zoneinfo/Africa/Blantyre b/testdata/zoneinfo/Africa/Blantyre
new file mode 100644
index 0000000..651e5cf
Binary files /dev/null and b/testdata/zoneinfo/Africa/Blantyre differ
diff --git a/testdata/zoneinfo/Africa/Brazzaville b/testdata/zoneinfo/Africa/Brazzaville
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Brazzaville differ
diff --git a/testdata/zoneinfo/Africa/Bujumbura b/testdata/zoneinfo/Africa/Bujumbura
new file mode 100644
index 0000000..651e5cf
Binary files /dev/null and b/testdata/zoneinfo/Africa/Bujumbura differ
diff --git a/testdata/zoneinfo/Africa/Cairo b/testdata/zoneinfo/Africa/Cairo
new file mode 100644
index 0000000..ea38c97
Binary files /dev/null and b/testdata/zoneinfo/Africa/Cairo differ
diff --git a/testdata/zoneinfo/Africa/Casablanca b/testdata/zoneinfo/Africa/Casablanca
new file mode 100644
index 0000000..0263c90
Binary files /dev/null and b/testdata/zoneinfo/Africa/Casablanca differ
diff --git a/testdata/zoneinfo/Africa/Ceuta b/testdata/zoneinfo/Africa/Ceuta
new file mode 100644
index 0000000..a461dce
Binary files /dev/null and b/testdata/zoneinfo/Africa/Ceuta differ
diff --git a/testdata/zoneinfo/Africa/Conakry b/testdata/zoneinfo/Africa/Conakry
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Conakry differ
diff --git a/testdata/zoneinfo/Africa/Dakar b/testdata/zoneinfo/Africa/Dakar
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Dakar differ
diff --git a/testdata/zoneinfo/Africa/Dar_es_Salaam b/testdata/zoneinfo/Africa/Dar_es_Salaam
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Africa/Dar_es_Salaam differ
diff --git a/testdata/zoneinfo/Africa/Djibouti b/testdata/zoneinfo/Africa/Djibouti
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Africa/Djibouti differ
diff --git a/testdata/zoneinfo/Africa/Douala b/testdata/zoneinfo/Africa/Douala
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Douala differ
diff --git a/testdata/zoneinfo/Africa/El_Aaiun b/testdata/zoneinfo/Africa/El_Aaiun
new file mode 100644
index 0000000..772e23c
Binary files /dev/null and b/testdata/zoneinfo/Africa/El_Aaiun differ
diff --git a/testdata/zoneinfo/Africa/Freetown b/testdata/zoneinfo/Africa/Freetown
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Freetown differ
diff --git a/testdata/zoneinfo/Africa/Gaborone b/testdata/zoneinfo/Africa/Gaborone
new file mode 100644
index 0000000..651e5cf
Binary files /dev/null and b/testdata/zoneinfo/Africa/Gaborone differ
diff --git a/testdata/zoneinfo/Africa/Harare b/testdata/zoneinfo/Africa/Harare
new file mode 100644
index 0000000..651e5cf
Binary files /dev/null and b/testdata/zoneinfo/Africa/Harare differ
diff --git a/testdata/zoneinfo/Africa/Johannesburg b/testdata/zoneinfo/Africa/Johannesburg
new file mode 100644
index 0000000..bada063
Binary files /dev/null and b/testdata/zoneinfo/Africa/Johannesburg differ
diff --git a/testdata/zoneinfo/Africa/Juba b/testdata/zoneinfo/Africa/Juba
new file mode 100644
index 0000000..0aba9ff
Binary files /dev/null and b/testdata/zoneinfo/Africa/Juba differ
diff --git a/testdata/zoneinfo/Africa/Kampala b/testdata/zoneinfo/Africa/Kampala
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Africa/Kampala differ
diff --git a/testdata/zoneinfo/Africa/Khartoum b/testdata/zoneinfo/Africa/Khartoum
new file mode 100644
index 0000000..3f8e44b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Khartoum differ
diff --git a/testdata/zoneinfo/Africa/Kigali b/testdata/zoneinfo/Africa/Kigali
new file mode 100644
index 0000000..651e5cf
Binary files /dev/null and b/testdata/zoneinfo/Africa/Kigali differ
diff --git a/testdata/zoneinfo/Africa/Kinshasa b/testdata/zoneinfo/Africa/Kinshasa
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Kinshasa differ
diff --git a/testdata/zoneinfo/Africa/Lagos b/testdata/zoneinfo/Africa/Lagos
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Lagos differ
diff --git a/testdata/zoneinfo/Africa/Libreville b/testdata/zoneinfo/Africa/Libreville
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Libreville differ
diff --git a/testdata/zoneinfo/Africa/Lome b/testdata/zoneinfo/Africa/Lome
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Lome differ
diff --git a/testdata/zoneinfo/Africa/Luanda b/testdata/zoneinfo/Africa/Luanda
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Luanda differ
diff --git a/testdata/zoneinfo/Africa/Lubumbashi b/testdata/zoneinfo/Africa/Lubumbashi
new file mode 100644
index 0000000..651e5cf
Binary files /dev/null and b/testdata/zoneinfo/Africa/Lubumbashi differ
diff --git a/testdata/zoneinfo/Africa/Lusaka b/testdata/zoneinfo/Africa/Lusaka
new file mode 100644
index 0000000..651e5cf
Binary files /dev/null and b/testdata/zoneinfo/Africa/Lusaka differ
diff --git a/testdata/zoneinfo/Africa/Malabo b/testdata/zoneinfo/Africa/Malabo
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Malabo differ
diff --git a/testdata/zoneinfo/Africa/Maputo b/testdata/zoneinfo/Africa/Maputo
new file mode 100644
index 0000000..651e5cf
Binary files /dev/null and b/testdata/zoneinfo/Africa/Maputo differ
diff --git a/testdata/zoneinfo/Africa/Maseru b/testdata/zoneinfo/Africa/Maseru
new file mode 100644
index 0000000..bada063
Binary files /dev/null and b/testdata/zoneinfo/Africa/Maseru differ
diff --git a/testdata/zoneinfo/Africa/Mbabane b/testdata/zoneinfo/Africa/Mbabane
new file mode 100644
index 0000000..bada063
Binary files /dev/null and b/testdata/zoneinfo/Africa/Mbabane differ
diff --git a/testdata/zoneinfo/Africa/Mogadishu b/testdata/zoneinfo/Africa/Mogadishu
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Africa/Mogadishu differ
diff --git a/testdata/zoneinfo/Africa/Monrovia b/testdata/zoneinfo/Africa/Monrovia
new file mode 100644
index 0000000..8377809
Binary files /dev/null and b/testdata/zoneinfo/Africa/Monrovia differ
diff --git a/testdata/zoneinfo/Africa/Nairobi b/testdata/zoneinfo/Africa/Nairobi
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Africa/Nairobi differ
diff --git a/testdata/zoneinfo/Africa/Ndjamena b/testdata/zoneinfo/Africa/Ndjamena
new file mode 100644
index 0000000..ecbc096
Binary files /dev/null and b/testdata/zoneinfo/Africa/Ndjamena differ
diff --git a/testdata/zoneinfo/Africa/Niamey b/testdata/zoneinfo/Africa/Niamey
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Niamey differ
diff --git a/testdata/zoneinfo/Africa/Nouakchott b/testdata/zoneinfo/Africa/Nouakchott
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Nouakchott differ
diff --git a/testdata/zoneinfo/Africa/Ouagadougou b/testdata/zoneinfo/Africa/Ouagadougou
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Ouagadougou differ
diff --git a/testdata/zoneinfo/Africa/Porto-Novo b/testdata/zoneinfo/Africa/Porto-Novo
new file mode 100644
index 0000000..3d7a71b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Porto-Novo differ
diff --git a/testdata/zoneinfo/Africa/Sao_Tome b/testdata/zoneinfo/Africa/Sao_Tome
new file mode 100644
index 0000000..425ad3f
Binary files /dev/null and b/testdata/zoneinfo/Africa/Sao_Tome differ
diff --git a/testdata/zoneinfo/Africa/Timbuktu b/testdata/zoneinfo/Africa/Timbuktu
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Africa/Timbuktu differ
diff --git a/testdata/zoneinfo/Africa/Tripoli b/testdata/zoneinfo/Africa/Tripoli
new file mode 100644
index 0000000..e0c8997
Binary files /dev/null and b/testdata/zoneinfo/Africa/Tripoli differ
diff --git a/testdata/zoneinfo/Africa/Tunis b/testdata/zoneinfo/Africa/Tunis
new file mode 100644
index 0000000..ca324cb
Binary files /dev/null and b/testdata/zoneinfo/Africa/Tunis differ
diff --git a/testdata/zoneinfo/Africa/Windhoek b/testdata/zoneinfo/Africa/Windhoek
new file mode 100644
index 0000000..0edc52b
Binary files /dev/null and b/testdata/zoneinfo/Africa/Windhoek differ
diff --git a/testdata/zoneinfo/America/Adak b/testdata/zoneinfo/America/Adak
new file mode 100644
index 0000000..b1497bd
Binary files /dev/null and b/testdata/zoneinfo/America/Adak differ
diff --git a/testdata/zoneinfo/America/Anchorage b/testdata/zoneinfo/America/Anchorage
new file mode 100644
index 0000000..cdf0572
Binary files /dev/null and b/testdata/zoneinfo/America/Anchorage differ
diff --git a/testdata/zoneinfo/America/Anguilla b/testdata/zoneinfo/America/Anguilla
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Anguilla differ
diff --git a/testdata/zoneinfo/America/Antigua b/testdata/zoneinfo/America/Antigua
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Antigua differ
diff --git a/testdata/zoneinfo/America/Araguaina b/testdata/zoneinfo/America/Araguaina
new file mode 100644
index 0000000..f66c9f7
Binary files /dev/null and b/testdata/zoneinfo/America/Araguaina differ
diff --git a/testdata/zoneinfo/America/Argentina/Buenos_Aires b/testdata/zoneinfo/America/Argentina/Buenos_Aires
new file mode 100644
index 0000000..d6f999b
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/Buenos_Aires differ
diff --git a/testdata/zoneinfo/America/Argentina/Catamarca b/testdata/zoneinfo/America/Argentina/Catamarca
new file mode 100644
index 0000000..1dcc8d8
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/Catamarca differ
diff --git a/testdata/zoneinfo/America/Argentina/ComodRivadavia b/testdata/zoneinfo/America/Argentina/ComodRivadavia
new file mode 100644
index 0000000..1dcc8d8
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/ComodRivadavia differ
diff --git a/testdata/zoneinfo/America/Argentina/Cordoba b/testdata/zoneinfo/America/Argentina/Cordoba
new file mode 100644
index 0000000..35a52e5
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/Cordoba differ
diff --git a/testdata/zoneinfo/America/Argentina/Jujuy b/testdata/zoneinfo/America/Argentina/Jujuy
new file mode 100644
index 0000000..b275f27
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/Jujuy differ
diff --git a/testdata/zoneinfo/America/Argentina/La_Rioja b/testdata/zoneinfo/America/Argentina/La_Rioja
new file mode 100644
index 0000000..23fca12
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/La_Rioja differ
diff --git a/testdata/zoneinfo/America/Argentina/Mendoza b/testdata/zoneinfo/America/Argentina/Mendoza
new file mode 100644
index 0000000..691c569
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/Mendoza differ
diff --git a/testdata/zoneinfo/America/Argentina/Rio_Gallegos b/testdata/zoneinfo/America/Argentina/Rio_Gallegos
new file mode 100644
index 0000000..991d1fa
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/Rio_Gallegos differ
diff --git a/testdata/zoneinfo/America/Argentina/Salta b/testdata/zoneinfo/America/Argentina/Salta
new file mode 100644
index 0000000..58863e0
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/Salta differ
diff --git a/testdata/zoneinfo/America/Argentina/San_Juan b/testdata/zoneinfo/America/Argentina/San_Juan
new file mode 100644
index 0000000..7eba33c
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/San_Juan differ
diff --git a/testdata/zoneinfo/America/Argentina/San_Luis b/testdata/zoneinfo/America/Argentina/San_Luis
new file mode 100644
index 0000000..0a81cbd
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/San_Luis differ
diff --git a/testdata/zoneinfo/America/Argentina/Tucuman b/testdata/zoneinfo/America/Argentina/Tucuman
new file mode 100644
index 0000000..10556d5
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/Tucuman differ
diff --git a/testdata/zoneinfo/America/Argentina/Ushuaia b/testdata/zoneinfo/America/Argentina/Ushuaia
new file mode 100644
index 0000000..e031750
Binary files /dev/null and b/testdata/zoneinfo/America/Argentina/Ushuaia differ
diff --git a/testdata/zoneinfo/America/Aruba b/testdata/zoneinfo/America/Aruba
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Aruba differ
diff --git a/testdata/zoneinfo/America/Asuncion b/testdata/zoneinfo/America/Asuncion
new file mode 100644
index 0000000..6225036
Binary files /dev/null and b/testdata/zoneinfo/America/Asuncion differ
diff --git a/testdata/zoneinfo/America/Atikokan b/testdata/zoneinfo/America/Atikokan
new file mode 100644
index 0000000..9154643
Binary files /dev/null and b/testdata/zoneinfo/America/Atikokan differ
diff --git a/testdata/zoneinfo/America/Atka b/testdata/zoneinfo/America/Atka
new file mode 100644
index 0000000..b1497bd
Binary files /dev/null and b/testdata/zoneinfo/America/Atka differ
diff --git a/testdata/zoneinfo/America/Bahia b/testdata/zoneinfo/America/Bahia
new file mode 100644
index 0000000..7969e30
Binary files /dev/null and b/testdata/zoneinfo/America/Bahia differ
diff --git a/testdata/zoneinfo/America/Bahia_Banderas b/testdata/zoneinfo/America/Bahia_Banderas
new file mode 100644
index 0000000..cbe22a7
Binary files /dev/null and b/testdata/zoneinfo/America/Bahia_Banderas differ
diff --git a/testdata/zoneinfo/America/Barbados b/testdata/zoneinfo/America/Barbados
new file mode 100644
index 0000000..720c986
Binary files /dev/null and b/testdata/zoneinfo/America/Barbados differ
diff --git a/testdata/zoneinfo/America/Belem b/testdata/zoneinfo/America/Belem
new file mode 100644
index 0000000..e0d7653
Binary files /dev/null and b/testdata/zoneinfo/America/Belem differ
diff --git a/testdata/zoneinfo/America/Belize b/testdata/zoneinfo/America/Belize
new file mode 100644
index 0000000..bfc19f4
Binary files /dev/null and b/testdata/zoneinfo/America/Belize differ
diff --git a/testdata/zoneinfo/America/Blanc-Sablon b/testdata/zoneinfo/America/Blanc-Sablon
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Blanc-Sablon differ
diff --git a/testdata/zoneinfo/America/Boa_Vista b/testdata/zoneinfo/America/Boa_Vista
new file mode 100644
index 0000000..fca9720
Binary files /dev/null and b/testdata/zoneinfo/America/Boa_Vista differ
diff --git a/testdata/zoneinfo/America/Bogota b/testdata/zoneinfo/America/Bogota
new file mode 100644
index 0000000..6cb53d4
Binary files /dev/null and b/testdata/zoneinfo/America/Bogota differ
diff --git a/testdata/zoneinfo/America/Boise b/testdata/zoneinfo/America/Boise
new file mode 100644
index 0000000..72fec9e
Binary files /dev/null and b/testdata/zoneinfo/America/Boise differ
diff --git a/testdata/zoneinfo/America/Buenos_Aires b/testdata/zoneinfo/America/Buenos_Aires
new file mode 100644
index 0000000..d6f999b
Binary files /dev/null and b/testdata/zoneinfo/America/Buenos_Aires differ
diff --git a/testdata/zoneinfo/America/Cambridge_Bay b/testdata/zoneinfo/America/Cambridge_Bay
new file mode 100644
index 0000000..0a22252
Binary files /dev/null and b/testdata/zoneinfo/America/Cambridge_Bay differ
diff --git a/testdata/zoneinfo/America/Campo_Grande b/testdata/zoneinfo/America/Campo_Grande
new file mode 100644
index 0000000..6855e4e
Binary files /dev/null and b/testdata/zoneinfo/America/Campo_Grande differ
diff --git a/testdata/zoneinfo/America/Cancun b/testdata/zoneinfo/America/Cancun
new file mode 100644
index 0000000..640b259
Binary files /dev/null and b/testdata/zoneinfo/America/Cancun differ
diff --git a/testdata/zoneinfo/America/Caracas b/testdata/zoneinfo/America/Caracas
new file mode 100644
index 0000000..8dbe6ff
Binary files /dev/null and b/testdata/zoneinfo/America/Caracas differ
diff --git a/testdata/zoneinfo/America/Catamarca b/testdata/zoneinfo/America/Catamarca
new file mode 100644
index 0000000..1dcc8d8
Binary files /dev/null and b/testdata/zoneinfo/America/Catamarca differ
diff --git a/testdata/zoneinfo/America/Cayenne b/testdata/zoneinfo/America/Cayenne
new file mode 100644
index 0000000..cd49f05
Binary files /dev/null and b/testdata/zoneinfo/America/Cayenne differ
diff --git a/testdata/zoneinfo/America/Cayman b/testdata/zoneinfo/America/Cayman
new file mode 100644
index 0000000..9154643
Binary files /dev/null and b/testdata/zoneinfo/America/Cayman differ
diff --git a/testdata/zoneinfo/America/Chicago b/testdata/zoneinfo/America/Chicago
new file mode 100644
index 0000000..b016880
Binary files /dev/null and b/testdata/zoneinfo/America/Chicago differ
diff --git a/testdata/zoneinfo/America/Chihuahua b/testdata/zoneinfo/America/Chihuahua
new file mode 100644
index 0000000..e1780a5
Binary files /dev/null and b/testdata/zoneinfo/America/Chihuahua differ
diff --git a/testdata/zoneinfo/America/Coral_Harbour b/testdata/zoneinfo/America/Coral_Harbour
new file mode 100644
index 0000000..9154643
Binary files /dev/null and b/testdata/zoneinfo/America/Coral_Harbour differ
diff --git a/testdata/zoneinfo/America/Cordoba b/testdata/zoneinfo/America/Cordoba
new file mode 100644
index 0000000..35a52e5
Binary files /dev/null and b/testdata/zoneinfo/America/Cordoba differ
diff --git a/testdata/zoneinfo/America/Costa_Rica b/testdata/zoneinfo/America/Costa_Rica
new file mode 100644
index 0000000..08f0128
Binary files /dev/null and b/testdata/zoneinfo/America/Costa_Rica differ
diff --git a/testdata/zoneinfo/America/Creston b/testdata/zoneinfo/America/Creston
new file mode 100644
index 0000000..c2bd2f9
Binary files /dev/null and b/testdata/zoneinfo/America/Creston differ
diff --git a/testdata/zoneinfo/America/Cuiaba b/testdata/zoneinfo/America/Cuiaba
new file mode 100644
index 0000000..c09a875
Binary files /dev/null and b/testdata/zoneinfo/America/Cuiaba differ
diff --git a/testdata/zoneinfo/America/Curacao b/testdata/zoneinfo/America/Curacao
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Curacao differ
diff --git a/testdata/zoneinfo/America/Danmarkshavn b/testdata/zoneinfo/America/Danmarkshavn
new file mode 100644
index 0000000..8718efc
Binary files /dev/null and b/testdata/zoneinfo/America/Danmarkshavn differ
diff --git a/testdata/zoneinfo/America/Dawson b/testdata/zoneinfo/America/Dawson
new file mode 100644
index 0000000..07e4c5f
Binary files /dev/null and b/testdata/zoneinfo/America/Dawson differ
diff --git a/testdata/zoneinfo/America/Dawson_Creek b/testdata/zoneinfo/America/Dawson_Creek
new file mode 100644
index 0000000..761d1d9
Binary files /dev/null and b/testdata/zoneinfo/America/Dawson_Creek differ
diff --git a/testdata/zoneinfo/America/Denver b/testdata/zoneinfo/America/Denver
new file mode 100644
index 0000000..09e54e5
Binary files /dev/null and b/testdata/zoneinfo/America/Denver differ
diff --git a/testdata/zoneinfo/America/Detroit b/testdata/zoneinfo/America/Detroit
new file mode 100644
index 0000000..6eb3ac4
Binary files /dev/null and b/testdata/zoneinfo/America/Detroit differ
diff --git a/testdata/zoneinfo/America/Dominica b/testdata/zoneinfo/America/Dominica
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Dominica differ
diff --git a/testdata/zoneinfo/America/Edmonton b/testdata/zoneinfo/America/Edmonton
new file mode 100644
index 0000000..645ee94
Binary files /dev/null and b/testdata/zoneinfo/America/Edmonton differ
diff --git a/testdata/zoneinfo/America/Eirunepe b/testdata/zoneinfo/America/Eirunepe
new file mode 100644
index 0000000..7da4b98
Binary files /dev/null and b/testdata/zoneinfo/America/Eirunepe differ
diff --git a/testdata/zoneinfo/America/El_Salvador b/testdata/zoneinfo/America/El_Salvador
new file mode 100644
index 0000000..4348411
Binary files /dev/null and b/testdata/zoneinfo/America/El_Salvador differ
diff --git a/testdata/zoneinfo/America/Ensenada b/testdata/zoneinfo/America/Ensenada
new file mode 100644
index 0000000..19ccd35
Binary files /dev/null and b/testdata/zoneinfo/America/Ensenada differ
diff --git a/testdata/zoneinfo/America/Fort_Nelson b/testdata/zoneinfo/America/Fort_Nelson
new file mode 100644
index 0000000..2a49c6c
Binary files /dev/null and b/testdata/zoneinfo/America/Fort_Nelson differ
diff --git a/testdata/zoneinfo/America/Fort_Wayne b/testdata/zoneinfo/America/Fort_Wayne
new file mode 100644
index 0000000..6b08d15
Binary files /dev/null and b/testdata/zoneinfo/America/Fort_Wayne differ
diff --git a/testdata/zoneinfo/America/Fortaleza b/testdata/zoneinfo/America/Fortaleza
new file mode 100644
index 0000000..092e40d
Binary files /dev/null and b/testdata/zoneinfo/America/Fortaleza differ
diff --git a/testdata/zoneinfo/America/Glace_Bay b/testdata/zoneinfo/America/Glace_Bay
new file mode 100644
index 0000000..f85eb34
Binary files /dev/null and b/testdata/zoneinfo/America/Glace_Bay differ
diff --git a/testdata/zoneinfo/America/Godthab b/testdata/zoneinfo/America/Godthab
new file mode 100644
index 0000000..4ddc99d
Binary files /dev/null and b/testdata/zoneinfo/America/Godthab differ
diff --git a/testdata/zoneinfo/America/Goose_Bay b/testdata/zoneinfo/America/Goose_Bay
new file mode 100644
index 0000000..820e0dd
Binary files /dev/null and b/testdata/zoneinfo/America/Goose_Bay differ
diff --git a/testdata/zoneinfo/America/Grand_Turk b/testdata/zoneinfo/America/Grand_Turk
new file mode 100644
index 0000000..9d90e74
Binary files /dev/null and b/testdata/zoneinfo/America/Grand_Turk differ
diff --git a/testdata/zoneinfo/America/Grenada b/testdata/zoneinfo/America/Grenada
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Grenada differ
diff --git a/testdata/zoneinfo/America/Guadeloupe b/testdata/zoneinfo/America/Guadeloupe
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Guadeloupe differ
diff --git a/testdata/zoneinfo/America/Guatemala b/testdata/zoneinfo/America/Guatemala
new file mode 100644
index 0000000..8aa8e58
Binary files /dev/null and b/testdata/zoneinfo/America/Guatemala differ
diff --git a/testdata/zoneinfo/America/Guayaquil b/testdata/zoneinfo/America/Guayaquil
new file mode 100644
index 0000000..381ae6c
Binary files /dev/null and b/testdata/zoneinfo/America/Guayaquil differ
diff --git a/testdata/zoneinfo/America/Guyana b/testdata/zoneinfo/America/Guyana
new file mode 100644
index 0000000..bcc6688
Binary files /dev/null and b/testdata/zoneinfo/America/Guyana differ
diff --git a/testdata/zoneinfo/America/Halifax b/testdata/zoneinfo/America/Halifax
new file mode 100644
index 0000000..9fa850a
Binary files /dev/null and b/testdata/zoneinfo/America/Halifax differ
diff --git a/testdata/zoneinfo/America/Havana b/testdata/zoneinfo/America/Havana
new file mode 100644
index 0000000..e06629d
Binary files /dev/null and b/testdata/zoneinfo/America/Havana differ
diff --git a/testdata/zoneinfo/America/Hermosillo b/testdata/zoneinfo/America/Hermosillo
new file mode 100644
index 0000000..8283239
Binary files /dev/null and b/testdata/zoneinfo/America/Hermosillo differ
diff --git a/testdata/zoneinfo/America/Indiana/Indianapolis b/testdata/zoneinfo/America/Indiana/Indianapolis
new file mode 100644
index 0000000..6b08d15
Binary files /dev/null and b/testdata/zoneinfo/America/Indiana/Indianapolis differ
diff --git a/testdata/zoneinfo/America/Indiana/Knox b/testdata/zoneinfo/America/Indiana/Knox
new file mode 100644
index 0000000..b187d5f
Binary files /dev/null and b/testdata/zoneinfo/America/Indiana/Knox differ
diff --git a/testdata/zoneinfo/America/Indiana/Marengo b/testdata/zoneinfo/America/Indiana/Marengo
new file mode 100644
index 0000000..a730fe6
Binary files /dev/null and b/testdata/zoneinfo/America/Indiana/Marengo differ
diff --git a/testdata/zoneinfo/America/Indiana/Petersburg b/testdata/zoneinfo/America/Indiana/Petersburg
new file mode 100644
index 0000000..341a023
Binary files /dev/null and b/testdata/zoneinfo/America/Indiana/Petersburg differ
diff --git a/testdata/zoneinfo/America/Indiana/Tell_City b/testdata/zoneinfo/America/Indiana/Tell_City
new file mode 100644
index 0000000..76e1f62
Binary files /dev/null and b/testdata/zoneinfo/America/Indiana/Tell_City differ
diff --git a/testdata/zoneinfo/America/Indiana/Vevay b/testdata/zoneinfo/America/Indiana/Vevay
new file mode 100644
index 0000000..f2acf6c
Binary files /dev/null and b/testdata/zoneinfo/America/Indiana/Vevay differ
diff --git a/testdata/zoneinfo/America/Indiana/Vincennes b/testdata/zoneinfo/America/Indiana/Vincennes
new file mode 100644
index 0000000..c255f89
Binary files /dev/null and b/testdata/zoneinfo/America/Indiana/Vincennes differ
diff --git a/testdata/zoneinfo/America/Indiana/Winamac b/testdata/zoneinfo/America/Indiana/Winamac
new file mode 100644
index 0000000..8700ed9
Binary files /dev/null and b/testdata/zoneinfo/America/Indiana/Winamac differ
diff --git a/testdata/zoneinfo/America/Indianapolis b/testdata/zoneinfo/America/Indianapolis
new file mode 100644
index 0000000..6b08d15
Binary files /dev/null and b/testdata/zoneinfo/America/Indianapolis differ
diff --git a/testdata/zoneinfo/America/Inuvik b/testdata/zoneinfo/America/Inuvik
new file mode 100644
index 0000000..af3107d
Binary files /dev/null and b/testdata/zoneinfo/America/Inuvik differ
diff --git a/testdata/zoneinfo/America/Iqaluit b/testdata/zoneinfo/America/Iqaluit
new file mode 100644
index 0000000..eb2c99c
Binary files /dev/null and b/testdata/zoneinfo/America/Iqaluit differ
diff --git a/testdata/zoneinfo/America/Jamaica b/testdata/zoneinfo/America/Jamaica
new file mode 100644
index 0000000..be6b1b6
Binary files /dev/null and b/testdata/zoneinfo/America/Jamaica differ
diff --git a/testdata/zoneinfo/America/Jujuy b/testdata/zoneinfo/America/Jujuy
new file mode 100644
index 0000000..b275f27
Binary files /dev/null and b/testdata/zoneinfo/America/Jujuy differ
diff --git a/testdata/zoneinfo/America/Juneau b/testdata/zoneinfo/America/Juneau
new file mode 100644
index 0000000..e347b36
Binary files /dev/null and b/testdata/zoneinfo/America/Juneau differ
diff --git a/testdata/zoneinfo/America/Kentucky/Louisville b/testdata/zoneinfo/America/Kentucky/Louisville
new file mode 100644
index 0000000..f2136d6
Binary files /dev/null and b/testdata/zoneinfo/America/Kentucky/Louisville differ
diff --git a/testdata/zoneinfo/America/Kentucky/Monticello b/testdata/zoneinfo/America/Kentucky/Monticello
new file mode 100644
index 0000000..d9f54a1
Binary files /dev/null and b/testdata/zoneinfo/America/Kentucky/Monticello differ
diff --git a/testdata/zoneinfo/America/Knox_IN b/testdata/zoneinfo/America/Knox_IN
new file mode 100644
index 0000000..b187d5f
Binary files /dev/null and b/testdata/zoneinfo/America/Knox_IN differ
diff --git a/testdata/zoneinfo/America/Kralendijk b/testdata/zoneinfo/America/Kralendijk
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Kralendijk differ
diff --git a/testdata/zoneinfo/America/La_Paz b/testdata/zoneinfo/America/La_Paz
new file mode 100644
index 0000000..68ddaae
Binary files /dev/null and b/testdata/zoneinfo/America/La_Paz differ
diff --git a/testdata/zoneinfo/America/Lima b/testdata/zoneinfo/America/Lima
new file mode 100644
index 0000000..b643c55
Binary files /dev/null and b/testdata/zoneinfo/America/Lima differ
diff --git a/testdata/zoneinfo/America/Los_Angeles b/testdata/zoneinfo/America/Los_Angeles
new file mode 100644
index 0000000..aaf0778
Binary files /dev/null and b/testdata/zoneinfo/America/Los_Angeles differ
diff --git a/testdata/zoneinfo/America/Louisville b/testdata/zoneinfo/America/Louisville
new file mode 100644
index 0000000..f2136d6
Binary files /dev/null and b/testdata/zoneinfo/America/Louisville differ
diff --git a/testdata/zoneinfo/America/Lower_Princes b/testdata/zoneinfo/America/Lower_Princes
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Lower_Princes differ
diff --git a/testdata/zoneinfo/America/Maceio b/testdata/zoneinfo/America/Maceio
new file mode 100644
index 0000000..dbb8d57
Binary files /dev/null and b/testdata/zoneinfo/America/Maceio differ
diff --git a/testdata/zoneinfo/America/Managua b/testdata/zoneinfo/America/Managua
new file mode 100644
index 0000000..86ef76b
Binary files /dev/null and b/testdata/zoneinfo/America/Managua differ
diff --git a/testdata/zoneinfo/America/Manaus b/testdata/zoneinfo/America/Manaus
new file mode 100644
index 0000000..59c952e
Binary files /dev/null and b/testdata/zoneinfo/America/Manaus differ
diff --git a/testdata/zoneinfo/America/Marigot b/testdata/zoneinfo/America/Marigot
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Marigot differ
diff --git a/testdata/zoneinfo/America/Martinique b/testdata/zoneinfo/America/Martinique
new file mode 100644
index 0000000..25c0232
Binary files /dev/null and b/testdata/zoneinfo/America/Martinique differ
diff --git a/testdata/zoneinfo/America/Matamoros b/testdata/zoneinfo/America/Matamoros
new file mode 100644
index 0000000..722751b
Binary files /dev/null and b/testdata/zoneinfo/America/Matamoros differ
diff --git a/testdata/zoneinfo/America/Mazatlan b/testdata/zoneinfo/America/Mazatlan
new file mode 100644
index 0000000..4c819fa
Binary files /dev/null and b/testdata/zoneinfo/America/Mazatlan differ
diff --git a/testdata/zoneinfo/America/Mendoza b/testdata/zoneinfo/America/Mendoza
new file mode 100644
index 0000000..691c569
Binary files /dev/null and b/testdata/zoneinfo/America/Mendoza differ
diff --git a/testdata/zoneinfo/America/Menominee b/testdata/zoneinfo/America/Menominee
new file mode 100644
index 0000000..28d2c56
Binary files /dev/null and b/testdata/zoneinfo/America/Menominee differ
diff --git a/testdata/zoneinfo/America/Merida b/testdata/zoneinfo/America/Merida
new file mode 100644
index 0000000..d3b0ca1
Binary files /dev/null and b/testdata/zoneinfo/America/Merida differ
diff --git a/testdata/zoneinfo/America/Metlakatla b/testdata/zoneinfo/America/Metlakatla
new file mode 100644
index 0000000..9fefee3
Binary files /dev/null and b/testdata/zoneinfo/America/Metlakatla differ
diff --git a/testdata/zoneinfo/America/Mexico_City b/testdata/zoneinfo/America/Mexico_City
new file mode 100644
index 0000000..ffcf8be
Binary files /dev/null and b/testdata/zoneinfo/America/Mexico_City differ
diff --git a/testdata/zoneinfo/America/Miquelon b/testdata/zoneinfo/America/Miquelon
new file mode 100644
index 0000000..3b62585
Binary files /dev/null and b/testdata/zoneinfo/America/Miquelon differ
diff --git a/testdata/zoneinfo/America/Moncton b/testdata/zoneinfo/America/Moncton
new file mode 100644
index 0000000..ecb69ef
Binary files /dev/null and b/testdata/zoneinfo/America/Moncton differ
diff --git a/testdata/zoneinfo/America/Monterrey b/testdata/zoneinfo/America/Monterrey
new file mode 100644
index 0000000..dea9e3f
Binary files /dev/null and b/testdata/zoneinfo/America/Monterrey differ
diff --git a/testdata/zoneinfo/America/Montevideo b/testdata/zoneinfo/America/Montevideo
new file mode 100644
index 0000000..4b2fb3e
Binary files /dev/null and b/testdata/zoneinfo/America/Montevideo differ
diff --git a/testdata/zoneinfo/America/Montreal b/testdata/zoneinfo/America/Montreal
new file mode 100644
index 0000000..fe6be8e
Binary files /dev/null and b/testdata/zoneinfo/America/Montreal differ
diff --git a/testdata/zoneinfo/America/Montserrat b/testdata/zoneinfo/America/Montserrat
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Montserrat differ
diff --git a/testdata/zoneinfo/America/Nassau b/testdata/zoneinfo/America/Nassau
new file mode 100644
index 0000000..fe6be8e
Binary files /dev/null and b/testdata/zoneinfo/America/Nassau differ
diff --git a/testdata/zoneinfo/America/New_York b/testdata/zoneinfo/America/New_York
new file mode 100644
index 0000000..2b6c2ee
Binary files /dev/null and b/testdata/zoneinfo/America/New_York differ
diff --git a/testdata/zoneinfo/America/Nipigon b/testdata/zoneinfo/America/Nipigon
new file mode 100644
index 0000000..b9f67a9
Binary files /dev/null and b/testdata/zoneinfo/America/Nipigon differ
diff --git a/testdata/zoneinfo/America/Nome b/testdata/zoneinfo/America/Nome
new file mode 100644
index 0000000..23ead1c
Binary files /dev/null and b/testdata/zoneinfo/America/Nome differ
diff --git a/testdata/zoneinfo/America/Noronha b/testdata/zoneinfo/America/Noronha
new file mode 100644
index 0000000..9e74745
Binary files /dev/null and b/testdata/zoneinfo/America/Noronha differ
diff --git a/testdata/zoneinfo/America/North_Dakota/Beulah b/testdata/zoneinfo/America/North_Dakota/Beulah
new file mode 100644
index 0000000..becf438
Binary files /dev/null and b/testdata/zoneinfo/America/North_Dakota/Beulah differ
diff --git a/testdata/zoneinfo/America/North_Dakota/Center b/testdata/zoneinfo/America/North_Dakota/Center
new file mode 100644
index 0000000..d03bda0
Binary files /dev/null and b/testdata/zoneinfo/America/North_Dakota/Center differ
diff --git a/testdata/zoneinfo/America/North_Dakota/New_Salem b/testdata/zoneinfo/America/North_Dakota/New_Salem
new file mode 100644
index 0000000..ecefc15
Binary files /dev/null and b/testdata/zoneinfo/America/North_Dakota/New_Salem differ
diff --git a/testdata/zoneinfo/America/Nuuk b/testdata/zoneinfo/America/Nuuk
new file mode 100644
index 0000000..4ddc99d
Binary files /dev/null and b/testdata/zoneinfo/America/Nuuk differ
diff --git a/testdata/zoneinfo/America/Ojinaga b/testdata/zoneinfo/America/Ojinaga
new file mode 100644
index 0000000..da0909c
Binary files /dev/null and b/testdata/zoneinfo/America/Ojinaga differ
diff --git a/testdata/zoneinfo/America/Panama b/testdata/zoneinfo/America/Panama
new file mode 100644
index 0000000..9154643
Binary files /dev/null and b/testdata/zoneinfo/America/Panama differ
diff --git a/testdata/zoneinfo/America/Pangnirtung b/testdata/zoneinfo/America/Pangnirtung
new file mode 100644
index 0000000..5be6f9b
Binary files /dev/null and b/testdata/zoneinfo/America/Pangnirtung differ
diff --git a/testdata/zoneinfo/America/Paramaribo b/testdata/zoneinfo/America/Paramaribo
new file mode 100644
index 0000000..24f925a
Binary files /dev/null and b/testdata/zoneinfo/America/Paramaribo differ
diff --git a/testdata/zoneinfo/America/Phoenix b/testdata/zoneinfo/America/Phoenix
new file mode 100644
index 0000000..c2bd2f9
Binary files /dev/null and b/testdata/zoneinfo/America/Phoenix differ
diff --git a/testdata/zoneinfo/America/Port-au-Prince b/testdata/zoneinfo/America/Port-au-Prince
new file mode 100644
index 0000000..3e75731
Binary files /dev/null and b/testdata/zoneinfo/America/Port-au-Prince differ
diff --git a/testdata/zoneinfo/America/Port_of_Spain b/testdata/zoneinfo/America/Port_of_Spain
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Port_of_Spain differ
diff --git a/testdata/zoneinfo/America/Porto_Acre b/testdata/zoneinfo/America/Porto_Acre
new file mode 100644
index 0000000..fb5185c
Binary files /dev/null and b/testdata/zoneinfo/America/Porto_Acre differ
diff --git a/testdata/zoneinfo/America/Porto_Velho b/testdata/zoneinfo/America/Porto_Velho
new file mode 100644
index 0000000..7f8047d
Binary files /dev/null and b/testdata/zoneinfo/America/Porto_Velho differ
diff --git a/testdata/zoneinfo/America/Puerto_Rico b/testdata/zoneinfo/America/Puerto_Rico
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Puerto_Rico differ
diff --git a/testdata/zoneinfo/America/Punta_Arenas b/testdata/zoneinfo/America/Punta_Arenas
new file mode 100644
index 0000000..c042104
Binary files /dev/null and b/testdata/zoneinfo/America/Punta_Arenas differ
diff --git a/testdata/zoneinfo/America/Rainy_River b/testdata/zoneinfo/America/Rainy_River
new file mode 100644
index 0000000..d6ddda4
Binary files /dev/null and b/testdata/zoneinfo/America/Rainy_River differ
diff --git a/testdata/zoneinfo/America/Rankin_Inlet b/testdata/zoneinfo/America/Rankin_Inlet
new file mode 100644
index 0000000..92e2ed2
Binary files /dev/null and b/testdata/zoneinfo/America/Rankin_Inlet differ
diff --git a/testdata/zoneinfo/America/Recife b/testdata/zoneinfo/America/Recife
new file mode 100644
index 0000000..305abcb
Binary files /dev/null and b/testdata/zoneinfo/America/Recife differ
diff --git a/testdata/zoneinfo/America/Regina b/testdata/zoneinfo/America/Regina
new file mode 100644
index 0000000..a3f8217
Binary files /dev/null and b/testdata/zoneinfo/America/Regina differ
diff --git a/testdata/zoneinfo/America/Resolute b/testdata/zoneinfo/America/Resolute
new file mode 100644
index 0000000..a84d1df
Binary files /dev/null and b/testdata/zoneinfo/America/Resolute differ
diff --git a/testdata/zoneinfo/America/Rio_Branco b/testdata/zoneinfo/America/Rio_Branco
new file mode 100644
index 0000000..fb5185c
Binary files /dev/null and b/testdata/zoneinfo/America/Rio_Branco differ
diff --git a/testdata/zoneinfo/America/Rosario b/testdata/zoneinfo/America/Rosario
new file mode 100644
index 0000000..35a52e5
Binary files /dev/null and b/testdata/zoneinfo/America/Rosario differ
diff --git a/testdata/zoneinfo/America/Santa_Isabel b/testdata/zoneinfo/America/Santa_Isabel
new file mode 100644
index 0000000..19ccd35
Binary files /dev/null and b/testdata/zoneinfo/America/Santa_Isabel differ
diff --git a/testdata/zoneinfo/America/Santarem b/testdata/zoneinfo/America/Santarem
new file mode 100644
index 0000000..f81d144
Binary files /dev/null and b/testdata/zoneinfo/America/Santarem differ
diff --git a/testdata/zoneinfo/America/Santiago b/testdata/zoneinfo/America/Santiago
new file mode 100644
index 0000000..cde8dbb
Binary files /dev/null and b/testdata/zoneinfo/America/Santiago differ
diff --git a/testdata/zoneinfo/America/Santo_Domingo b/testdata/zoneinfo/America/Santo_Domingo
new file mode 100644
index 0000000..3e07850
Binary files /dev/null and b/testdata/zoneinfo/America/Santo_Domingo differ
diff --git a/testdata/zoneinfo/America/Sao_Paulo b/testdata/zoneinfo/America/Sao_Paulo
new file mode 100644
index 0000000..a16da2c
Binary files /dev/null and b/testdata/zoneinfo/America/Sao_Paulo differ
diff --git a/testdata/zoneinfo/America/Scoresbysund b/testdata/zoneinfo/America/Scoresbysund
new file mode 100644
index 0000000..6db4912
Binary files /dev/null and b/testdata/zoneinfo/America/Scoresbysund differ
diff --git a/testdata/zoneinfo/America/Shiprock b/testdata/zoneinfo/America/Shiprock
new file mode 100644
index 0000000..09e54e5
Binary files /dev/null and b/testdata/zoneinfo/America/Shiprock differ
diff --git a/testdata/zoneinfo/America/Sitka b/testdata/zoneinfo/America/Sitka
new file mode 100644
index 0000000..36681ed
Binary files /dev/null and b/testdata/zoneinfo/America/Sitka differ
diff --git a/testdata/zoneinfo/America/St_Barthelemy b/testdata/zoneinfo/America/St_Barthelemy
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/St_Barthelemy differ
diff --git a/testdata/zoneinfo/America/St_Johns b/testdata/zoneinfo/America/St_Johns
new file mode 100644
index 0000000..e5f2aec
Binary files /dev/null and b/testdata/zoneinfo/America/St_Johns differ
diff --git a/testdata/zoneinfo/America/St_Kitts b/testdata/zoneinfo/America/St_Kitts
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/St_Kitts differ
diff --git a/testdata/zoneinfo/America/St_Lucia b/testdata/zoneinfo/America/St_Lucia
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/St_Lucia differ
diff --git a/testdata/zoneinfo/America/St_Thomas b/testdata/zoneinfo/America/St_Thomas
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/St_Thomas differ
diff --git a/testdata/zoneinfo/America/St_Vincent b/testdata/zoneinfo/America/St_Vincent
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/St_Vincent differ
diff --git a/testdata/zoneinfo/America/Swift_Current b/testdata/zoneinfo/America/Swift_Current
new file mode 100644
index 0000000..bdbb494
Binary files /dev/null and b/testdata/zoneinfo/America/Swift_Current differ
diff --git a/testdata/zoneinfo/America/Tegucigalpa b/testdata/zoneinfo/America/Tegucigalpa
new file mode 100644
index 0000000..38036a3
Binary files /dev/null and b/testdata/zoneinfo/America/Tegucigalpa differ
diff --git a/testdata/zoneinfo/America/Thule b/testdata/zoneinfo/America/Thule
new file mode 100644
index 0000000..f38dc56
Binary files /dev/null and b/testdata/zoneinfo/America/Thule differ
diff --git a/testdata/zoneinfo/America/Thunder_Bay b/testdata/zoneinfo/America/Thunder_Bay
new file mode 100644
index 0000000..fcb0328
Binary files /dev/null and b/testdata/zoneinfo/America/Thunder_Bay differ
diff --git a/testdata/zoneinfo/America/Tijuana b/testdata/zoneinfo/America/Tijuana
new file mode 100644
index 0000000..19ccd35
Binary files /dev/null and b/testdata/zoneinfo/America/Tijuana differ
diff --git a/testdata/zoneinfo/America/Toronto b/testdata/zoneinfo/America/Toronto
new file mode 100644
index 0000000..fe6be8e
Binary files /dev/null and b/testdata/zoneinfo/America/Toronto differ
diff --git a/testdata/zoneinfo/America/Tortola b/testdata/zoneinfo/America/Tortola
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Tortola differ
diff --git a/testdata/zoneinfo/America/Vancouver b/testdata/zoneinfo/America/Vancouver
new file mode 100644
index 0000000..c998491
Binary files /dev/null and b/testdata/zoneinfo/America/Vancouver differ
diff --git a/testdata/zoneinfo/America/Virgin b/testdata/zoneinfo/America/Virgin
new file mode 100644
index 0000000..47b4dc3
Binary files /dev/null and b/testdata/zoneinfo/America/Virgin differ
diff --git a/testdata/zoneinfo/America/Whitehorse b/testdata/zoneinfo/America/Whitehorse
new file mode 100644
index 0000000..878b6a9
Binary files /dev/null and b/testdata/zoneinfo/America/Whitehorse differ
diff --git a/testdata/zoneinfo/America/Winnipeg b/testdata/zoneinfo/America/Winnipeg
new file mode 100644
index 0000000..7e646d1
Binary files /dev/null and b/testdata/zoneinfo/America/Winnipeg differ
diff --git a/testdata/zoneinfo/America/Yakutat b/testdata/zoneinfo/America/Yakutat
new file mode 100644
index 0000000..773feba
Binary files /dev/null and b/testdata/zoneinfo/America/Yakutat differ
diff --git a/testdata/zoneinfo/America/Yellowknife b/testdata/zoneinfo/America/Yellowknife
new file mode 100644
index 0000000..c779cef
Binary files /dev/null and b/testdata/zoneinfo/America/Yellowknife differ
diff --git a/testdata/zoneinfo/Antarctica/Casey b/testdata/zoneinfo/Antarctica/Casey
new file mode 100644
index 0000000..30315cc
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/Casey differ
diff --git a/testdata/zoneinfo/Antarctica/Davis b/testdata/zoneinfo/Antarctica/Davis
new file mode 100644
index 0000000..3ec3222
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/Davis differ
diff --git a/testdata/zoneinfo/Antarctica/DumontDUrville b/testdata/zoneinfo/Antarctica/DumontDUrville
new file mode 100644
index 0000000..5d8fc3a
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/DumontDUrville differ
diff --git a/testdata/zoneinfo/Antarctica/Macquarie b/testdata/zoneinfo/Antarctica/Macquarie
new file mode 100644
index 0000000..3fc1f23
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/Macquarie differ
diff --git a/testdata/zoneinfo/Antarctica/Mawson b/testdata/zoneinfo/Antarctica/Mawson
new file mode 100644
index 0000000..05e4c6c
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/Mawson differ
diff --git a/testdata/zoneinfo/Antarctica/McMurdo b/testdata/zoneinfo/Antarctica/McMurdo
new file mode 100644
index 0000000..afb3929
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/McMurdo differ
diff --git a/testdata/zoneinfo/Antarctica/Palmer b/testdata/zoneinfo/Antarctica/Palmer
new file mode 100644
index 0000000..32c1941
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/Palmer differ
diff --git a/testdata/zoneinfo/Antarctica/Rothera b/testdata/zoneinfo/Antarctica/Rothera
new file mode 100644
index 0000000..ea49c00
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/Rothera differ
diff --git a/testdata/zoneinfo/Antarctica/South_Pole b/testdata/zoneinfo/Antarctica/South_Pole
new file mode 100644
index 0000000..afb3929
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/South_Pole differ
diff --git a/testdata/zoneinfo/Antarctica/Syowa b/testdata/zoneinfo/Antarctica/Syowa
new file mode 100644
index 0000000..01c47cc
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/Syowa differ
diff --git a/testdata/zoneinfo/Antarctica/Troll b/testdata/zoneinfo/Antarctica/Troll
new file mode 100644
index 0000000..4e31aff
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/Troll differ
diff --git a/testdata/zoneinfo/Antarctica/Vostok b/testdata/zoneinfo/Antarctica/Vostok
new file mode 100644
index 0000000..6e32907
Binary files /dev/null and b/testdata/zoneinfo/Antarctica/Vostok differ
diff --git a/testdata/zoneinfo/Arctic/Longyearbyen b/testdata/zoneinfo/Arctic/Longyearbyen
new file mode 100644
index 0000000..dfc5095
Binary files /dev/null and b/testdata/zoneinfo/Arctic/Longyearbyen differ
diff --git a/testdata/zoneinfo/Asia/Aden b/testdata/zoneinfo/Asia/Aden
new file mode 100644
index 0000000..01c47cc
Binary files /dev/null and b/testdata/zoneinfo/Asia/Aden differ
diff --git a/testdata/zoneinfo/Asia/Almaty b/testdata/zoneinfo/Asia/Almaty
new file mode 100644
index 0000000..3ec4fc8
Binary files /dev/null and b/testdata/zoneinfo/Asia/Almaty differ
diff --git a/testdata/zoneinfo/Asia/Amman b/testdata/zoneinfo/Asia/Amman
new file mode 100644
index 0000000..d97d308
Binary files /dev/null and b/testdata/zoneinfo/Asia/Amman differ
diff --git a/testdata/zoneinfo/Asia/Anadyr b/testdata/zoneinfo/Asia/Anadyr
new file mode 100644
index 0000000..551884d
Binary files /dev/null and b/testdata/zoneinfo/Asia/Anadyr differ
diff --git a/testdata/zoneinfo/Asia/Aqtau b/testdata/zoneinfo/Asia/Aqtau
new file mode 100644
index 0000000..3a40d11
Binary files /dev/null and b/testdata/zoneinfo/Asia/Aqtau differ
diff --git a/testdata/zoneinfo/Asia/Aqtobe b/testdata/zoneinfo/Asia/Aqtobe
new file mode 100644
index 0000000..62c5840
Binary files /dev/null and b/testdata/zoneinfo/Asia/Aqtobe differ
diff --git a/testdata/zoneinfo/Asia/Ashgabat b/testdata/zoneinfo/Asia/Ashgabat
new file mode 100644
index 0000000..8482167
Binary files /dev/null and b/testdata/zoneinfo/Asia/Ashgabat differ
diff --git a/testdata/zoneinfo/Asia/Ashkhabad b/testdata/zoneinfo/Asia/Ashkhabad
new file mode 100644
index 0000000..8482167
Binary files /dev/null and b/testdata/zoneinfo/Asia/Ashkhabad differ
diff --git a/testdata/zoneinfo/Asia/Atyrau b/testdata/zoneinfo/Asia/Atyrau
new file mode 100644
index 0000000..cb2c82f
Binary files /dev/null and b/testdata/zoneinfo/Asia/Atyrau differ
diff --git a/testdata/zoneinfo/Asia/Baghdad b/testdata/zoneinfo/Asia/Baghdad
new file mode 100644
index 0000000..a3ce975
Binary files /dev/null and b/testdata/zoneinfo/Asia/Baghdad differ
diff --git a/testdata/zoneinfo/Asia/Bahrain b/testdata/zoneinfo/Asia/Bahrain
new file mode 100644
index 0000000..7409d74
Binary files /dev/null and b/testdata/zoneinfo/Asia/Bahrain differ
diff --git a/testdata/zoneinfo/Asia/Baku b/testdata/zoneinfo/Asia/Baku
new file mode 100644
index 0000000..96203d7
Binary files /dev/null and b/testdata/zoneinfo/Asia/Baku differ
diff --git a/testdata/zoneinfo/Asia/Bangkok b/testdata/zoneinfo/Asia/Bangkok
new file mode 100644
index 0000000..ed687d2
Binary files /dev/null and b/testdata/zoneinfo/Asia/Bangkok differ
diff --git a/testdata/zoneinfo/Asia/Barnaul b/testdata/zoneinfo/Asia/Barnaul
new file mode 100644
index 0000000..ff976dd
Binary files /dev/null and b/testdata/zoneinfo/Asia/Barnaul differ
diff --git a/testdata/zoneinfo/Asia/Beirut b/testdata/zoneinfo/Asia/Beirut
new file mode 100644
index 0000000..55dce57
Binary files /dev/null and b/testdata/zoneinfo/Asia/Beirut differ
diff --git a/testdata/zoneinfo/Asia/Bishkek b/testdata/zoneinfo/Asia/Bishkek
new file mode 100644
index 0000000..fe7832c
Binary files /dev/null and b/testdata/zoneinfo/Asia/Bishkek differ
diff --git a/testdata/zoneinfo/Asia/Brunei b/testdata/zoneinfo/Asia/Brunei
new file mode 100644
index 0000000..e67b411
Binary files /dev/null and b/testdata/zoneinfo/Asia/Brunei differ
diff --git a/testdata/zoneinfo/Asia/Calcutta b/testdata/zoneinfo/Asia/Calcutta
new file mode 100644
index 0000000..00bc80a
Binary files /dev/null and b/testdata/zoneinfo/Asia/Calcutta differ
diff --git a/testdata/zoneinfo/Asia/Chita b/testdata/zoneinfo/Asia/Chita
new file mode 100644
index 0000000..9d49cd3
Binary files /dev/null and b/testdata/zoneinfo/Asia/Chita differ
diff --git a/testdata/zoneinfo/Asia/Choibalsan b/testdata/zoneinfo/Asia/Choibalsan
new file mode 100644
index 0000000..0a948c2
Binary files /dev/null and b/testdata/zoneinfo/Asia/Choibalsan differ
diff --git a/testdata/zoneinfo/Asia/Chongqing b/testdata/zoneinfo/Asia/Chongqing
new file mode 100644
index 0000000..d6b6698
Binary files /dev/null and b/testdata/zoneinfo/Asia/Chongqing differ
diff --git a/testdata/zoneinfo/Asia/Chungking b/testdata/zoneinfo/Asia/Chungking
new file mode 100644
index 0000000..d6b6698
Binary files /dev/null and b/testdata/zoneinfo/Asia/Chungking differ
diff --git a/testdata/zoneinfo/Asia/Colombo b/testdata/zoneinfo/Asia/Colombo
new file mode 100644
index 0000000..3eeb1b7
Binary files /dev/null and b/testdata/zoneinfo/Asia/Colombo differ
diff --git a/testdata/zoneinfo/Asia/Dacca b/testdata/zoneinfo/Asia/Dacca
new file mode 100644
index 0000000..2813680
Binary files /dev/null and b/testdata/zoneinfo/Asia/Dacca differ
diff --git a/testdata/zoneinfo/Asia/Damascus b/testdata/zoneinfo/Asia/Damascus
new file mode 100644
index 0000000..168ef9b
Binary files /dev/null and b/testdata/zoneinfo/Asia/Damascus differ
diff --git a/testdata/zoneinfo/Asia/Dhaka b/testdata/zoneinfo/Asia/Dhaka
new file mode 100644
index 0000000..2813680
Binary files /dev/null and b/testdata/zoneinfo/Asia/Dhaka differ
diff --git a/testdata/zoneinfo/Asia/Dili b/testdata/zoneinfo/Asia/Dili
new file mode 100644
index 0000000..bb7be9f
Binary files /dev/null and b/testdata/zoneinfo/Asia/Dili differ
diff --git a/testdata/zoneinfo/Asia/Dubai b/testdata/zoneinfo/Asia/Dubai
new file mode 100644
index 0000000..58d75bc
Binary files /dev/null and b/testdata/zoneinfo/Asia/Dubai differ
diff --git a/testdata/zoneinfo/Asia/Dushanbe b/testdata/zoneinfo/Asia/Dushanbe
new file mode 100644
index 0000000..d83fb07
Binary files /dev/null and b/testdata/zoneinfo/Asia/Dushanbe differ
diff --git a/testdata/zoneinfo/Asia/Famagusta b/testdata/zoneinfo/Asia/Famagusta
new file mode 100644
index 0000000..cc44179
Binary files /dev/null and b/testdata/zoneinfo/Asia/Famagusta differ
diff --git a/testdata/zoneinfo/Asia/Gaza b/testdata/zoneinfo/Asia/Gaza
new file mode 100644
index 0000000..effc4df
Binary files /dev/null and b/testdata/zoneinfo/Asia/Gaza differ
diff --git a/testdata/zoneinfo/Asia/Harbin b/testdata/zoneinfo/Asia/Harbin
new file mode 100644
index 0000000..d6b6698
Binary files /dev/null and b/testdata/zoneinfo/Asia/Harbin differ
diff --git a/testdata/zoneinfo/Asia/Hebron b/testdata/zoneinfo/Asia/Hebron
new file mode 100644
index 0000000..aa52bd2
Binary files /dev/null and b/testdata/zoneinfo/Asia/Hebron differ
diff --git a/testdata/zoneinfo/Asia/Ho_Chi_Minh b/testdata/zoneinfo/Asia/Ho_Chi_Minh
new file mode 100644
index 0000000..7ca9972
Binary files /dev/null and b/testdata/zoneinfo/Asia/Ho_Chi_Minh differ
diff --git a/testdata/zoneinfo/Asia/Hong_Kong b/testdata/zoneinfo/Asia/Hong_Kong
new file mode 100644
index 0000000..c80e364
Binary files /dev/null and b/testdata/zoneinfo/Asia/Hong_Kong differ
diff --git a/testdata/zoneinfo/Asia/Hovd b/testdata/zoneinfo/Asia/Hovd
new file mode 100644
index 0000000..6e08a26
Binary files /dev/null and b/testdata/zoneinfo/Asia/Hovd differ
diff --git a/testdata/zoneinfo/Asia/Irkutsk b/testdata/zoneinfo/Asia/Irkutsk
new file mode 100644
index 0000000..550e2a0
Binary files /dev/null and b/testdata/zoneinfo/Asia/Irkutsk differ
diff --git a/testdata/zoneinfo/Asia/Istanbul b/testdata/zoneinfo/Asia/Istanbul
new file mode 100644
index 0000000..c891866
Binary files /dev/null and b/testdata/zoneinfo/Asia/Istanbul differ
diff --git a/testdata/zoneinfo/Asia/Jakarta b/testdata/zoneinfo/Asia/Jakarta
new file mode 100644
index 0000000..c9752d2
Binary files /dev/null and b/testdata/zoneinfo/Asia/Jakarta differ
diff --git a/testdata/zoneinfo/Asia/Jayapura b/testdata/zoneinfo/Asia/Jayapura
new file mode 100644
index 0000000..7c22f53
Binary files /dev/null and b/testdata/zoneinfo/Asia/Jayapura differ
diff --git a/testdata/zoneinfo/Asia/Jerusalem b/testdata/zoneinfo/Asia/Jerusalem
new file mode 100644
index 0000000..4c49bbf
Binary files /dev/null and b/testdata/zoneinfo/Asia/Jerusalem differ
diff --git a/testdata/zoneinfo/Asia/Kabul b/testdata/zoneinfo/Asia/Kabul
new file mode 100644
index 0000000..660ce4c
Binary files /dev/null and b/testdata/zoneinfo/Asia/Kabul differ
diff --git a/testdata/zoneinfo/Asia/Kamchatka b/testdata/zoneinfo/Asia/Kamchatka
new file mode 100644
index 0000000..c651554
Binary files /dev/null and b/testdata/zoneinfo/Asia/Kamchatka differ
diff --git a/testdata/zoneinfo/Asia/Karachi b/testdata/zoneinfo/Asia/Karachi
new file mode 100644
index 0000000..e56d5af
Binary files /dev/null and b/testdata/zoneinfo/Asia/Karachi differ
diff --git a/testdata/zoneinfo/Asia/Kashgar b/testdata/zoneinfo/Asia/Kashgar
new file mode 100644
index 0000000..69ff7f6
Binary files /dev/null and b/testdata/zoneinfo/Asia/Kashgar differ
diff --git a/testdata/zoneinfo/Asia/Kathmandu b/testdata/zoneinfo/Asia/Kathmandu
new file mode 100644
index 0000000..3a0d330
Binary files /dev/null and b/testdata/zoneinfo/Asia/Kathmandu differ
diff --git a/testdata/zoneinfo/Asia/Katmandu b/testdata/zoneinfo/Asia/Katmandu
new file mode 100644
index 0000000..3a0d330
Binary files /dev/null and b/testdata/zoneinfo/Asia/Katmandu differ
diff --git a/testdata/zoneinfo/Asia/Khandyga b/testdata/zoneinfo/Asia/Khandyga
new file mode 100644
index 0000000..aeb7332
Binary files /dev/null and b/testdata/zoneinfo/Asia/Khandyga differ
diff --git a/testdata/zoneinfo/Asia/Kolkata b/testdata/zoneinfo/Asia/Kolkata
new file mode 100644
index 0000000..00bc80a
Binary files /dev/null and b/testdata/zoneinfo/Asia/Kolkata differ
diff --git a/testdata/zoneinfo/Asia/Krasnoyarsk b/testdata/zoneinfo/Asia/Krasnoyarsk
new file mode 100644
index 0000000..e0d4fcb
Binary files /dev/null and b/testdata/zoneinfo/Asia/Krasnoyarsk differ
diff --git a/testdata/zoneinfo/Asia/Kuala_Lumpur b/testdata/zoneinfo/Asia/Kuala_Lumpur
new file mode 100644
index 0000000..e93dd51
Binary files /dev/null and b/testdata/zoneinfo/Asia/Kuala_Lumpur differ
diff --git a/testdata/zoneinfo/Asia/Kuching b/testdata/zoneinfo/Asia/Kuching
new file mode 100644
index 0000000..59bc6e4
Binary files /dev/null and b/testdata/zoneinfo/Asia/Kuching differ
diff --git a/testdata/zoneinfo/Asia/Kuwait b/testdata/zoneinfo/Asia/Kuwait
new file mode 100644
index 0000000..01c47cc
Binary files /dev/null and b/testdata/zoneinfo/Asia/Kuwait differ
diff --git a/testdata/zoneinfo/Asia/Macao b/testdata/zoneinfo/Asia/Macao
new file mode 100644
index 0000000..c22f75e
Binary files /dev/null and b/testdata/zoneinfo/Asia/Macao differ
diff --git a/testdata/zoneinfo/Asia/Macau b/testdata/zoneinfo/Asia/Macau
new file mode 100644
index 0000000..c22f75e
Binary files /dev/null and b/testdata/zoneinfo/Asia/Macau differ
diff --git a/testdata/zoneinfo/Asia/Magadan b/testdata/zoneinfo/Asia/Magadan
new file mode 100644
index 0000000..16bac84
Binary files /dev/null and b/testdata/zoneinfo/Asia/Magadan differ
diff --git a/testdata/zoneinfo/Asia/Makassar b/testdata/zoneinfo/Asia/Makassar
new file mode 100644
index 0000000..5990010
Binary files /dev/null and b/testdata/zoneinfo/Asia/Makassar differ
diff --git a/testdata/zoneinfo/Asia/Manila b/testdata/zoneinfo/Asia/Manila
new file mode 100644
index 0000000..3c3584e
Binary files /dev/null and b/testdata/zoneinfo/Asia/Manila differ
diff --git a/testdata/zoneinfo/Asia/Muscat b/testdata/zoneinfo/Asia/Muscat
new file mode 100644
index 0000000..58d75bc
Binary files /dev/null and b/testdata/zoneinfo/Asia/Muscat differ
diff --git a/testdata/zoneinfo/Asia/Nicosia b/testdata/zoneinfo/Asia/Nicosia
new file mode 100644
index 0000000..c210d0a
Binary files /dev/null and b/testdata/zoneinfo/Asia/Nicosia differ
diff --git a/testdata/zoneinfo/Asia/Novokuznetsk b/testdata/zoneinfo/Asia/Novokuznetsk
new file mode 100644
index 0000000..9378d50
Binary files /dev/null and b/testdata/zoneinfo/Asia/Novokuznetsk differ
diff --git a/testdata/zoneinfo/Asia/Novosibirsk b/testdata/zoneinfo/Asia/Novosibirsk
new file mode 100644
index 0000000..65a9fa2
Binary files /dev/null and b/testdata/zoneinfo/Asia/Novosibirsk differ
diff --git a/testdata/zoneinfo/Asia/Omsk b/testdata/zoneinfo/Asia/Omsk
new file mode 100644
index 0000000..dc0ed42
Binary files /dev/null and b/testdata/zoneinfo/Asia/Omsk differ
diff --git a/testdata/zoneinfo/Asia/Oral b/testdata/zoneinfo/Asia/Oral
new file mode 100644
index 0000000..25a63ec
Binary files /dev/null and b/testdata/zoneinfo/Asia/Oral differ
diff --git a/testdata/zoneinfo/Asia/Phnom_Penh b/testdata/zoneinfo/Asia/Phnom_Penh
new file mode 100644
index 0000000..ed687d2
Binary files /dev/null and b/testdata/zoneinfo/Asia/Phnom_Penh differ
diff --git a/testdata/zoneinfo/Asia/Pontianak b/testdata/zoneinfo/Asia/Pontianak
new file mode 100644
index 0000000..285bed2
Binary files /dev/null and b/testdata/zoneinfo/Asia/Pontianak differ
diff --git a/testdata/zoneinfo/Asia/Pyongyang b/testdata/zoneinfo/Asia/Pyongyang
new file mode 100644
index 0000000..57240cf
Binary files /dev/null and b/testdata/zoneinfo/Asia/Pyongyang differ
diff --git a/testdata/zoneinfo/Asia/Qatar b/testdata/zoneinfo/Asia/Qatar
new file mode 100644
index 0000000..7409d74
Binary files /dev/null and b/testdata/zoneinfo/Asia/Qatar differ
diff --git a/testdata/zoneinfo/Asia/Qostanay b/testdata/zoneinfo/Asia/Qostanay
new file mode 100644
index 0000000..ff6fe61
Binary files /dev/null and b/testdata/zoneinfo/Asia/Qostanay differ
diff --git a/testdata/zoneinfo/Asia/Qyzylorda b/testdata/zoneinfo/Asia/Qyzylorda
new file mode 100644
index 0000000..fe4d6c6
Binary files /dev/null and b/testdata/zoneinfo/Asia/Qyzylorda differ
diff --git a/testdata/zoneinfo/Asia/Rangoon b/testdata/zoneinfo/Asia/Rangoon
new file mode 100644
index 0000000..14b2ad0
Binary files /dev/null and b/testdata/zoneinfo/Asia/Rangoon differ
diff --git a/testdata/zoneinfo/Asia/Riyadh b/testdata/zoneinfo/Asia/Riyadh
new file mode 100644
index 0000000..01c47cc
Binary files /dev/null and b/testdata/zoneinfo/Asia/Riyadh differ
diff --git a/testdata/zoneinfo/Asia/Saigon b/testdata/zoneinfo/Asia/Saigon
new file mode 100644
index 0000000..7ca9972
Binary files /dev/null and b/testdata/zoneinfo/Asia/Saigon differ
diff --git a/testdata/zoneinfo/Asia/Sakhalin b/testdata/zoneinfo/Asia/Sakhalin
new file mode 100644
index 0000000..69f0faa
Binary files /dev/null and b/testdata/zoneinfo/Asia/Sakhalin differ
diff --git a/testdata/zoneinfo/Asia/Samarkand b/testdata/zoneinfo/Asia/Samarkand
new file mode 100644
index 0000000..c43e27c
Binary files /dev/null and b/testdata/zoneinfo/Asia/Samarkand differ
diff --git a/testdata/zoneinfo/Asia/Seoul b/testdata/zoneinfo/Asia/Seoul
new file mode 100644
index 0000000..1755147
Binary files /dev/null and b/testdata/zoneinfo/Asia/Seoul differ
diff --git a/testdata/zoneinfo/Asia/Shanghai b/testdata/zoneinfo/Asia/Shanghai
new file mode 100644
index 0000000..d6b6698
Binary files /dev/null and b/testdata/zoneinfo/Asia/Shanghai differ
diff --git a/testdata/zoneinfo/Asia/Singapore b/testdata/zoneinfo/Asia/Singapore
new file mode 100644
index 0000000..350d77e
Binary files /dev/null and b/testdata/zoneinfo/Asia/Singapore differ
diff --git a/testdata/zoneinfo/Asia/Srednekolymsk b/testdata/zoneinfo/Asia/Srednekolymsk
new file mode 100644
index 0000000..7fdee5c
Binary files /dev/null and b/testdata/zoneinfo/Asia/Srednekolymsk differ
diff --git a/testdata/zoneinfo/Asia/Taipei b/testdata/zoneinfo/Asia/Taipei
new file mode 100644
index 0000000..35d89d0
Binary files /dev/null and b/testdata/zoneinfo/Asia/Taipei differ
diff --git a/testdata/zoneinfo/Asia/Tashkent b/testdata/zoneinfo/Asia/Tashkent
new file mode 100644
index 0000000..65ee428
Binary files /dev/null and b/testdata/zoneinfo/Asia/Tashkent differ
diff --git a/testdata/zoneinfo/Asia/Tbilisi b/testdata/zoneinfo/Asia/Tbilisi
new file mode 100644
index 0000000..166e434
Binary files /dev/null and b/testdata/zoneinfo/Asia/Tbilisi differ
diff --git a/testdata/zoneinfo/Asia/Tehran b/testdata/zoneinfo/Asia/Tehran
new file mode 100644
index 0000000..f1555f0
Binary files /dev/null and b/testdata/zoneinfo/Asia/Tehran differ
diff --git a/testdata/zoneinfo/Asia/Tel_Aviv b/testdata/zoneinfo/Asia/Tel_Aviv
new file mode 100644
index 0000000..4c49bbf
Binary files /dev/null and b/testdata/zoneinfo/Asia/Tel_Aviv differ
diff --git a/testdata/zoneinfo/Asia/Thimbu b/testdata/zoneinfo/Asia/Thimbu
new file mode 100644
index 0000000..0edc72c
Binary files /dev/null and b/testdata/zoneinfo/Asia/Thimbu differ
diff --git a/testdata/zoneinfo/Asia/Thimphu b/testdata/zoneinfo/Asia/Thimphu
new file mode 100644
index 0000000..0edc72c
Binary files /dev/null and b/testdata/zoneinfo/Asia/Thimphu differ
diff --git a/testdata/zoneinfo/Asia/Tokyo b/testdata/zoneinfo/Asia/Tokyo
new file mode 100644
index 0000000..1aa066c
Binary files /dev/null and b/testdata/zoneinfo/Asia/Tokyo differ
diff --git a/testdata/zoneinfo/Asia/Tomsk b/testdata/zoneinfo/Asia/Tomsk
new file mode 100644
index 0000000..c3c307d
Binary files /dev/null and b/testdata/zoneinfo/Asia/Tomsk differ
diff --git a/testdata/zoneinfo/Asia/Ujung_Pandang b/testdata/zoneinfo/Asia/Ujung_Pandang
new file mode 100644
index 0000000..5990010
Binary files /dev/null and b/testdata/zoneinfo/Asia/Ujung_Pandang differ
diff --git a/testdata/zoneinfo/Asia/Ulaanbaatar b/testdata/zoneinfo/Asia/Ulaanbaatar
new file mode 100644
index 0000000..6f5d3a1
Binary files /dev/null and b/testdata/zoneinfo/Asia/Ulaanbaatar differ
diff --git a/testdata/zoneinfo/Asia/Ulan_Bator b/testdata/zoneinfo/Asia/Ulan_Bator
new file mode 100644
index 0000000..6f5d3a1
Binary files /dev/null and b/testdata/zoneinfo/Asia/Ulan_Bator differ
diff --git a/testdata/zoneinfo/Asia/Urumqi b/testdata/zoneinfo/Asia/Urumqi
new file mode 100644
index 0000000..69ff7f6
Binary files /dev/null and b/testdata/zoneinfo/Asia/Urumqi differ
diff --git a/testdata/zoneinfo/Asia/Ust-Nera b/testdata/zoneinfo/Asia/Ust-Nera
new file mode 100644
index 0000000..c39331e
Binary files /dev/null and b/testdata/zoneinfo/Asia/Ust-Nera differ
diff --git a/testdata/zoneinfo/Asia/Vientiane b/testdata/zoneinfo/Asia/Vientiane
new file mode 100644
index 0000000..ed687d2
Binary files /dev/null and b/testdata/zoneinfo/Asia/Vientiane differ
diff --git a/testdata/zoneinfo/Asia/Vladivostok b/testdata/zoneinfo/Asia/Vladivostok
new file mode 100644
index 0000000..72a3d4e
Binary files /dev/null and b/testdata/zoneinfo/Asia/Vladivostok differ
diff --git a/testdata/zoneinfo/Asia/Yakutsk b/testdata/zoneinfo/Asia/Yakutsk
new file mode 100644
index 0000000..336f932
Binary files /dev/null and b/testdata/zoneinfo/Asia/Yakutsk differ
diff --git a/testdata/zoneinfo/Asia/Yangon b/testdata/zoneinfo/Asia/Yangon
new file mode 100644
index 0000000..14b2ad0
Binary files /dev/null and b/testdata/zoneinfo/Asia/Yangon differ
diff --git a/testdata/zoneinfo/Asia/Yekaterinburg b/testdata/zoneinfo/Asia/Yekaterinburg
new file mode 100644
index 0000000..a3bf7f2
Binary files /dev/null and b/testdata/zoneinfo/Asia/Yekaterinburg differ
diff --git a/testdata/zoneinfo/Asia/Yerevan b/testdata/zoneinfo/Asia/Yerevan
new file mode 100644
index 0000000..6dd927c
Binary files /dev/null and b/testdata/zoneinfo/Asia/Yerevan differ
diff --git a/testdata/zoneinfo/Atlantic/Azores b/testdata/zoneinfo/Atlantic/Azores
new file mode 100644
index 0000000..e6e2616
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Azores differ
diff --git a/testdata/zoneinfo/Atlantic/Bermuda b/testdata/zoneinfo/Atlantic/Bermuda
new file mode 100644
index 0000000..abc75ea
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Bermuda differ
diff --git a/testdata/zoneinfo/Atlantic/Canary b/testdata/zoneinfo/Atlantic/Canary
new file mode 100644
index 0000000..5ab3243
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Canary differ
diff --git a/testdata/zoneinfo/Atlantic/Cape_Verde b/testdata/zoneinfo/Atlantic/Cape_Verde
new file mode 100644
index 0000000..8f7de1c
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Cape_Verde differ
diff --git a/testdata/zoneinfo/Atlantic/Faeroe b/testdata/zoneinfo/Atlantic/Faeroe
new file mode 100644
index 0000000..9558bf7
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Faeroe differ
diff --git a/testdata/zoneinfo/Atlantic/Faroe b/testdata/zoneinfo/Atlantic/Faroe
new file mode 100644
index 0000000..9558bf7
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Faroe differ
diff --git a/testdata/zoneinfo/Atlantic/Jan_Mayen b/testdata/zoneinfo/Atlantic/Jan_Mayen
new file mode 100644
index 0000000..dfc5095
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Jan_Mayen differ
diff --git a/testdata/zoneinfo/Atlantic/Madeira b/testdata/zoneinfo/Atlantic/Madeira
new file mode 100644
index 0000000..cf965c3
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Madeira differ
diff --git a/testdata/zoneinfo/Atlantic/Reykjavik b/testdata/zoneinfo/Atlantic/Reykjavik
new file mode 100644
index 0000000..2451aca
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Reykjavik differ
diff --git a/testdata/zoneinfo/Atlantic/South_Georgia b/testdata/zoneinfo/Atlantic/South_Georgia
new file mode 100644
index 0000000..7fa5f46
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/South_Georgia differ
diff --git a/testdata/zoneinfo/Atlantic/St_Helena b/testdata/zoneinfo/Atlantic/St_Helena
new file mode 100644
index 0000000..8906e88
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/St_Helena differ
diff --git a/testdata/zoneinfo/Atlantic/Stanley b/testdata/zoneinfo/Atlantic/Stanley
new file mode 100644
index 0000000..1a4c8ea
Binary files /dev/null and b/testdata/zoneinfo/Atlantic/Stanley differ
diff --git a/testdata/zoneinfo/Australia/ACT b/testdata/zoneinfo/Australia/ACT
new file mode 100644
index 0000000..1975a3a
Binary files /dev/null and b/testdata/zoneinfo/Australia/ACT differ
diff --git a/testdata/zoneinfo/Australia/Adelaide b/testdata/zoneinfo/Australia/Adelaide
new file mode 100644
index 0000000..3bfbbc5
Binary files /dev/null and b/testdata/zoneinfo/Australia/Adelaide differ
diff --git a/testdata/zoneinfo/Australia/Brisbane b/testdata/zoneinfo/Australia/Brisbane
new file mode 100644
index 0000000..dc9a980
Binary files /dev/null and b/testdata/zoneinfo/Australia/Brisbane differ
diff --git a/testdata/zoneinfo/Australia/Broken_Hill b/testdata/zoneinfo/Australia/Broken_Hill
new file mode 100644
index 0000000..947b509
Binary files /dev/null and b/testdata/zoneinfo/Australia/Broken_Hill differ
diff --git a/testdata/zoneinfo/Australia/Canberra b/testdata/zoneinfo/Australia/Canberra
new file mode 100644
index 0000000..1975a3a
Binary files /dev/null and b/testdata/zoneinfo/Australia/Canberra differ
diff --git a/testdata/zoneinfo/Australia/Currie b/testdata/zoneinfo/Australia/Currie
new file mode 100644
index 0000000..dc2ef55
Binary files /dev/null and b/testdata/zoneinfo/Australia/Currie differ
diff --git a/testdata/zoneinfo/Australia/Darwin b/testdata/zoneinfo/Australia/Darwin
new file mode 100644
index 0000000..a6a6730
Binary files /dev/null and b/testdata/zoneinfo/Australia/Darwin differ
diff --git a/testdata/zoneinfo/Australia/Eucla b/testdata/zoneinfo/Australia/Eucla
new file mode 100644
index 0000000..9080f5c
Binary files /dev/null and b/testdata/zoneinfo/Australia/Eucla differ
diff --git a/testdata/zoneinfo/Australia/Hobart b/testdata/zoneinfo/Australia/Hobart
new file mode 100644
index 0000000..dc2ef55
Binary files /dev/null and b/testdata/zoneinfo/Australia/Hobart differ
diff --git a/testdata/zoneinfo/Australia/LHI b/testdata/zoneinfo/Australia/LHI
new file mode 100644
index 0000000..4d4ec8c
Binary files /dev/null and b/testdata/zoneinfo/Australia/LHI differ
diff --git a/testdata/zoneinfo/Australia/Lindeman b/testdata/zoneinfo/Australia/Lindeman
new file mode 100644
index 0000000..131d77b
Binary files /dev/null and b/testdata/zoneinfo/Australia/Lindeman differ
diff --git a/testdata/zoneinfo/Australia/Lord_Howe b/testdata/zoneinfo/Australia/Lord_Howe
new file mode 100644
index 0000000..4d4ec8c
Binary files /dev/null and b/testdata/zoneinfo/Australia/Lord_Howe differ
diff --git a/testdata/zoneinfo/Australia/Melbourne b/testdata/zoneinfo/Australia/Melbourne
new file mode 100644
index 0000000..d3f195a
Binary files /dev/null and b/testdata/zoneinfo/Australia/Melbourne differ
diff --git a/testdata/zoneinfo/Australia/NSW b/testdata/zoneinfo/Australia/NSW
new file mode 100644
index 0000000..1975a3a
Binary files /dev/null and b/testdata/zoneinfo/Australia/NSW differ
diff --git a/testdata/zoneinfo/Australia/North b/testdata/zoneinfo/Australia/North
new file mode 100644
index 0000000..a6a6730
Binary files /dev/null and b/testdata/zoneinfo/Australia/North differ
diff --git a/testdata/zoneinfo/Australia/Perth b/testdata/zoneinfo/Australia/Perth
new file mode 100644
index 0000000..4f77182
Binary files /dev/null and b/testdata/zoneinfo/Australia/Perth differ
diff --git a/testdata/zoneinfo/Australia/Queensland b/testdata/zoneinfo/Australia/Queensland
new file mode 100644
index 0000000..dc9a980
Binary files /dev/null and b/testdata/zoneinfo/Australia/Queensland differ
diff --git a/testdata/zoneinfo/Australia/South b/testdata/zoneinfo/Australia/South
new file mode 100644
index 0000000..3bfbbc5
Binary files /dev/null and b/testdata/zoneinfo/Australia/South differ
diff --git a/testdata/zoneinfo/Australia/Sydney b/testdata/zoneinfo/Australia/Sydney
new file mode 100644
index 0000000..1975a3a
Binary files /dev/null and b/testdata/zoneinfo/Australia/Sydney differ
diff --git a/testdata/zoneinfo/Australia/Tasmania b/testdata/zoneinfo/Australia/Tasmania
new file mode 100644
index 0000000..dc2ef55
Binary files /dev/null and b/testdata/zoneinfo/Australia/Tasmania differ
diff --git a/testdata/zoneinfo/Australia/Victoria b/testdata/zoneinfo/Australia/Victoria
new file mode 100644
index 0000000..d3f195a
Binary files /dev/null and b/testdata/zoneinfo/Australia/Victoria differ
diff --git a/testdata/zoneinfo/Australia/West b/testdata/zoneinfo/Australia/West
new file mode 100644
index 0000000..4f77182
Binary files /dev/null and b/testdata/zoneinfo/Australia/West differ
diff --git a/testdata/zoneinfo/Australia/Yancowinna b/testdata/zoneinfo/Australia/Yancowinna
new file mode 100644
index 0000000..947b509
Binary files /dev/null and b/testdata/zoneinfo/Australia/Yancowinna differ
diff --git a/testdata/zoneinfo/Brazil/Acre b/testdata/zoneinfo/Brazil/Acre
new file mode 100644
index 0000000..fb5185c
Binary files /dev/null and b/testdata/zoneinfo/Brazil/Acre differ
diff --git a/testdata/zoneinfo/Brazil/DeNoronha b/testdata/zoneinfo/Brazil/DeNoronha
new file mode 100644
index 0000000..9e74745
Binary files /dev/null and b/testdata/zoneinfo/Brazil/DeNoronha differ
diff --git a/testdata/zoneinfo/Brazil/East b/testdata/zoneinfo/Brazil/East
new file mode 100644
index 0000000..a16da2c
Binary files /dev/null and b/testdata/zoneinfo/Brazil/East differ
diff --git a/testdata/zoneinfo/Brazil/West b/testdata/zoneinfo/Brazil/West
new file mode 100644
index 0000000..59c952e
Binary files /dev/null and b/testdata/zoneinfo/Brazil/West differ
diff --git a/testdata/zoneinfo/CET b/testdata/zoneinfo/CET
new file mode 100644
index 0000000..546748d
Binary files /dev/null and b/testdata/zoneinfo/CET differ
diff --git a/testdata/zoneinfo/CST6CDT b/testdata/zoneinfo/CST6CDT
new file mode 100644
index 0000000..d931558
Binary files /dev/null and b/testdata/zoneinfo/CST6CDT differ
diff --git a/testdata/zoneinfo/Canada/Atlantic b/testdata/zoneinfo/Canada/Atlantic
new file mode 100644
index 0000000..9fa850a
Binary files /dev/null and b/testdata/zoneinfo/Canada/Atlantic differ
diff --git a/testdata/zoneinfo/Canada/Central b/testdata/zoneinfo/Canada/Central
new file mode 100644
index 0000000..7e646d1
Binary files /dev/null and b/testdata/zoneinfo/Canada/Central differ
diff --git a/testdata/zoneinfo/Canada/Eastern b/testdata/zoneinfo/Canada/Eastern
new file mode 100644
index 0000000..fe6be8e
Binary files /dev/null and b/testdata/zoneinfo/Canada/Eastern differ
diff --git a/testdata/zoneinfo/Canada/Mountain b/testdata/zoneinfo/Canada/Mountain
new file mode 100644
index 0000000..645ee94
Binary files /dev/null and b/testdata/zoneinfo/Canada/Mountain differ
diff --git a/testdata/zoneinfo/Canada/Newfoundland b/testdata/zoneinfo/Canada/Newfoundland
new file mode 100644
index 0000000..e5f2aec
Binary files /dev/null and b/testdata/zoneinfo/Canada/Newfoundland differ
diff --git a/testdata/zoneinfo/Canada/Pacific b/testdata/zoneinfo/Canada/Pacific
new file mode 100644
index 0000000..c998491
Binary files /dev/null and b/testdata/zoneinfo/Canada/Pacific differ
diff --git a/testdata/zoneinfo/Canada/Saskatchewan b/testdata/zoneinfo/Canada/Saskatchewan
new file mode 100644
index 0000000..a3f8217
Binary files /dev/null and b/testdata/zoneinfo/Canada/Saskatchewan differ
diff --git a/testdata/zoneinfo/Canada/Yukon b/testdata/zoneinfo/Canada/Yukon
new file mode 100644
index 0000000..878b6a9
Binary files /dev/null and b/testdata/zoneinfo/Canada/Yukon differ
diff --git a/testdata/zoneinfo/Chile/Continental b/testdata/zoneinfo/Chile/Continental
new file mode 100644
index 0000000..cde8dbb
Binary files /dev/null and b/testdata/zoneinfo/Chile/Continental differ
diff --git a/testdata/zoneinfo/Chile/EasterIsland b/testdata/zoneinfo/Chile/EasterIsland
new file mode 100644
index 0000000..d29bcd6
Binary files /dev/null and b/testdata/zoneinfo/Chile/EasterIsland differ
diff --git a/testdata/zoneinfo/Cuba b/testdata/zoneinfo/Cuba
new file mode 100644
index 0000000..e06629d
Binary files /dev/null and b/testdata/zoneinfo/Cuba differ
diff --git a/testdata/zoneinfo/EET b/testdata/zoneinfo/EET
new file mode 100644
index 0000000..378919e
Binary files /dev/null and b/testdata/zoneinfo/EET differ
diff --git a/testdata/zoneinfo/EST b/testdata/zoneinfo/EST
new file mode 100644
index 0000000..3ae9691
Binary files /dev/null and b/testdata/zoneinfo/EST differ
diff --git a/testdata/zoneinfo/EST5EDT b/testdata/zoneinfo/EST5EDT
new file mode 100644
index 0000000..50c95e0
Binary files /dev/null and b/testdata/zoneinfo/EST5EDT differ
diff --git a/testdata/zoneinfo/Egypt b/testdata/zoneinfo/Egypt
new file mode 100644
index 0000000..ea38c97
Binary files /dev/null and b/testdata/zoneinfo/Egypt differ
diff --git a/testdata/zoneinfo/Eire b/testdata/zoneinfo/Eire
new file mode 100644
index 0000000..4a45ea8
Binary files /dev/null and b/testdata/zoneinfo/Eire differ
diff --git a/testdata/zoneinfo/Etc/GMT b/testdata/zoneinfo/Etc/GMT
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT differ
diff --git a/testdata/zoneinfo/Etc/GMT+0 b/testdata/zoneinfo/Etc/GMT+0
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+0 differ
diff --git a/testdata/zoneinfo/Etc/GMT+1 b/testdata/zoneinfo/Etc/GMT+1
new file mode 100644
index 0000000..98d5dcf
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+1 differ
diff --git a/testdata/zoneinfo/Etc/GMT+10 b/testdata/zoneinfo/Etc/GMT+10
new file mode 100644
index 0000000..ecb287e
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+10 differ
diff --git a/testdata/zoneinfo/Etc/GMT+11 b/testdata/zoneinfo/Etc/GMT+11
new file mode 100644
index 0000000..e941412
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+11 differ
diff --git a/testdata/zoneinfo/Etc/GMT+12 b/testdata/zoneinfo/Etc/GMT+12
new file mode 100644
index 0000000..9c95bd0
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+12 differ
diff --git a/testdata/zoneinfo/Etc/GMT+2 b/testdata/zoneinfo/Etc/GMT+2
new file mode 100644
index 0000000..6d5ce3d
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+2 differ
diff --git a/testdata/zoneinfo/Etc/GMT+3 b/testdata/zoneinfo/Etc/GMT+3
new file mode 100644
index 0000000..5ef7be7
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+3 differ
diff --git a/testdata/zoneinfo/Etc/GMT+4 b/testdata/zoneinfo/Etc/GMT+4
new file mode 100644
index 0000000..75f1621
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+4 differ
diff --git a/testdata/zoneinfo/Etc/GMT+5 b/testdata/zoneinfo/Etc/GMT+5
new file mode 100644
index 0000000..589990a
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+5 differ
diff --git a/testdata/zoneinfo/Etc/GMT+6 b/testdata/zoneinfo/Etc/GMT+6
new file mode 100644
index 0000000..fcb60ca
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+6 differ
diff --git a/testdata/zoneinfo/Etc/GMT+7 b/testdata/zoneinfo/Etc/GMT+7
new file mode 100644
index 0000000..c0427a4
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+7 differ
diff --git a/testdata/zoneinfo/Etc/GMT+8 b/testdata/zoneinfo/Etc/GMT+8
new file mode 100644
index 0000000..9bdc228
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+8 differ
diff --git a/testdata/zoneinfo/Etc/GMT+9 b/testdata/zoneinfo/Etc/GMT+9
new file mode 100644
index 0000000..ca7a81f
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT+9 differ
diff --git a/testdata/zoneinfo/Etc/GMT-0 b/testdata/zoneinfo/Etc/GMT-0
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-0 differ
diff --git a/testdata/zoneinfo/Etc/GMT-1 b/testdata/zoneinfo/Etc/GMT-1
new file mode 100644
index 0000000..cb45601
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-1 differ
diff --git a/testdata/zoneinfo/Etc/GMT-10 b/testdata/zoneinfo/Etc/GMT-10
new file mode 100644
index 0000000..11d988e
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-10 differ
diff --git a/testdata/zoneinfo/Etc/GMT-11 b/testdata/zoneinfo/Etc/GMT-11
new file mode 100644
index 0000000..f4c5d5c
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-11 differ
diff --git a/testdata/zoneinfo/Etc/GMT-12 b/testdata/zoneinfo/Etc/GMT-12
new file mode 100644
index 0000000..cd397b0
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-12 differ
diff --git a/testdata/zoneinfo/Etc/GMT-13 b/testdata/zoneinfo/Etc/GMT-13
new file mode 100644
index 0000000..8fad7c6
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-13 differ
diff --git a/testdata/zoneinfo/Etc/GMT-14 b/testdata/zoneinfo/Etc/GMT-14
new file mode 100644
index 0000000..a595e60
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-14 differ
diff --git a/testdata/zoneinfo/Etc/GMT-2 b/testdata/zoneinfo/Etc/GMT-2
new file mode 100644
index 0000000..97b44a9
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-2 differ
diff --git a/testdata/zoneinfo/Etc/GMT-3 b/testdata/zoneinfo/Etc/GMT-3
new file mode 100644
index 0000000..4eb17ff
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-3 differ
diff --git a/testdata/zoneinfo/Etc/GMT-4 b/testdata/zoneinfo/Etc/GMT-4
new file mode 100644
index 0000000..13aef80
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-4 differ
diff --git a/testdata/zoneinfo/Etc/GMT-5 b/testdata/zoneinfo/Etc/GMT-5
new file mode 100644
index 0000000..83a2816
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-5 differ
diff --git a/testdata/zoneinfo/Etc/GMT-6 b/testdata/zoneinfo/Etc/GMT-6
new file mode 100644
index 0000000..79a983e
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-6 differ
diff --git a/testdata/zoneinfo/Etc/GMT-7 b/testdata/zoneinfo/Etc/GMT-7
new file mode 100644
index 0000000..e136690
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-7 differ
diff --git a/testdata/zoneinfo/Etc/GMT-8 b/testdata/zoneinfo/Etc/GMT-8
new file mode 100644
index 0000000..bc70fe4
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-8 differ
diff --git a/testdata/zoneinfo/Etc/GMT-9 b/testdata/zoneinfo/Etc/GMT-9
new file mode 100644
index 0000000..d18cedd
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT-9 differ
diff --git a/testdata/zoneinfo/Etc/GMT0 b/testdata/zoneinfo/Etc/GMT0
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/Etc/GMT0 differ
diff --git a/testdata/zoneinfo/Etc/Greenwich b/testdata/zoneinfo/Etc/Greenwich
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/Etc/Greenwich differ
diff --git a/testdata/zoneinfo/Etc/UCT b/testdata/zoneinfo/Etc/UCT
new file mode 100644
index 0000000..00841a6
Binary files /dev/null and b/testdata/zoneinfo/Etc/UCT differ
diff --git a/testdata/zoneinfo/Etc/UTC b/testdata/zoneinfo/Etc/UTC
new file mode 100644
index 0000000..00841a6
Binary files /dev/null and b/testdata/zoneinfo/Etc/UTC differ
diff --git a/testdata/zoneinfo/Etc/Universal b/testdata/zoneinfo/Etc/Universal
new file mode 100644
index 0000000..00841a6
Binary files /dev/null and b/testdata/zoneinfo/Etc/Universal differ
diff --git a/testdata/zoneinfo/Etc/Zulu b/testdata/zoneinfo/Etc/Zulu
new file mode 100644
index 0000000..00841a6
Binary files /dev/null and b/testdata/zoneinfo/Etc/Zulu differ
diff --git a/testdata/zoneinfo/Europe/Amsterdam b/testdata/zoneinfo/Europe/Amsterdam
new file mode 100644
index 0000000..4a6fa1d
Binary files /dev/null and b/testdata/zoneinfo/Europe/Amsterdam differ
diff --git a/testdata/zoneinfo/Europe/Andorra b/testdata/zoneinfo/Europe/Andorra
new file mode 100644
index 0000000..38685d4
Binary files /dev/null and b/testdata/zoneinfo/Europe/Andorra differ
diff --git a/testdata/zoneinfo/Europe/Astrakhan b/testdata/zoneinfo/Europe/Astrakhan
new file mode 100644
index 0000000..aff8d82
Binary files /dev/null and b/testdata/zoneinfo/Europe/Astrakhan differ
diff --git a/testdata/zoneinfo/Europe/Athens b/testdata/zoneinfo/Europe/Athens
new file mode 100644
index 0000000..231bf9c
Binary files /dev/null and b/testdata/zoneinfo/Europe/Athens differ
diff --git a/testdata/zoneinfo/Europe/Belfast b/testdata/zoneinfo/Europe/Belfast
new file mode 100644
index 0000000..323cd38
Binary files /dev/null and b/testdata/zoneinfo/Europe/Belfast differ
diff --git a/testdata/zoneinfo/Europe/Belgrade b/testdata/zoneinfo/Europe/Belgrade
new file mode 100644
index 0000000..a1bf928
Binary files /dev/null and b/testdata/zoneinfo/Europe/Belgrade differ
diff --git a/testdata/zoneinfo/Europe/Berlin b/testdata/zoneinfo/Europe/Berlin
new file mode 100644
index 0000000..465546b
Binary files /dev/null and b/testdata/zoneinfo/Europe/Berlin differ
diff --git a/testdata/zoneinfo/Europe/Bratislava b/testdata/zoneinfo/Europe/Bratislava
new file mode 100644
index 0000000..fb7c145
Binary files /dev/null and b/testdata/zoneinfo/Europe/Bratislava differ
diff --git a/testdata/zoneinfo/Europe/Brussels b/testdata/zoneinfo/Europe/Brussels
new file mode 100644
index 0000000..3197327
Binary files /dev/null and b/testdata/zoneinfo/Europe/Brussels differ
diff --git a/testdata/zoneinfo/Europe/Bucharest b/testdata/zoneinfo/Europe/Bucharest
new file mode 100644
index 0000000..efa689b
Binary files /dev/null and b/testdata/zoneinfo/Europe/Bucharest differ
diff --git a/testdata/zoneinfo/Europe/Budapest b/testdata/zoneinfo/Europe/Budapest
new file mode 100644
index 0000000..940be46
Binary files /dev/null and b/testdata/zoneinfo/Europe/Budapest differ
diff --git a/testdata/zoneinfo/Europe/Busingen b/testdata/zoneinfo/Europe/Busingen
new file mode 100644
index 0000000..388df29
Binary files /dev/null and b/testdata/zoneinfo/Europe/Busingen differ
diff --git a/testdata/zoneinfo/Europe/Chisinau b/testdata/zoneinfo/Europe/Chisinau
new file mode 100644
index 0000000..6970b14
Binary files /dev/null and b/testdata/zoneinfo/Europe/Chisinau differ
diff --git a/testdata/zoneinfo/Europe/Copenhagen b/testdata/zoneinfo/Europe/Copenhagen
new file mode 100644
index 0000000..45984a7
Binary files /dev/null and b/testdata/zoneinfo/Europe/Copenhagen differ
diff --git a/testdata/zoneinfo/Europe/Dublin b/testdata/zoneinfo/Europe/Dublin
new file mode 100644
index 0000000..4a45ea8
Binary files /dev/null and b/testdata/zoneinfo/Europe/Dublin differ
diff --git a/testdata/zoneinfo/Europe/Gibraltar b/testdata/zoneinfo/Europe/Gibraltar
new file mode 100644
index 0000000..017bb2e
Binary files /dev/null and b/testdata/zoneinfo/Europe/Gibraltar differ
diff --git a/testdata/zoneinfo/Europe/Guernsey b/testdata/zoneinfo/Europe/Guernsey
new file mode 100644
index 0000000..323cd38
Binary files /dev/null and b/testdata/zoneinfo/Europe/Guernsey differ
diff --git a/testdata/zoneinfo/Europe/Helsinki b/testdata/zoneinfo/Europe/Helsinki
new file mode 100644
index 0000000..ff5e565
Binary files /dev/null and b/testdata/zoneinfo/Europe/Helsinki differ
diff --git a/testdata/zoneinfo/Europe/Isle_of_Man b/testdata/zoneinfo/Europe/Isle_of_Man
new file mode 100644
index 0000000..323cd38
Binary files /dev/null and b/testdata/zoneinfo/Europe/Isle_of_Man differ
diff --git a/testdata/zoneinfo/Europe/Istanbul b/testdata/zoneinfo/Europe/Istanbul
new file mode 100644
index 0000000..c891866
Binary files /dev/null and b/testdata/zoneinfo/Europe/Istanbul differ
diff --git a/testdata/zoneinfo/Europe/Jersey b/testdata/zoneinfo/Europe/Jersey
new file mode 100644
index 0000000..323cd38
Binary files /dev/null and b/testdata/zoneinfo/Europe/Jersey differ
diff --git a/testdata/zoneinfo/Europe/Kaliningrad b/testdata/zoneinfo/Europe/Kaliningrad
new file mode 100644
index 0000000..0ec4756
Binary files /dev/null and b/testdata/zoneinfo/Europe/Kaliningrad differ
diff --git a/testdata/zoneinfo/Europe/Kiev b/testdata/zoneinfo/Europe/Kiev
new file mode 100644
index 0000000..4e02685
Binary files /dev/null and b/testdata/zoneinfo/Europe/Kiev differ
diff --git a/testdata/zoneinfo/Europe/Kirov b/testdata/zoneinfo/Europe/Kirov
new file mode 100644
index 0000000..d1c93c5
Binary files /dev/null and b/testdata/zoneinfo/Europe/Kirov differ
diff --git a/testdata/zoneinfo/Europe/Lisbon b/testdata/zoneinfo/Europe/Lisbon
new file mode 100644
index 0000000..f0c70b6
Binary files /dev/null and b/testdata/zoneinfo/Europe/Lisbon differ
diff --git a/testdata/zoneinfo/Europe/Ljubljana b/testdata/zoneinfo/Europe/Ljubljana
new file mode 100644
index 0000000..a1bf928
Binary files /dev/null and b/testdata/zoneinfo/Europe/Ljubljana differ
diff --git a/testdata/zoneinfo/Europe/London b/testdata/zoneinfo/Europe/London
new file mode 100644
index 0000000..323cd38
Binary files /dev/null and b/testdata/zoneinfo/Europe/London differ
diff --git a/testdata/zoneinfo/Europe/Luxembourg b/testdata/zoneinfo/Europe/Luxembourg
new file mode 100644
index 0000000..682bcbf
Binary files /dev/null and b/testdata/zoneinfo/Europe/Luxembourg differ
diff --git a/testdata/zoneinfo/Europe/Madrid b/testdata/zoneinfo/Europe/Madrid
new file mode 100644
index 0000000..60bdf4d
Binary files /dev/null and b/testdata/zoneinfo/Europe/Madrid differ
diff --git a/testdata/zoneinfo/Europe/Malta b/testdata/zoneinfo/Europe/Malta
new file mode 100644
index 0000000..27539c2
Binary files /dev/null and b/testdata/zoneinfo/Europe/Malta differ
diff --git a/testdata/zoneinfo/Europe/Mariehamn b/testdata/zoneinfo/Europe/Mariehamn
new file mode 100644
index 0000000..ff5e565
Binary files /dev/null and b/testdata/zoneinfo/Europe/Mariehamn differ
diff --git a/testdata/zoneinfo/Europe/Minsk b/testdata/zoneinfo/Europe/Minsk
new file mode 100644
index 0000000..30d3a67
Binary files /dev/null and b/testdata/zoneinfo/Europe/Minsk differ
diff --git a/testdata/zoneinfo/Europe/Monaco b/testdata/zoneinfo/Europe/Monaco
new file mode 100644
index 0000000..f30dfc7
Binary files /dev/null and b/testdata/zoneinfo/Europe/Monaco differ
diff --git a/testdata/zoneinfo/Europe/Moscow b/testdata/zoneinfo/Europe/Moscow
new file mode 100644
index 0000000..5e6b6de
Binary files /dev/null and b/testdata/zoneinfo/Europe/Moscow differ
diff --git a/testdata/zoneinfo/Europe/Nicosia b/testdata/zoneinfo/Europe/Nicosia
new file mode 100644
index 0000000..c210d0a
Binary files /dev/null and b/testdata/zoneinfo/Europe/Nicosia differ
diff --git a/testdata/zoneinfo/Europe/Oslo b/testdata/zoneinfo/Europe/Oslo
new file mode 100644
index 0000000..dfc5095
Binary files /dev/null and b/testdata/zoneinfo/Europe/Oslo differ
diff --git a/testdata/zoneinfo/Europe/Paris b/testdata/zoneinfo/Europe/Paris
new file mode 100644
index 0000000..00a2726
Binary files /dev/null and b/testdata/zoneinfo/Europe/Paris differ
diff --git a/testdata/zoneinfo/Europe/Podgorica b/testdata/zoneinfo/Europe/Podgorica
new file mode 100644
index 0000000..a1bf928
Binary files /dev/null and b/testdata/zoneinfo/Europe/Podgorica differ
diff --git a/testdata/zoneinfo/Europe/Prague b/testdata/zoneinfo/Europe/Prague
new file mode 100644
index 0000000..fb7c145
Binary files /dev/null and b/testdata/zoneinfo/Europe/Prague differ
diff --git a/testdata/zoneinfo/Europe/Riga b/testdata/zoneinfo/Europe/Riga
new file mode 100644
index 0000000..26af4c9
Binary files /dev/null and b/testdata/zoneinfo/Europe/Riga differ
diff --git a/testdata/zoneinfo/Europe/Rome b/testdata/zoneinfo/Europe/Rome
new file mode 100644
index 0000000..639ca3b
Binary files /dev/null and b/testdata/zoneinfo/Europe/Rome differ
diff --git a/testdata/zoneinfo/Europe/Samara b/testdata/zoneinfo/Europe/Samara
new file mode 100644
index 0000000..8d0c26e
Binary files /dev/null and b/testdata/zoneinfo/Europe/Samara differ
diff --git a/testdata/zoneinfo/Europe/San_Marino b/testdata/zoneinfo/Europe/San_Marino
new file mode 100644
index 0000000..639ca3b
Binary files /dev/null and b/testdata/zoneinfo/Europe/San_Marino differ
diff --git a/testdata/zoneinfo/Europe/Sarajevo b/testdata/zoneinfo/Europe/Sarajevo
new file mode 100644
index 0000000..a1bf928
Binary files /dev/null and b/testdata/zoneinfo/Europe/Sarajevo differ
diff --git a/testdata/zoneinfo/Europe/Saratov b/testdata/zoneinfo/Europe/Saratov
new file mode 100644
index 0000000..2684d8f
Binary files /dev/null and b/testdata/zoneinfo/Europe/Saratov differ
diff --git a/testdata/zoneinfo/Europe/Simferopol b/testdata/zoneinfo/Europe/Simferopol
new file mode 100644
index 0000000..40d23c0
Binary files /dev/null and b/testdata/zoneinfo/Europe/Simferopol differ
diff --git a/testdata/zoneinfo/Europe/Skopje b/testdata/zoneinfo/Europe/Skopje
new file mode 100644
index 0000000..a1bf928
Binary files /dev/null and b/testdata/zoneinfo/Europe/Skopje differ
diff --git a/testdata/zoneinfo/Europe/Sofia b/testdata/zoneinfo/Europe/Sofia
new file mode 100644
index 0000000..eabc972
Binary files /dev/null and b/testdata/zoneinfo/Europe/Sofia differ
diff --git a/testdata/zoneinfo/Europe/Stockholm b/testdata/zoneinfo/Europe/Stockholm
new file mode 100644
index 0000000..dd3eb32
Binary files /dev/null and b/testdata/zoneinfo/Europe/Stockholm differ
diff --git a/testdata/zoneinfo/Europe/Tallinn b/testdata/zoneinfo/Europe/Tallinn
new file mode 100644
index 0000000..5321bbd
Binary files /dev/null and b/testdata/zoneinfo/Europe/Tallinn differ
diff --git a/testdata/zoneinfo/Europe/Tirane b/testdata/zoneinfo/Europe/Tirane
new file mode 100644
index 0000000..743a733
Binary files /dev/null and b/testdata/zoneinfo/Europe/Tirane differ
diff --git a/testdata/zoneinfo/Europe/Tiraspol b/testdata/zoneinfo/Europe/Tiraspol
new file mode 100644
index 0000000..6970b14
Binary files /dev/null and b/testdata/zoneinfo/Europe/Tiraspol differ
diff --git a/testdata/zoneinfo/Europe/Ulyanovsk b/testdata/zoneinfo/Europe/Ulyanovsk
new file mode 100644
index 0000000..bb842cb
Binary files /dev/null and b/testdata/zoneinfo/Europe/Ulyanovsk differ
diff --git a/testdata/zoneinfo/Europe/Uzhgorod b/testdata/zoneinfo/Europe/Uzhgorod
new file mode 100644
index 0000000..d4c3591
Binary files /dev/null and b/testdata/zoneinfo/Europe/Uzhgorod differ
diff --git a/testdata/zoneinfo/Europe/Vaduz b/testdata/zoneinfo/Europe/Vaduz
new file mode 100644
index 0000000..388df29
Binary files /dev/null and b/testdata/zoneinfo/Europe/Vaduz differ
diff --git a/testdata/zoneinfo/Europe/Vatican b/testdata/zoneinfo/Europe/Vatican
new file mode 100644
index 0000000..639ca3b
Binary files /dev/null and b/testdata/zoneinfo/Europe/Vatican differ
diff --git a/testdata/zoneinfo/Europe/Vienna b/testdata/zoneinfo/Europe/Vienna
new file mode 100644
index 0000000..75339e9
Binary files /dev/null and b/testdata/zoneinfo/Europe/Vienna differ
diff --git a/testdata/zoneinfo/Europe/Vilnius b/testdata/zoneinfo/Europe/Vilnius
new file mode 100644
index 0000000..75b2eeb
Binary files /dev/null and b/testdata/zoneinfo/Europe/Vilnius differ
diff --git a/testdata/zoneinfo/Europe/Volgograd b/testdata/zoneinfo/Europe/Volgograd
new file mode 100644
index 0000000..c517002
Binary files /dev/null and b/testdata/zoneinfo/Europe/Volgograd differ
diff --git a/testdata/zoneinfo/Europe/Warsaw b/testdata/zoneinfo/Europe/Warsaw
new file mode 100644
index 0000000..efe1a40
Binary files /dev/null and b/testdata/zoneinfo/Europe/Warsaw differ
diff --git a/testdata/zoneinfo/Europe/Zagreb b/testdata/zoneinfo/Europe/Zagreb
new file mode 100644
index 0000000..a1bf928
Binary files /dev/null and b/testdata/zoneinfo/Europe/Zagreb differ
diff --git a/testdata/zoneinfo/Europe/Zaporozhye b/testdata/zoneinfo/Europe/Zaporozhye
new file mode 100644
index 0000000..71819a5
Binary files /dev/null and b/testdata/zoneinfo/Europe/Zaporozhye differ
diff --git a/testdata/zoneinfo/Europe/Zurich b/testdata/zoneinfo/Europe/Zurich
new file mode 100644
index 0000000..388df29
Binary files /dev/null and b/testdata/zoneinfo/Europe/Zurich differ
diff --git a/testdata/zoneinfo/Factory b/testdata/zoneinfo/Factory
new file mode 100644
index 0000000..b4dd773
Binary files /dev/null and b/testdata/zoneinfo/Factory differ
diff --git a/testdata/zoneinfo/GB b/testdata/zoneinfo/GB
new file mode 100644
index 0000000..323cd38
Binary files /dev/null and b/testdata/zoneinfo/GB differ
diff --git a/testdata/zoneinfo/GB-Eire b/testdata/zoneinfo/GB-Eire
new file mode 100644
index 0000000..323cd38
Binary files /dev/null and b/testdata/zoneinfo/GB-Eire differ
diff --git a/testdata/zoneinfo/GMT b/testdata/zoneinfo/GMT
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/GMT differ
diff --git a/testdata/zoneinfo/GMT+0 b/testdata/zoneinfo/GMT+0
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/GMT+0 differ
diff --git a/testdata/zoneinfo/GMT-0 b/testdata/zoneinfo/GMT-0
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/GMT-0 differ
diff --git a/testdata/zoneinfo/GMT0 b/testdata/zoneinfo/GMT0
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/GMT0 differ
diff --git a/testdata/zoneinfo/Greenwich b/testdata/zoneinfo/Greenwich
new file mode 100644
index 0000000..157573b
Binary files /dev/null and b/testdata/zoneinfo/Greenwich differ
diff --git a/testdata/zoneinfo/HST b/testdata/zoneinfo/HST
new file mode 100644
index 0000000..160a53e
Binary files /dev/null and b/testdata/zoneinfo/HST differ
diff --git a/testdata/zoneinfo/Hongkong b/testdata/zoneinfo/Hongkong
new file mode 100644
index 0000000..c80e364
Binary files /dev/null and b/testdata/zoneinfo/Hongkong differ
diff --git a/testdata/zoneinfo/Iceland b/testdata/zoneinfo/Iceland
new file mode 100644
index 0000000..2451aca
Binary files /dev/null and b/testdata/zoneinfo/Iceland differ
diff --git a/testdata/zoneinfo/Indian/Antananarivo b/testdata/zoneinfo/Indian/Antananarivo
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Indian/Antananarivo differ
diff --git a/testdata/zoneinfo/Indian/Chagos b/testdata/zoneinfo/Indian/Chagos
new file mode 100644
index 0000000..8b8ce22
Binary files /dev/null and b/testdata/zoneinfo/Indian/Chagos differ
diff --git a/testdata/zoneinfo/Indian/Christmas b/testdata/zoneinfo/Indian/Christmas
new file mode 100644
index 0000000..766024b
Binary files /dev/null and b/testdata/zoneinfo/Indian/Christmas differ
diff --git a/testdata/zoneinfo/Indian/Cocos b/testdata/zoneinfo/Indian/Cocos
new file mode 100644
index 0000000..1175034
Binary files /dev/null and b/testdata/zoneinfo/Indian/Cocos differ
diff --git a/testdata/zoneinfo/Indian/Comoro b/testdata/zoneinfo/Indian/Comoro
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Indian/Comoro differ
diff --git a/testdata/zoneinfo/Indian/Kerguelen b/testdata/zoneinfo/Indian/Kerguelen
new file mode 100644
index 0000000..8ce93e0
Binary files /dev/null and b/testdata/zoneinfo/Indian/Kerguelen differ
diff --git a/testdata/zoneinfo/Indian/Mahe b/testdata/zoneinfo/Indian/Mahe
new file mode 100644
index 0000000..e7fccf8
Binary files /dev/null and b/testdata/zoneinfo/Indian/Mahe differ
diff --git a/testdata/zoneinfo/Indian/Maldives b/testdata/zoneinfo/Indian/Maldives
new file mode 100644
index 0000000..58a82e4
Binary files /dev/null and b/testdata/zoneinfo/Indian/Maldives differ
diff --git a/testdata/zoneinfo/Indian/Mauritius b/testdata/zoneinfo/Indian/Mauritius
new file mode 100644
index 0000000..7c11134
Binary files /dev/null and b/testdata/zoneinfo/Indian/Mauritius differ
diff --git a/testdata/zoneinfo/Indian/Mayotte b/testdata/zoneinfo/Indian/Mayotte
new file mode 100644
index 0000000..5f4ebcb
Binary files /dev/null and b/testdata/zoneinfo/Indian/Mayotte differ
diff --git a/testdata/zoneinfo/Indian/Reunion b/testdata/zoneinfo/Indian/Reunion
new file mode 100644
index 0000000..248a7c9
Binary files /dev/null and b/testdata/zoneinfo/Indian/Reunion differ
diff --git a/testdata/zoneinfo/Iran b/testdata/zoneinfo/Iran
new file mode 100644
index 0000000..f1555f0
Binary files /dev/null and b/testdata/zoneinfo/Iran differ
diff --git a/testdata/zoneinfo/Israel b/testdata/zoneinfo/Israel
new file mode 100644
index 0000000..4c49bbf
Binary files /dev/null and b/testdata/zoneinfo/Israel differ
diff --git a/testdata/zoneinfo/Jamaica b/testdata/zoneinfo/Jamaica
new file mode 100644
index 0000000..be6b1b6
Binary files /dev/null and b/testdata/zoneinfo/Jamaica differ
diff --git a/testdata/zoneinfo/Japan b/testdata/zoneinfo/Japan
new file mode 100644
index 0000000..1aa066c
Binary files /dev/null and b/testdata/zoneinfo/Japan differ
diff --git a/testdata/zoneinfo/Kwajalein b/testdata/zoneinfo/Kwajalein
new file mode 100644
index 0000000..9416d52
Binary files /dev/null and b/testdata/zoneinfo/Kwajalein differ
diff --git a/testdata/zoneinfo/Libya b/testdata/zoneinfo/Libya
new file mode 100644
index 0000000..e0c8997
Binary files /dev/null and b/testdata/zoneinfo/Libya differ
diff --git a/testdata/zoneinfo/MET b/testdata/zoneinfo/MET
new file mode 100644
index 0000000..6f0558c
Binary files /dev/null and b/testdata/zoneinfo/MET differ
diff --git a/testdata/zoneinfo/MST b/testdata/zoneinfo/MST
new file mode 100644
index 0000000..a0953d1
Binary files /dev/null and b/testdata/zoneinfo/MST differ
diff --git a/testdata/zoneinfo/MST7MDT b/testdata/zoneinfo/MST7MDT
new file mode 100644
index 0000000..137867c
Binary files /dev/null and b/testdata/zoneinfo/MST7MDT differ
diff --git a/testdata/zoneinfo/Mexico/BajaNorte b/testdata/zoneinfo/Mexico/BajaNorte
new file mode 100644
index 0000000..19ccd35
Binary files /dev/null and b/testdata/zoneinfo/Mexico/BajaNorte differ
diff --git a/testdata/zoneinfo/Mexico/BajaSur b/testdata/zoneinfo/Mexico/BajaSur
new file mode 100644
index 0000000..4c819fa
Binary files /dev/null and b/testdata/zoneinfo/Mexico/BajaSur differ
diff --git a/testdata/zoneinfo/Mexico/General b/testdata/zoneinfo/Mexico/General
new file mode 100644
index 0000000..ffcf8be
Binary files /dev/null and b/testdata/zoneinfo/Mexico/General differ
diff --git a/testdata/zoneinfo/NZ b/testdata/zoneinfo/NZ
new file mode 100644
index 0000000..afb3929
Binary files /dev/null and b/testdata/zoneinfo/NZ differ
diff --git a/testdata/zoneinfo/NZ-CHAT b/testdata/zoneinfo/NZ-CHAT
new file mode 100644
index 0000000..f06065e
Binary files /dev/null and b/testdata/zoneinfo/NZ-CHAT differ
diff --git a/testdata/zoneinfo/Navajo b/testdata/zoneinfo/Navajo
new file mode 100644
index 0000000..09e54e5
Binary files /dev/null and b/testdata/zoneinfo/Navajo differ
diff --git a/testdata/zoneinfo/PRC b/testdata/zoneinfo/PRC
new file mode 100644
index 0000000..d6b6698
Binary files /dev/null and b/testdata/zoneinfo/PRC differ
diff --git a/testdata/zoneinfo/PST8PDT b/testdata/zoneinfo/PST8PDT
new file mode 100644
index 0000000..fde4833
Binary files /dev/null and b/testdata/zoneinfo/PST8PDT differ
diff --git a/testdata/zoneinfo/Pacific/Apia b/testdata/zoneinfo/Pacific/Apia
new file mode 100644
index 0000000..a6b835a
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Apia differ
diff --git a/testdata/zoneinfo/Pacific/Auckland b/testdata/zoneinfo/Pacific/Auckland
new file mode 100644
index 0000000..afb3929
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Auckland differ
diff --git a/testdata/zoneinfo/Pacific/Bougainville b/testdata/zoneinfo/Pacific/Bougainville
new file mode 100644
index 0000000..7c66709
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Bougainville differ
diff --git a/testdata/zoneinfo/Pacific/Chatham b/testdata/zoneinfo/Pacific/Chatham
new file mode 100644
index 0000000..f06065e
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Chatham differ
diff --git a/testdata/zoneinfo/Pacific/Chuuk b/testdata/zoneinfo/Pacific/Chuuk
new file mode 100644
index 0000000..ea3fb5c
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Chuuk differ
diff --git a/testdata/zoneinfo/Pacific/Easter b/testdata/zoneinfo/Pacific/Easter
new file mode 100644
index 0000000..d29bcd6
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Easter differ
diff --git a/testdata/zoneinfo/Pacific/Efate b/testdata/zoneinfo/Pacific/Efate
new file mode 100644
index 0000000..bf7471d
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Efate differ
diff --git a/testdata/zoneinfo/Pacific/Enderbury b/testdata/zoneinfo/Pacific/Enderbury
new file mode 100644
index 0000000..2b6a060
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Enderbury differ
diff --git a/testdata/zoneinfo/Pacific/Fakaofo b/testdata/zoneinfo/Pacific/Fakaofo
new file mode 100644
index 0000000..b7b3021
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Fakaofo differ
diff --git a/testdata/zoneinfo/Pacific/Fiji b/testdata/zoneinfo/Pacific/Fiji
new file mode 100644
index 0000000..8b2dd52
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Fiji differ
diff --git a/testdata/zoneinfo/Pacific/Funafuti b/testdata/zoneinfo/Pacific/Funafuti
new file mode 100644
index 0000000..78ab35b
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Funafuti differ
diff --git a/testdata/zoneinfo/Pacific/Galapagos b/testdata/zoneinfo/Pacific/Galapagos
new file mode 100644
index 0000000..a9403ec
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Galapagos differ
diff --git a/testdata/zoneinfo/Pacific/Gambier b/testdata/zoneinfo/Pacific/Gambier
new file mode 100644
index 0000000..ddfc34f
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Gambier differ
diff --git a/testdata/zoneinfo/Pacific/Guadalcanal b/testdata/zoneinfo/Pacific/Guadalcanal
new file mode 100644
index 0000000..720c679
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Guadalcanal differ
diff --git a/testdata/zoneinfo/Pacific/Guam b/testdata/zoneinfo/Pacific/Guam
new file mode 100644
index 0000000..bf9a2d9
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Guam differ
diff --git a/testdata/zoneinfo/Pacific/Honolulu b/testdata/zoneinfo/Pacific/Honolulu
new file mode 100644
index 0000000..40e3d49
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Honolulu differ
diff --git a/testdata/zoneinfo/Pacific/Johnston b/testdata/zoneinfo/Pacific/Johnston
new file mode 100644
index 0000000..40e3d49
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Johnston differ
diff --git a/testdata/zoneinfo/Pacific/Kanton b/testdata/zoneinfo/Pacific/Kanton
new file mode 100644
index 0000000..2b6a060
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Kanton differ
diff --git a/testdata/zoneinfo/Pacific/Kiritimati b/testdata/zoneinfo/Pacific/Kiritimati
new file mode 100644
index 0000000..2f676d3
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Kiritimati differ
diff --git a/testdata/zoneinfo/Pacific/Kosrae b/testdata/zoneinfo/Pacific/Kosrae
new file mode 100644
index 0000000..f5d5824
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Kosrae differ
diff --git a/testdata/zoneinfo/Pacific/Kwajalein b/testdata/zoneinfo/Pacific/Kwajalein
new file mode 100644
index 0000000..9416d52
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Kwajalein differ
diff --git a/testdata/zoneinfo/Pacific/Majuro b/testdata/zoneinfo/Pacific/Majuro
new file mode 100644
index 0000000..9228ee0
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Majuro differ
diff --git a/testdata/zoneinfo/Pacific/Marquesas b/testdata/zoneinfo/Pacific/Marquesas
new file mode 100644
index 0000000..6ea24b7
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Marquesas differ
diff --git a/testdata/zoneinfo/Pacific/Midway b/testdata/zoneinfo/Pacific/Midway
new file mode 100644
index 0000000..001289c
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Midway differ
diff --git a/testdata/zoneinfo/Pacific/Nauru b/testdata/zoneinfo/Pacific/Nauru
new file mode 100644
index 0000000..ae13aac
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Nauru differ
diff --git a/testdata/zoneinfo/Pacific/Niue b/testdata/zoneinfo/Pacific/Niue
new file mode 100644
index 0000000..be874e2
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Niue differ
diff --git a/testdata/zoneinfo/Pacific/Norfolk b/testdata/zoneinfo/Pacific/Norfolk
new file mode 100644
index 0000000..79e2a94
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Norfolk differ
diff --git a/testdata/zoneinfo/Pacific/Noumea b/testdata/zoneinfo/Pacific/Noumea
new file mode 100644
index 0000000..824f814
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Noumea differ
diff --git a/testdata/zoneinfo/Pacific/Pago_Pago b/testdata/zoneinfo/Pacific/Pago_Pago
new file mode 100644
index 0000000..001289c
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Pago_Pago differ
diff --git a/testdata/zoneinfo/Pacific/Palau b/testdata/zoneinfo/Pacific/Palau
new file mode 100644
index 0000000..bc8eb7a
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Palau differ
diff --git a/testdata/zoneinfo/Pacific/Pitcairn b/testdata/zoneinfo/Pacific/Pitcairn
new file mode 100644
index 0000000..8a4ba4d
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Pitcairn differ
diff --git a/testdata/zoneinfo/Pacific/Pohnpei b/testdata/zoneinfo/Pacific/Pohnpei
new file mode 100644
index 0000000..b92b254
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Pohnpei differ
diff --git a/testdata/zoneinfo/Pacific/Ponape b/testdata/zoneinfo/Pacific/Ponape
new file mode 100644
index 0000000..b92b254
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Ponape differ
diff --git a/testdata/zoneinfo/Pacific/Port_Moresby b/testdata/zoneinfo/Pacific/Port_Moresby
new file mode 100644
index 0000000..5d8fc3a
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Port_Moresby differ
diff --git a/testdata/zoneinfo/Pacific/Rarotonga b/testdata/zoneinfo/Pacific/Rarotonga
new file mode 100644
index 0000000..7220bda
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Rarotonga differ
diff --git a/testdata/zoneinfo/Pacific/Saipan b/testdata/zoneinfo/Pacific/Saipan
new file mode 100644
index 0000000..bf9a2d9
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Saipan differ
diff --git a/testdata/zoneinfo/Pacific/Samoa b/testdata/zoneinfo/Pacific/Samoa
new file mode 100644
index 0000000..001289c
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Samoa differ
diff --git a/testdata/zoneinfo/Pacific/Tahiti b/testdata/zoneinfo/Pacific/Tahiti
new file mode 100644
index 0000000..50a064f
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Tahiti differ
diff --git a/testdata/zoneinfo/Pacific/Tarawa b/testdata/zoneinfo/Pacific/Tarawa
new file mode 100644
index 0000000..6bc2168
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Tarawa differ
diff --git a/testdata/zoneinfo/Pacific/Tongatapu b/testdata/zoneinfo/Pacific/Tongatapu
new file mode 100644
index 0000000..f28c840
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Tongatapu differ
diff --git a/testdata/zoneinfo/Pacific/Truk b/testdata/zoneinfo/Pacific/Truk
new file mode 100644
index 0000000..ea3fb5c
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Truk differ
diff --git a/testdata/zoneinfo/Pacific/Wake b/testdata/zoneinfo/Pacific/Wake
new file mode 100644
index 0000000..71cca88
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Wake differ
diff --git a/testdata/zoneinfo/Pacific/Wallis b/testdata/zoneinfo/Pacific/Wallis
new file mode 100644
index 0000000..4bce893
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Wallis differ
diff --git a/testdata/zoneinfo/Pacific/Yap b/testdata/zoneinfo/Pacific/Yap
new file mode 100644
index 0000000..ea3fb5c
Binary files /dev/null and b/testdata/zoneinfo/Pacific/Yap differ
diff --git a/testdata/zoneinfo/Poland b/testdata/zoneinfo/Poland
new file mode 100644
index 0000000..efe1a40
Binary files /dev/null and b/testdata/zoneinfo/Poland differ
diff --git a/testdata/zoneinfo/Portugal b/testdata/zoneinfo/Portugal
new file mode 100644
index 0000000..f0c70b6
Binary files /dev/null and b/testdata/zoneinfo/Portugal differ
diff --git a/testdata/zoneinfo/ROC b/testdata/zoneinfo/ROC
new file mode 100644
index 0000000..35d89d0
Binary files /dev/null and b/testdata/zoneinfo/ROC differ
diff --git a/testdata/zoneinfo/ROK b/testdata/zoneinfo/ROK
new file mode 100644
index 0000000..1755147
Binary files /dev/null and b/testdata/zoneinfo/ROK differ
diff --git a/testdata/zoneinfo/Singapore b/testdata/zoneinfo/Singapore
new file mode 100644
index 0000000..350d77e
Binary files /dev/null and b/testdata/zoneinfo/Singapore differ
diff --git a/testdata/zoneinfo/Turkey b/testdata/zoneinfo/Turkey
new file mode 100644
index 0000000..c891866
Binary files /dev/null and b/testdata/zoneinfo/Turkey differ
diff --git a/testdata/zoneinfo/UCT b/testdata/zoneinfo/UCT
new file mode 100644
index 0000000..00841a6
Binary files /dev/null and b/testdata/zoneinfo/UCT differ
diff --git a/testdata/zoneinfo/US/Alaska b/testdata/zoneinfo/US/Alaska
new file mode 100644
index 0000000..cdf0572
Binary files /dev/null and b/testdata/zoneinfo/US/Alaska differ
diff --git a/testdata/zoneinfo/US/Aleutian b/testdata/zoneinfo/US/Aleutian
new file mode 100644
index 0000000..b1497bd
Binary files /dev/null and b/testdata/zoneinfo/US/Aleutian differ
diff --git a/testdata/zoneinfo/US/Arizona b/testdata/zoneinfo/US/Arizona
new file mode 100644
index 0000000..c2bd2f9
Binary files /dev/null and b/testdata/zoneinfo/US/Arizona differ
diff --git a/testdata/zoneinfo/US/Central b/testdata/zoneinfo/US/Central
new file mode 100644
index 0000000..b016880
Binary files /dev/null and b/testdata/zoneinfo/US/Central differ
diff --git a/testdata/zoneinfo/US/East-Indiana b/testdata/zoneinfo/US/East-Indiana
new file mode 100644
index 0000000..6b08d15
Binary files /dev/null and b/testdata/zoneinfo/US/East-Indiana differ
diff --git a/testdata/zoneinfo/US/Eastern b/testdata/zoneinfo/US/Eastern
new file mode 100644
index 0000000..2b6c2ee
Binary files /dev/null and b/testdata/zoneinfo/US/Eastern differ
diff --git a/testdata/zoneinfo/US/Hawaii b/testdata/zoneinfo/US/Hawaii
new file mode 100644
index 0000000..40e3d49
Binary files /dev/null and b/testdata/zoneinfo/US/Hawaii differ
diff --git a/testdata/zoneinfo/US/Indiana-Starke b/testdata/zoneinfo/US/Indiana-Starke
new file mode 100644
index 0000000..b187d5f
Binary files /dev/null and b/testdata/zoneinfo/US/Indiana-Starke differ
diff --git a/testdata/zoneinfo/US/Michigan b/testdata/zoneinfo/US/Michigan
new file mode 100644
index 0000000..6eb3ac4
Binary files /dev/null and b/testdata/zoneinfo/US/Michigan differ
diff --git a/testdata/zoneinfo/US/Mountain b/testdata/zoneinfo/US/Mountain
new file mode 100644
index 0000000..09e54e5
Binary files /dev/null and b/testdata/zoneinfo/US/Mountain differ
diff --git a/testdata/zoneinfo/US/Pacific b/testdata/zoneinfo/US/Pacific
new file mode 100644
index 0000000..aaf0778
Binary files /dev/null and b/testdata/zoneinfo/US/Pacific differ
diff --git a/testdata/zoneinfo/US/Samoa b/testdata/zoneinfo/US/Samoa
new file mode 100644
index 0000000..001289c
Binary files /dev/null and b/testdata/zoneinfo/US/Samoa differ
diff --git a/testdata/zoneinfo/UTC b/testdata/zoneinfo/UTC
new file mode 100644
index 0000000..00841a6
Binary files /dev/null and b/testdata/zoneinfo/UTC differ
diff --git a/testdata/zoneinfo/Universal b/testdata/zoneinfo/Universal
new file mode 100644
index 0000000..00841a6
Binary files /dev/null and b/testdata/zoneinfo/Universal differ
diff --git a/testdata/zoneinfo/W-SU b/testdata/zoneinfo/W-SU
new file mode 100644
index 0000000..5e6b6de
Binary files /dev/null and b/testdata/zoneinfo/W-SU differ
diff --git a/testdata/zoneinfo/WET b/testdata/zoneinfo/WET
new file mode 100644
index 0000000..423c6c2
Binary files /dev/null and b/testdata/zoneinfo/WET differ
diff --git a/testdata/zoneinfo/Zulu b/testdata/zoneinfo/Zulu
new file mode 100644
index 0000000..00841a6
Binary files /dev/null and b/testdata/zoneinfo/Zulu differ
diff --git a/testdata/zoneinfo/iso3166.tab b/testdata/zoneinfo/iso3166.tab
new file mode 100644
index 0000000..a4ff61a
--- /dev/null
+++ b/testdata/zoneinfo/iso3166.tab
@@ -0,0 +1,274 @@
+# ISO 3166 alpha-2 country codes
+#
+# This file is in the public domain, so clarified as of
+# 2009-05-17 by Arthur David Olson.
+#
+# From Paul Eggert (2015-05-02):
+# This file contains a table of two-letter country codes. Columns are
+# separated by a single tab. Lines beginning with '#' are comments.
+# All text uses UTF-8 encoding. The columns of the table are as follows:
+#
+# 1. ISO 3166-1 alpha-2 country code, current as of
+# ISO 3166-1 N976 (2018-11-06). See: Updates on ISO 3166-1
+# https://isotc.iso.org/livelink/livelink/Open/16944257
+# 2. The usual English name for the coded region,
+# chosen so that alphabetic sorting of subsets produces helpful lists.
+# This is not the same as the English name in the ISO 3166 tables.
+#
+# The table is sorted by country code.
+#
+# This table is intended as an aid for users, to help them select time
+# zone data appropriate for their practical needs. It is not intended
+# to take or endorse any position on legal or territorial claims.
+#
+#country-
+#code name of country, territory, area, or subdivision
+AD Andorra
+AE United Arab Emirates
+AF Afghanistan
+AG Antigua & Barbuda
+AI Anguilla
+AL Albania
+AM Armenia
+AO Angola
+AQ Antarctica
+AR Argentina
+AS Samoa (American)
+AT Austria
+AU Australia
+AW Aruba
+AX Åland Islands
+AZ Azerbaijan
+BA Bosnia & Herzegovina
+BB Barbados
+BD Bangladesh
+BE Belgium
+BF Burkina Faso
+BG Bulgaria
+BH Bahrain
+BI Burundi
+BJ Benin
+BL St Barthelemy
+BM Bermuda
+BN Brunei
+BO Bolivia
+BQ Caribbean NL
+BR Brazil
+BS Bahamas
+BT Bhutan
+BV Bouvet Island
+BW Botswana
+BY Belarus
+BZ Belize
+CA Canada
+CC Cocos (Keeling) Islands
+CD Congo (Dem. Rep.)
+CF Central African Rep.
+CG Congo (Rep.)
+CH Switzerland
+CI Côte d'Ivoire
+CK Cook Islands
+CL Chile
+CM Cameroon
+CN China
+CO Colombia
+CR Costa Rica
+CU Cuba
+CV Cape Verde
+CW Curaçao
+CX Christmas Island
+CY Cyprus
+CZ Czech Republic
+DE Germany
+DJ Djibouti
+DK Denmark
+DM Dominica
+DO Dominican Republic
+DZ Algeria
+EC Ecuador
+EE Estonia
+EG Egypt
+EH Western Sahara
+ER Eritrea
+ES Spain
+ET Ethiopia
+FI Finland
+FJ Fiji
+FK Falkland Islands
+FM Micronesia
+FO Faroe Islands
+FR France
+GA Gabon
+GB Britain (UK)
+GD Grenada
+GE Georgia
+GF French Guiana
+GG Guernsey
+GH Ghana
+GI Gibraltar
+GL Greenland
+GM Gambia
+GN Guinea
+GP Guadeloupe
+GQ Equatorial Guinea
+GR Greece
+GS South Georgia & the South Sandwich Islands
+GT Guatemala
+GU Guam
+GW Guinea-Bissau
+GY Guyana
+HK Hong Kong
+HM Heard Island & McDonald Islands
+HN Honduras
+HR Croatia
+HT Haiti
+HU Hungary
+ID Indonesia
+IE Ireland
+IL Israel
+IM Isle of Man
+IN India
+IO British Indian Ocean Territory
+IQ Iraq
+IR Iran
+IS Iceland
+IT Italy
+JE Jersey
+JM Jamaica
+JO Jordan
+JP Japan
+KE Kenya
+KG Kyrgyzstan
+KH Cambodia
+KI Kiribati
+KM Comoros
+KN St Kitts & Nevis
+KP Korea (North)
+KR Korea (South)
+KW Kuwait
+KY Cayman Islands
+KZ Kazakhstan
+LA Laos
+LB Lebanon
+LC St Lucia
+LI Liechtenstein
+LK Sri Lanka
+LR Liberia
+LS Lesotho
+LT Lithuania
+LU Luxembourg
+LV Latvia
+LY Libya
+MA Morocco
+MC Monaco
+MD Moldova
+ME Montenegro
+MF St Martin (French)
+MG Madagascar
+MH Marshall Islands
+MK North Macedonia
+ML Mali
+MM Myanmar (Burma)
+MN Mongolia
+MO Macau
+MP Northern Mariana Islands
+MQ Martinique
+MR Mauritania
+MS Montserrat
+MT Malta
+MU Mauritius
+MV Maldives
+MW Malawi
+MX Mexico
+MY Malaysia
+MZ Mozambique
+NA Namibia
+NC New Caledonia
+NE Niger
+NF Norfolk Island
+NG Nigeria
+NI Nicaragua
+NL Netherlands
+NO Norway
+NP Nepal
+NR Nauru
+NU Niue
+NZ New Zealand
+OM Oman
+PA Panama
+PE Peru
+PF French Polynesia
+PG Papua New Guinea
+PH Philippines
+PK Pakistan
+PL Poland
+PM St Pierre & Miquelon
+PN Pitcairn
+PR Puerto Rico
+PS Palestine
+PT Portugal
+PW Palau
+PY Paraguay
+QA Qatar
+RE Réunion
+RO Romania
+RS Serbia
+RU Russia
+RW Rwanda
+SA Saudi Arabia
+SB Solomon Islands
+SC Seychelles
+SD Sudan
+SE Sweden
+SG Singapore
+SH St Helena
+SI Slovenia
+SJ Svalbard & Jan Mayen
+SK Slovakia
+SL Sierra Leone
+SM San Marino
+SN Senegal
+SO Somalia
+SR Suriname
+SS South Sudan
+ST Sao Tome & Principe
+SV El Salvador
+SX St Maarten (Dutch)
+SY Syria
+SZ Eswatini (Swaziland)
+TC Turks & Caicos Is
+TD Chad
+TF French Southern & Antarctic Lands
+TG Togo
+TH Thailand
+TJ Tajikistan
+TK Tokelau
+TL East Timor
+TM Turkmenistan
+TN Tunisia
+TO Tonga
+TR Turkey
+TT Trinidad & Tobago
+TV Tuvalu
+TW Taiwan
+TZ Tanzania
+UA Ukraine
+UG Uganda
+UM US minor outlying islands
+US United States
+UY Uruguay
+UZ Uzbekistan
+VA Vatican City
+VC St Vincent
+VE Venezuela
+VG Virgin Islands (UK)
+VI Virgin Islands (US)
+VN Vietnam
+VU Vanuatu
+WF Wallis & Futuna
+WS Samoa (western)
+YE Yemen
+YT Mayotte
+ZA South Africa
+ZM Zambia
+ZW Zimbabwe
diff --git a/testdata/zoneinfo/localtime b/testdata/zoneinfo/localtime
new file mode 100644
index 0000000..afeeb88
Binary files /dev/null and b/testdata/zoneinfo/localtime differ
diff --git a/testdata/zoneinfo/zone1970.tab b/testdata/zoneinfo/zone1970.tab
new file mode 100644
index 0000000..c614be8
--- /dev/null
+++ b/testdata/zoneinfo/zone1970.tab
@@ -0,0 +1,374 @@
+# tzdb timezone descriptions
+#
+# This file is in the public domain.
+#
+# From Paul Eggert (2018-06-27):
+# This file contains a table where each row stands for a timezone where
+# civil timestamps have agreed since 1970. Columns are separated by
+# a single tab. Lines beginning with '#' are comments. All text uses
+# UTF-8 encoding. The columns of the table are as follows:
+#
+# 1. The countries that overlap the timezone, as a comma-separated list
+# of ISO 3166 2-character country codes. See the file 'iso3166.tab'.
+# 2. Latitude and longitude of the timezone's principal location
+# in ISO 6709 sign-degrees-minutes-seconds format,
+# either ±DDMM±DDDMM or ±DDMMSS±DDDMMSS,
+# first latitude (+ is north), then longitude (+ is east).
+# 3. Timezone name used in value of TZ environment variable.
+# 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.
+#
+# If a timezone covers multiple countries, the most-populous city is used,
+# and that country is listed first in column 1; any other countries
+# are listed alphabetically by country code. The table is sorted
+# first by country code, then (if possible) by an order within the
+# country that (1) makes some geographical sense, and (2) puts the
+# most populous timezones first, where that does not contradict (1).
+#
+# This table is intended as an aid for users, to help them select timezones
+# appropriate for their practical needs. It is not intended to take or
+# endorse any position on legal or territorial claims.
+#
+#country-
+#codes coordinates TZ comments
+AD +4230+00131 Europe/Andorra
+AE,OM +2518+05518 Asia/Dubai
+AF +3431+06912 Asia/Kabul
+AL +4120+01950 Europe/Tirane
+AM +4011+04430 Asia/Yerevan
+AQ -6617+11031 Antarctica/Casey Casey
+AQ -6835+07758 Antarctica/Davis Davis
+AQ -6736+06253 Antarctica/Mawson Mawson
+AQ -6448-06406 Antarctica/Palmer Palmer
+AQ -6734-06808 Antarctica/Rothera Rothera
+AQ -720041+0023206 Antarctica/Troll Troll
+AQ -7824+10654 Antarctica/Vostok Vostok
+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 -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)
+AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH)
+AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR)
+AR -3132-06831 America/Argentina/San_Juan San Juan (SJ)
+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
+AT +4813+01620 Europe/Vienna
+AU -3133+15905 Australia/Lord_Howe Lord Howe Island
+AU -5430+15857 Antarctica/Macquarie Macquarie Island
+AU -4253+14719 Australia/Hobart Tasmania
+AU -3749+14458 Australia/Melbourne Victoria
+AU -3352+15113 Australia/Sydney New South Wales (most areas)
+AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna)
+AU -2728+15302 Australia/Brisbane Queensland (most areas)
+AU -2016+14900 Australia/Lindeman Queensland (Whitsunday Islands)
+AU -3455+13835 Australia/Adelaide South Australia
+AU -1228+13050 Australia/Darwin Northern Territory
+AU -3157+11551 Australia/Perth Western Australia (most areas)
+AU -3143+12852 Australia/Eucla Western Australia (Eucla)
+AZ +4023+04951 Asia/Baku
+BB +1306-05937 America/Barbados
+BD +2343+09025 Asia/Dhaka
+BE +5050+00420 Europe/Brussels
+BG +4241+02319 Europe/Sofia
+BM +3217-06446 Atlantic/Bermuda
+BN +0456+11455 Asia/Brunei
+BO -1630-06809 America/La_Paz
+BR -0351-03225 America/Noronha Atlantic islands
+BR -0127-04829 America/Belem Pará (east); Amapá
+BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB)
+BR -0803-03454 America/Recife Pernambuco
+BR -0712-04812 America/Araguaina Tocantins
+BR -0940-03543 America/Maceio Alagoas, Sergipe
+BR -1259-03831 America/Bahia Bahia
+BR -2332-04637 America/Sao_Paulo Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS)
+BR -2027-05437 America/Campo_Grande Mato Grosso do Sul
+BR -1535-05605 America/Cuiaba Mato Grosso
+BR -0226-05452 America/Santarem Pará (west)
+BR -0846-06354 America/Porto_Velho Rondônia
+BR +0249-06040 America/Boa_Vista Roraima
+BR -0308-06001 America/Manaus Amazonas (east)
+BR -0640-06952 America/Eirunepe Amazonas (west)
+BR -0958-06748 America/Rio_Branco Acre
+BT +2728+08939 Asia/Thimphu
+BY +5354+02734 Europe/Minsk
+BZ +1730-08812 America/Belize
+CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast)
+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 +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73)
+CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay)
+CA +6344-06828 America/Iqaluit Eastern - NU (most east areas)
+CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung)
+CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba
+CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances)
+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 +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 +5946-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)
+CC -1210+09655 Indian/Cocos
+CH,DE,LI +4723+00832 Europe/Zurich Swiss time
+CI,BF,GH,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan
+CK -2114-15946 Pacific/Rarotonga
+CL -3327-07040 America/Santiago Chile (most areas)
+CL -5309-07055 America/Punta_Arenas Region of Magallanes
+CL -2709-10926 Pacific/Easter Easter Island
+CN +3114+12128 Asia/Shanghai Beijing Time
+CN +4348+08735 Asia/Urumqi Xinjiang Time
+CO +0436-07405 America/Bogota
+CR +0956-08405 America/Costa_Rica
+CU +2308-08222 America/Havana
+CV +1455-02331 Atlantic/Cape_Verde
+CX -1025+10543 Indian/Christmas
+CY +3510+03322 Asia/Nicosia Cyprus (most areas)
+CY +3507+03357 Asia/Famagusta Northern Cyprus
+CZ,SK +5005+01426 Europe/Prague
+DE +5230+01322 Europe/Berlin Germany (most areas)
+DK +5540+01235 Europe/Copenhagen
+DO +1828-06954 America/Santo_Domingo
+DZ +3647+00303 Africa/Algiers
+EC -0210-07950 America/Guayaquil Ecuador (mainland)
+EC -0054-08936 Pacific/Galapagos Galápagos Islands
+EE +5925+02445 Europe/Tallinn
+EG +3003+03115 Africa/Cairo
+EH +2709-01312 Africa/El_Aaiun
+ES +4024-00341 Europe/Madrid Spain (mainland)
+ES +3553-00519 Africa/Ceuta Ceuta, Melilla
+ES +2806-01524 Atlantic/Canary Canary Islands
+FI,AX +6010+02458 Europe/Helsinki
+FJ -1808+17825 Pacific/Fiji
+FK -5142-05751 Atlantic/Stanley
+FM +0725+15147 Pacific/Chuuk Chuuk/Truk, Yap
+FM +0658+15813 Pacific/Pohnpei Pohnpei/Ponape
+FM +0519+16259 Pacific/Kosrae Kosrae
+FO +6201-00646 Atlantic/Faroe
+FR +4852+00220 Europe/Paris
+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 +7646-01840 America/Danmarkshavn National Park (east coast)
+GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit
+GL +7634-06847 America/Thule Thule/Pituffik
+GR +3758+02343 Europe/Athens
+GS -5416-03632 Atlantic/South_Georgia
+GT +1438-09031 America/Guatemala
+GU,MP +1328+14445 Pacific/Guam
+GW +1151-01535 Africa/Bissau
+GY +0648-05810 America/Guyana
+HK +2217+11409 Asia/Hong_Kong
+HN +1406-08713 America/Tegucigalpa
+HT +1832-07220 America/Port-au-Prince
+HU +4730+01905 Europe/Budapest
+ID -0610+10648 Asia/Jakarta Java, Sumatra
+ID -0002+10920 Asia/Pontianak Borneo (west, central)
+ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west)
+ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas
+IE +5320-00615 Europe/Dublin
+IL +314650+0351326 Asia/Jerusalem
+IN +2232+08822 Asia/Kolkata
+IO -0720+07225 Indian/Chagos
+IQ +3321+04425 Asia/Baghdad
+IR +3540+05126 Asia/Tehran
+IS +6409-02151 Atlantic/Reykjavik
+IT,SM,VA +4154+01229 Europe/Rome
+JM +175805-0764736 America/Jamaica
+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 +0125+17300 Pacific/Tarawa Gilbert Islands
+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 +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda
+KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay
+KZ +5017+05710 Asia/Aqtobe Aqtöbe/Aktobe
+KZ +4431+05016 Asia/Aqtau Mangghystaū/Mankistau
+KZ +4707+05156 Asia/Atyrau Atyraū/Atirau/Gur'yev
+KZ +5113+05121 Asia/Oral West Kazakhstan
+LB +3353+03530 Asia/Beirut
+LK +0656+07951 Asia/Colombo
+LR +0618-01047 Africa/Monrovia
+LT +5441+02519 Europe/Vilnius
+LU +4936+00609 Europe/Luxembourg
+LV +5657+02406 Europe/Riga
+LY +3254+01311 Africa/Tripoli
+MA +3339-00735 Africa/Casablanca
+MC +4342+00723 Europe/Monaco
+MD +4700+02850 Europe/Chisinau
+MH +0709+17112 Pacific/Majuro Marshall Islands (most areas)
+MH +0905+16720 Pacific/Kwajalein Kwajalein
+MM +1647+09610 Asia/Yangon
+MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas)
+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 +0410+07330 Indian/Maldives
+MX +1924-09909 America/Mexico_City Central Time
+MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo
+MX +2058-08937 America/Merida Central Time - Campeche, Yucatán
+MX +2540-10019 America/Monterrey Central Time - Durango; Coahuila, Nuevo León, Tamaulipas (most areas)
+MX +2550-09730 America/Matamoros Central Time US - Coahuila, Nuevo León, Tamaulipas (US border)
+MX +2313-10625 America/Mazatlan Mountain Time - Baja California Sur, Nayarit, Sinaloa
+MX +2838-10605 America/Chihuahua Mountain Time - Chihuahua (most areas)
+MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border)
+MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora
+MX +3232-11701 America/Tijuana Pacific Time US - Baja California
+MX +2048-10515 America/Bahia_Banderas Central Time - Bahía de Banderas
+MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula)
+MY +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
+NF -2903+16758 Pacific/Norfolk
+NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE +0627+00324 Africa/Lagos West Africa Time
+NI +1209-08617 America/Managua
+NL +5222+00454 Europe/Amsterdam
+NO,SJ +5955+01045 Europe/Oslo
+NP +2743+08519 Asia/Kathmandu
+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)
+PE -1203-07703 America/Lima
+PF -1732-14934 Pacific/Tahiti Society Islands
+PF -0900-13930 Pacific/Marquesas Marquesas Islands
+PF -2308-13457 Pacific/Gambier Gambier Islands
+PG,AQ -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas), Dumont d'Urville
+PG -0613+15534 Pacific/Bougainville Bougainville
+PH +1435+12100 Asia/Manila
+PK +2452+06703 Asia/Karachi
+PL +5215+02100 Europe/Warsaw
+PM +4703-05620 America/Miquelon
+PN -2504-13005 Pacific/Pitcairn
+PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST
+PS +3130+03428 Asia/Gaza Gaza Strip
+PS +313200+0350542 Asia/Hebron West Bank
+PT +3843-00908 Europe/Lisbon Portugal (mainland)
+PT +3238-01654 Atlantic/Madeira Madeira Islands
+PT +3744-02540 Atlantic/Azores Azores
+PW +0720+13429 Pacific/Palau
+PY -2516-05740 America/Asuncion
+QA,BH +2517+05132 Asia/Qatar
+RE,TF -2052+05528 Indian/Reunion Réunion, Crozet, Scattered Islands
+RO +4426+02606 Europe/Bucharest
+RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade
+RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad
+RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area
+# Mention RU and UA alphabetically. See "territorial claims" above.
+RU,UA +4457+03406 Europe/Simferopol Crimea
+RU +5836+04939 Europe/Kirov MSK+00 - Kirov
+RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd
+RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan
+RU +5134+04602 Europe/Saratov MSK+01 - Saratov
+RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk
+RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia
+RU +5651+06036 Asia/Yekaterinburg MSK+02 - Urals
+RU +5500+07324 Asia/Omsk MSK+03 - Omsk
+RU +5502+08255 Asia/Novosibirsk MSK+04 - Novosibirsk
+RU +5322+08345 Asia/Barnaul MSK+04 - Altai
+RU +5630+08458 Asia/Tomsk MSK+04 - Tomsk
+RU +5345+08707 Asia/Novokuznetsk MSK+04 - Kemerovo
+RU +5601+09250 Asia/Krasnoyarsk MSK+04 - Krasnoyarsk area
+RU +5216+10420 Asia/Irkutsk MSK+05 - Irkutsk, Buryatia
+RU +5203+11328 Asia/Chita MSK+06 - Zabaykalsky
+RU +6200+12940 Asia/Yakutsk MSK+06 - Lena River
+RU +623923+1353314 Asia/Khandyga MSK+06 - Tomponsky, Ust-Maysky
+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 +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 -0932+16012 Pacific/Guadalcanal
+SC -0440+05528 Indian/Mahe
+SD +1536+03232 Africa/Khartoum
+SE +5920+01803 Europe/Stockholm
+SG,MY +0117+10351 Asia/Singapore Singapore, peninsular Malaysia
+SR +0550-05510 America/Paramaribo
+SS +0451+03137 Africa/Juba
+ST +0020+00644 Africa/Sao_Tome
+SV +1342-08912 America/El_Salvador
+SY +3330+03618 Asia/Damascus
+TC +2128-07108 America/Grand_Turk
+TD +1207+01503 Africa/Ndjamena
+TF -492110+0701303 Indian/Kerguelen Kerguelen, St Paul Island, Amsterdam Island
+TH,KH,LA,VN +1345+10031 Asia/Bangkok Indochina (most areas)
+TJ +3835+06848 Asia/Dushanbe
+TK -0922-17114 Pacific/Fakaofo
+TL -0833+12535 Asia/Dili
+TM +3757+05823 Asia/Ashgabat
+TN +3648+01011 Africa/Tunis
+TO -210800-1751200 Pacific/Tongatapu
+TR +4101+02858 Europe/Istanbul
+TV -0831+17913 Pacific/Funafuti
+TW +2503+12130 Asia/Taipei
+UA +5026+03031 Europe/Kiev Ukraine (most areas)
+UA +4837+02218 Europe/Uzhgorod Transcarpathia
+UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk
+UM +1917+16637 Pacific/Wake Wake Island
+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)
+US +364947-0845057 America/Kentucky/Monticello Eastern - KY (Wayne)
+US +394606-0860929 America/Indiana/Indianapolis Eastern - IN (most areas)
+US +384038-0873143 America/Indiana/Vincennes Eastern - IN (Da, Du, K, Mn)
+US +410305-0863611 America/Indiana/Winamac Eastern - IN (Pulaski)
+US +382232-0862041 America/Indiana/Marengo Eastern - IN (Crawford)
+US +382931-0871643 America/Indiana/Petersburg Eastern - IN (Pike)
+US +384452-0850402 America/Indiana/Vevay Eastern - IN (Switzerland)
+US +415100-0873900 America/Chicago Central (most areas)
+US +375711-0864541 America/Indiana/Tell_City Central - IN (Perry)
+US +411745-0863730 America/Indiana/Knox Central - IN (Starke)
+US +450628-0873651 America/Menominee Central - MI (Wisconsin border)
+US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver)
+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 +340308-1181434 America/Los_Angeles Pacific
+US +611305-1495401 America/Anchorage Alaska (most areas)
+US +581807-1342511 America/Juneau Alaska - Juneau area
+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
+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)
+VU -1740+16825 Pacific/Efate
+WF -1318-17610 Pacific/Wallis
+WS -1350-17144 Pacific/Apia
+ZA,LS,SZ -2615+02800 Africa/Johannesburg
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/lib/debug/.build-id/38/e3b7e0e2a10a41edc9163125934364a9f8bd2e.debug -rw-r--r-- root/root /usr/lib/x86_64-linux-gnu/libcctz.so.2.3+git20220321.1.455b5f5 lrwxrwxrwx root/root /usr/lib/x86_64-linux-gnu/libcctz.so.2 -> libcctz.so.2.3+git20220321.1.455b5f5
Files in first set of .debs but not in second
-rw-r--r-- root/root /usr/lib/debug/.build-id/bf/9b289b261659577da4fa60e9156703d025bebf.debug -rw-r--r-- root/root /usr/lib/x86_64-linux-gnu/libcctz.so.2.3+dfsg1 lrwxrwxrwx root/root /usr/lib/x86_64-linux-gnu/libcctz.so.2 -> libcctz.so.2.3+dfsg1
No differences were encountered between the control files of package libcctz-dev
No differences were encountered between the control files of package libcctz-doc
No differences were encountered between the control files of package libcctz2
Control files of package libcctz2-dbgsym: lines which differ (wdiff format)
Build-Ids: bf9b289b261659577da4fa60e9156703d025bebf 38e3b7e0e2a10a41edc9163125934364a9f8bd2e