Commit af788260 by Abseil Team Committed by Derek Mauro

- fd5f3d7077270ffc5ea74cdb9e18bbae3b9b46aa Fix typo optional -> variant by…

  - fd5f3d7077270ffc5ea74cdb9e18bbae3b9b46aa Fix typo optional -> variant by Abseil Team <absl-team@google.com>
  - 9136c06dfa8dbfdde0a427ad3509e34763d607a6 Fix string_view_test and str_cat_test build under MSVC de... by Derek Mauro <dmauro@google.com>
  - a463820f9441888f4368aa87328599e3209f9b07 Removes constexpr optional<T>::operator->(). This was don... by Abseil Team <absl-team@google.com>
  - 3bf78a7f126daafff329f7815d507422f1ca378d Remove dependencies on external CCTZ project. by Shaindel Schwartz <shaindel@google.com>
  - a4ae574a11b1ddf6e88459af3d638cf79aea7ecd Internal change by Jon Cohen <cohenjon@google.com>

GitOrigin-RevId: fd5f3d7077270ffc5ea74cdb9e18bbae3b9b46aa
Change-Id: I6ab8ab99863716fe9b2745a12ef285f7a6da6d1e
parent 94ce52d4
......@@ -34,7 +34,7 @@ function(absl_library)
cmake_parse_arguments(ABSL_LIB
"DISABLE_INSTALL" # keep that in case we want to support installation one day
"TARGET;EXPORT_NAME"
"SOURCES;PUBLIC_LIBRARIES;PRIVATE_COMPILE_FLAGS;PUBLIC_INCLUDE_DIRS;PRIVATE_INCLUDE_DIRS"
"SOURCES;PUBLIC_LIBRARIES;PRIVATE_COMPILE_FLAGS"
${ARGN}
)
......
......@@ -19,10 +19,8 @@ googletest framework
### Step-by-Step Instructions
1. If you haven't done so already, integrate the Abseil dependency
[CCTZ](https://github.com/google/cctz) into your CMake project. Consequently, the
target 'cctz' needs to be declared in your CMake project **before** including Abseil.<br>
Note: If you want to build the Abseil tests, you'll also need [Google Test](https://github.com/google/googletest). To disable Abseil tests, you have to pass
1. If you want to build the Abseil tests, integrate the Abseil dependency
[Google Test](https://github.com/google/googletest) into your CMake project. To disable Abseil tests, you have to pass
`-DBUILD_TESTING=OFF` when configuring your project with CMake.
2. Download Abseil and copy it into a subdirectory in your CMake project or add
......@@ -31,8 +29,7 @@ CMake project.
3. You can then use the CMake command
[`add_subdirectory()`](https://cmake.org/cmake/help/latest/command/add_subdirectory.html)
to include Abseil directly in your CMake project. In addition, it's possible to
customize the name of the `cctz` target with the `-DABSL_CCTZ_TARGET=*my_cctz*` option.
to include Abseil directly in your CMake project.
4. Add the **absl::** target you wish to use to the
[`target_link_libraries()`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html)
......
......@@ -65,15 +65,9 @@ set(ABSL_EXCEPTIONS_FLAG "${CMAKE_CXX_EXCEPTIONS}")
## pthread
find_package(Threads REQUIRED)
if(NOT ABSL_CCTZ_TARGET)
set(ABSL_CCTZ_TARGET cctz)
endif()
# commented: used only for standalone test
# Don't remove these or else CMake CI will break
#add_subdirectory(cctz)
#add_subdirectory(googletest)
check_target(${ABSL_CCTZ_TARGET})
## check targets
if(BUILD_TESTING)
......
......@@ -17,13 +17,6 @@ http_archive(
strip_prefix = "googletest-master",
)
# CCTZ (Time-zone framework).
http_archive(
name = "com_googlesource_code_cctz",
urls = ["https://github.com/google/cctz/archive/master.zip"],
strip_prefix = "cctz-master",
)
# RE2 regular-expression framework. Used by some unit-tests.
http_archive(
name = "com_googlesource_code_re2",
......
......@@ -384,7 +384,7 @@
// ABSL_HAVE_STD_VARIANT
//
// Checks whether C++17 std::optional is available.
// Checks whether C++17 std::variant is available.
#ifdef ABSL_HAVE_STD_VARIANT
#error "ABSL_HAVE_STD_VARIANT cannot be directly set."
#endif
......
......@@ -206,6 +206,8 @@ struct Mallocator {
typedef Mallocator<U> other;
};
Mallocator() = default;
template <class U>
Mallocator(const Mallocator<U>&) {} // NOLINT(runtime/explicit)
T* allocate(size_t n) { return static_cast<T*>(std::malloc(n * sizeof(T))); }
void deallocate(T* p, size_t) { std::free(p); }
......
......@@ -50,6 +50,8 @@ struct Mallocator {
typedef Mallocator<U> other;
};
Mallocator() = default;
template <class U>
Mallocator(const Mallocator<U>&) {} // NOLINT(runtime/explicit)
T* allocate(size_t n) { return static_cast<T*>(std::malloc(n * sizeof(T))); }
void deallocate(T* p, size_t) { std::free(p); }
......
......@@ -44,8 +44,8 @@ cc_library(
"//absl/base",
"//absl/base:core_headers",
"//absl/numeric:int128",
"@com_googlesource_code_cctz//:civil_time",
"@com_googlesource_code_cctz//:time_zone",
"//absl/time/internal/cctz:civil_time",
"//absl/time/internal/cctz:time_zone",
],
)
......@@ -63,7 +63,7 @@ cc_library(
deps = [
":time",
"//absl/base",
"@com_googlesource_code_cctz//:time_zone",
"//absl/time/internal/cctz:time_zone",
],
)
......@@ -91,7 +91,7 @@ cc_test(
"//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/time/internal/cctz:time_zone",
"@com_google_googletest//:gtest_main",
"@com_googlesource_code_cctz//:time_zone",
],
)
......@@ -22,6 +22,10 @@ list(APPEND TIME_PUBLIC_HEADERS
list(APPEND TIME_INTERNAL_HEADERS
"internal/test_util.h"
"internal/cctz/include/cctz/civil_time.h"
"internal/cctz/include/cctz/civil_time_detail.h"
"internal/cctz/include/cctz/time_zone.h"
"internal/cctz/include/cctz/zone_info_source.h"
)
list(APPEND TIME_SRC
......@@ -29,10 +33,27 @@ list(APPEND TIME_SRC
"clock.cc"
"duration.cc"
"format.cc"
"internal/cctz/src/civil_time_detail.cc"
"internal/cctz/src/time_zone_fixed.cc"
"internal/cctz/src/time_zone_fixed.h"
"internal/cctz/src/time_zone_format.cc"
"internal/cctz/src/time_zone_if.cc"
"internal/cctz/src/time_zone_if.h"
"internal/cctz/src/time_zone_impl.cc"
"internal/cctz/src/time_zone_impl.h"
"internal/cctz/src/time_zone_info.cc"
"internal/cctz/src/time_zone_info.h"
"internal/cctz/src/time_zone_libc.cc"
"internal/cctz/src/time_zone_libc.h"
"internal/cctz/src/time_zone_lookup.cc"
"internal/cctz/src/time_zone_posix.cc"
"internal/cctz/src/time_zone_posix.h"
"internal/cctz/src/tzfile.h"
"internal/cctz/src/zone_info_source.cc"
${TIME_PUBLIC_HEADERS}
${TIME_INTERNAL_HEADERS}
)
set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128 ${ABSL_CCTZ_TARGET})
set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128)
absl_library(
TARGET
......@@ -41,8 +62,6 @@ absl_library(
${TIME_SRC}
PUBLIC_LIBRARIES
${TIME_PUBLIC_LIBRARIES}
PUBLIC_INCLUDE_DIRS
${CCTZ_INCLUDE_DIRS}
EXPORT_NAME
time
)
......
......@@ -16,8 +16,10 @@
#include <cctype>
#include <cstdint>
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#include "absl/time/time.h"
#include "cctz/time_zone.h"
namespace cctz = absl::time_internal::cctz;
namespace absl {
......
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"]) # Apache License
### libraries
cc_library(
name = "includes",
textual_hdrs = [
"include/cctz/civil_time.h",
"include/cctz/civil_time_detail.h",
"include/cctz/time_zone.h",
],
visibility = ["//absl/time:__pkg__"],
)
cc_library(
name = "civil_time",
srcs = ["src/civil_time_detail.cc"],
hdrs = [
"include/cctz/civil_time.h",
],
textual_hdrs = ["include/cctz/civil_time_detail.h"],
visibility = ["//visibility:public"],
)
cc_library(
name = "time_zone",
srcs = [
"src/time_zone_fixed.cc",
"src/time_zone_fixed.h",
"src/time_zone_format.cc",
"src/time_zone_if.cc",
"src/time_zone_if.h",
"src/time_zone_impl.cc",
"src/time_zone_impl.h",
"src/time_zone_info.cc",
"src/time_zone_info.h",
"src/time_zone_libc.cc",
"src/time_zone_libc.h",
"src/time_zone_lookup.cc",
"src/time_zone_posix.cc",
"src/time_zone_posix.h",
"src/tzfile.h",
"src/zone_info_source.cc",
],
hdrs = [
"include/cctz/time_zone.h",
"include/cctz/zone_info_source.h",
],
visibility = ["//visibility:public"],
deps = [":civil_time"],
)
### tests
cc_test(
name = "civil_time_test",
size = "small",
srcs = ["src/civil_time_test.cc"],
deps = [
":civil_time",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "time_zone_format_test",
size = "small",
srcs = ["src/time_zone_format_test.cc"],
deps = [
":civil_time",
":time_zone",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "time_zone_lookup_test",
size = "small",
srcs = ["src/time_zone_lookup_test.cc"],
deps = [
":civil_time",
":time_zone",
"@com_google_googletest//:gtest_main",
],
)
### benchmarks
### examples
### binaries
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
#define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h"
namespace absl {
namespace time_internal {
namespace cctz {
// The term "civil time" refers to the legally recognized human-scale time
// that is represented by the six fields YYYY-MM-DD hh:mm:ss. Modern-day civil
// time follows the Gregorian Calendar and is a time-zone-independent concept.
// A "date" is perhaps the most common example of a civil time (represented in
// this library as cctz::civil_day). This library provides six classes and a
// handful of functions that help with rounding, iterating, and arithmetic on
// civil times while avoiding complications like daylight-saving time (DST).
//
// The following six classes form the core of this civil-time library:
//
// * civil_second
// * civil_minute
// * civil_hour
// * civil_day
// * civil_month
// * civil_year
//
// Each class is a simple value type with the same interface for construction
// and the same six accessors for each of the civil fields (year, month, day,
// hour, minute, and second, aka YMDHMS). These classes differ only in their
// alignment, which is indicated by the type name and specifies the field on
// which arithmetic operates.
//
// Each class can be constructed by passing up to six optional integer
// arguments representing the YMDHMS fields (in that order) to the
// constructor. Omitted fields are assigned their minimum valid value. Hours,
// minutes, and seconds will be set to 0, month and day will be set to 1, and
// since there is no minimum valid year, it will be set to 1970. So, a
// default-constructed civil-time object will have YMDHMS fields representing
// "1970-01-01 00:00:00". Fields that are out-of-range are normalized (e.g.,
// October 32 -> November 1) so that all civil-time objects represent valid
// values.
//
// Each civil-time class is aligned to the civil-time field indicated in the
// class's name after normalization. Alignment is performed by setting all the
// inferior fields to their minimum valid value (as described above). The
// following are examples of how each of the six types would align the fields
// representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the
// std::string format used here is not important; it's just a shorthand way of
// showing the six YMDHMS fields.)
//
// civil_second 2015-11-22 12:34:56
// civil_minute 2015-11-22 12:34:00
// civil_hour 2015-11-22 12:00:00
// civil_day 2015-11-22 00:00:00
// civil_month 2015-11-01 00:00:00
// civil_year 2015-01-01 00:00:00
//
// Each civil-time type performs arithmetic on the field to which it is
// aligned. This means that adding 1 to a civil_day increments the day field
// (normalizing as necessary), and subtracting 7 from a civil_month operates
// on the month field (normalizing as necessary). All arithmetic produces a
// valid civil time. Difference requires two similarly aligned civil-time
// objects and returns the scalar answer in units of the objects' alignment.
// For example, the difference between two civil_hour objects will give an
// answer in units of civil hours.
//
// In addition to the six civil-time types just described, there are
// a handful of helper functions and algorithms for performing common
// calculations. These are described below.
//
// Note: In C++14 and later, this library is usable in a constexpr context.
//
// CONSTRUCTION:
//
// Each of the civil-time types can be constructed in two ways: by directly
// passing to the constructor up to six (optional) integers representing the
// YMDHMS fields, or by copying the YMDHMS fields from a differently aligned
// civil-time type.
//
// civil_day default_value; // 1970-01-01 00:00:00
//
// civil_day a(2015, 2, 3); // 2015-02-03 00:00:00
// civil_day b(2015, 2, 3, 4, 5, 6); // 2015-02-03 00:00:00
// civil_day c(2015); // 2015-01-01 00:00:00
//
// civil_second ss(2015, 2, 3, 4, 5, 6); // 2015-02-03 04:05:06
// civil_minute mm(ss); // 2015-02-03 04:05:00
// civil_hour hh(mm); // 2015-02-03 04:00:00
// civil_day d(hh); // 2015-02-03 00:00:00
// civil_month m(d); // 2015-02-01 00:00:00
// civil_year y(m); // 2015-01-01 00:00:00
//
// m = civil_month(y); // 2015-01-01 00:00:00
// d = civil_day(m); // 2015-01-01 00:00:00
// hh = civil_hour(d); // 2015-01-01 00:00:00
// mm = civil_minute(hh); // 2015-01-01 00:00:00
// ss = civil_second(mm); // 2015-01-01 00:00:00
//
// ALIGNMENT CONVERSION:
//
// The alignment of a civil-time object cannot change, but the object may be
// used to construct a new object with a different alignment. This is referred
// to as "realigning". When realigning to a type with the same or more
// precision (e.g., civil_day -> civil_second), the conversion may be
// performed implicitly since no information is lost. However, if information
// could be discarded (e.g., civil_second -> civil_day), the conversion must
// be explicit at the call site.
//
// void fun(const civil_day& day);
//
// civil_second cs;
// fun(cs); // Won't compile because data may be discarded
// fun(civil_day(cs)); // OK: explicit conversion
//
// civil_day cd;
// fun(cd); // OK: no conversion needed
//
// civil_month cm;
// fun(cm); // OK: implicit conversion to civil_day
//
// NORMALIZATION:
//
// Integer arguments passed to the constructor may be out-of-range, in which
// case they are normalized to produce a valid civil-time object. This enables
// natural arithmetic on constructor arguments without worrying about the
// field's range. Normalization guarantees that there are no invalid
// civil-time objects.
//
// civil_day d(2016, 10, 32); // Out-of-range day; normalized to 2016-11-01
//
// Note: If normalization is undesired, you can signal an error by comparing
// the constructor arguments to the normalized values returned by the YMDHMS
// properties.
//
// PROPERTIES:
//
// 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.
//
// civil_day d(2015, 6, 28);
// // d.year() == 2015
// // d.month() == 6
// // d.day() == 28
// // d.hour() == 0
// // d.minute() == 0
// // d.second() == 0
//
// COMPARISON:
//
// Comparison always considers all six YMDHMS fields, regardless of the type's
// alignment. Comparison between differently aligned civil-time types is
// allowed.
//
// civil_day feb_3(2015, 2, 3); // 2015-02-03 00:00:00
// civil_day mar_4(2015, 3, 4); // 2015-03-04 00:00:00
// // feb_3 < mar_4
// // civil_year(feb_3) == civil_year(mar_4)
//
// civil_second feb_3_noon(2015, 2, 3, 12, 0, 0); // 2015-02-03 12:00:00
// // feb_3 < feb_3_noon
// // feb_3 == civil_day(feb_3_noon)
//
// // Iterates all the days of February 2015.
// for (civil_day d(2015, 2, 1); d < civil_month(2015, 3); ++d) {
// // ...
// }
//
// STREAMING:
//
// Each civil-time type may be sent to an output stream using operator<<().
// The output format follows the pattern "YYYY-MM-DDThh:mm:ss" where fields
// inferior to the type's alignment are omitted.
//
// civil_second cs(2015, 2, 3, 4, 5, 6);
// std::cout << cs << "\n"; // Outputs: 2015-02-03T04:05:06
//
// civil_day cd(cs);
// std::cout << cd << "\n"; // Outputs: 2015-02-03
//
// civil_year cy(cs);
// std::cout << cy << "\n"; // Outputs: 2015
//
// ARITHMETIC:
//
// Civil-time types support natural arithmetic operators such as addition,
// subtraction, and difference. Arithmetic operates on the civil-time field
// indicated in the type's name. Difference requires arguments with the same
// alignment and returns the answer in units of the alignment.
//
// civil_day a(2015, 2, 3);
// ++a; // 2015-02-04 00:00:00
// --a; // 2015-02-03 00:00:00
// civil_day b = a + 1; // 2015-02-04 00:00:00
// civil_day c = 1 + b; // 2015-02-05 00:00:00
// int n = c - a; // n = 2 (civil days)
// int m = c - civil_month(c); // Won't compile: different types.
//
// EXAMPLE: Adding a month to January 31.
//
// One of the classic questions that arises when considering a civil-time
// library (or a date library or a date/time library) is this: "What happens
// when you add a month to January 31?" This is an interesting question
// because there could be a number of possible answers:
//
// 1. March 3 (or 2 if a leap year). This may make sense if the operation
// wants the equivalent of February 31.
// 2. February 28 (or 29 if a leap year). This may make sense if the operation
// wants the last day of January to go to the last day of February.
// 3. Error. The caller may get some error, an exception, an invalid date
// object, or maybe false is returned. This may make sense because there is
// no single unambiguously correct answer to the question.
//
// Practically speaking, any answer that is not what the programmer intended
// is the wrong answer.
//
// This civil-time library avoids the problem by making it impossible to ask
// ambiguous questions. All civil-time objects are aligned to a particular
// civil-field boundary (such as aligned to a year, month, day, hour, minute,
// or second), and arithmetic operates on the field to which the object is
// aligned. This means that in order to "add a month" the object must first be
// aligned to a month boundary, which is equivalent to the first day of that
// month.
//
// Of course, there are ways to compute an answer the question at hand using
// this civil-time library, but they require the programmer to be explicit
// about the answer they expect. To illustrate, let's see how to compute all
// three of the above possible answers to the question of "Jan 31 plus 1
// month":
//
// const civil_day d(2015, 1, 31);
//
// // Answer 1:
// // Add 1 to the month field in the constructor, and rely on normalization.
// const auto ans_normalized = civil_day(d.year(), d.month() + 1, d.day());
// // ans_normalized == 2015-03-03 (aka Feb 31)
//
// // Answer 2:
// // Add 1 to month field, capping to the end of next month.
// const auto next_month = civil_month(d) + 1;
// const auto last_day_of_next_month = civil_day(next_month + 1) - 1;
// const auto ans_capped = std::min(ans_normalized, last_day_of_next_month);
// // ans_capped == 2015-02-28
//
// // Answer 3:
// // Signal an error if the normalized answer is not in next month.
// if (civil_month(ans_normalized) != next_month) {
// // error, month overflow
// }
//
using civil_year = detail::civil_year;
using civil_month = detail::civil_month;
using civil_day = detail::civil_day;
using civil_hour = detail::civil_hour;
using civil_minute = detail::civil_minute;
using civil_second = detail::civil_second;
// An enum class with members monday, tuesday, wednesday, thursday, friday,
// saturday, and sunday. These enum values may be sent to an output stream
// using operator<<(). The result is the full weekday name in English with a
// leading capital letter.
//
// weekday wd = weekday::thursday;
// std::cout << wd << "\n"; // Outputs: Thursday
//
using detail::weekday;
// Returns the weekday for the given civil_day.
//
// civil_day a(2015, 8, 13);
// weekday wd = get_weekday(a); // wd == weekday::thursday
//
using detail::get_weekday;
// Returns the civil_day that strictly follows or precedes the given
// civil_day, and that falls on the given weekday.
//
// For example, given:
//
// August 2015
// Su Mo Tu We Th Fr Sa
// 1
// 2 3 4 5 6 7 8
// 9 10 11 12 13 14 15
// 16 17 18 19 20 21 22
// 23 24 25 26 27 28 29
// 30 31
//
// civil_day a(2015, 8, 13); // get_weekday(a) == weekday::thursday
// civil_day b = next_weekday(a, weekday::thursday); // b = 2015-08-20
// civil_day c = prev_weekday(a, weekday::thursday); // c = 2015-08-06
//
// civil_day d = ...
// // Gets the following Thursday if d is not already Thursday
// civil_day thurs1 = prev_weekday(d, weekday::thursday) + 7;
// // Gets the previous Thursday if d is not already Thursday
// civil_day thurs2 = next_weekday(d, weekday::thursday) - 7;
//
using detail::next_weekday;
using detail::prev_weekday;
// Returns the day-of-year for the given civil_day.
//
// civil_day a(2015, 1, 1);
// int yd_jan_1 = get_yearday(a); // yd_jan_1 = 1
// civil_day b(2015, 12, 31);
// int yd_dec_31 = get_yearday(b); // yd_dec_31 = 365
//
using detail::get_yearday;
} // namespace cctz
} // namespace time_internal
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
#define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
#include <cstdint>
#include <limits>
#include <ostream>
#include <type_traits>
// Disable constexpr support unless we are using clang in C++14 mode.
#if __clang__ && __cpp_constexpr >= 201304
#define CONSTEXPR_D constexpr // data
#define CONSTEXPR_F constexpr // function
#define CONSTEXPR_M constexpr // member
#else
#define CONSTEXPR_D const
#define CONSTEXPR_F inline
#define CONSTEXPR_M
#endif
namespace absl {
namespace time_internal {
namespace cctz {
// Support years that at least span the range of 64-bit time_t values.
using year_t = std::int_fast64_t;
// Type alias that indicates an argument is not normalized (e.g., the
// constructor parameters and operands/results of addition/subtraction).
using diff_t = std::int_fast64_t;
namespace detail {
// Type aliases that indicate normalized argument values.
using month_t = std::int_fast8_t; // [1:12]
using day_t = std::int_fast8_t; // [1:31]
using hour_t = std::int_fast8_t; // [0:23]
using minute_t = std::int_fast8_t; // [0:59]
using second_t = std::int_fast8_t; // [0:59]
// Normalized civil-time fields: Y-M-D HH:MM:SS.
struct fields {
CONSTEXPR_M fields(year_t year, month_t month, day_t day,
hour_t hour, minute_t minute, second_t second)
: y(year), m(month), d(day), hh(hour), mm(minute), ss(second) {}
std::int_least64_t y;
std::int_least8_t m;
std::int_least8_t d;
std::int_least8_t hh;
std::int_least8_t mm;
std::int_least8_t ss;
};
struct second_tag {};
struct minute_tag : second_tag {};
struct hour_tag : minute_tag {};
struct day_tag : hour_tag {};
struct month_tag : day_tag {};
struct year_tag : month_tag {};
////////////////////////////////////////////////////////////////////////
// Field normalization (without avoidable overflow).
namespace impl {
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;
}
CONSTEXPR_F int days_per_century(year_t y, month_t m) noexcept {
const int yi = year_index(y, m);
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);
return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96);
}
CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept {
return is_leap_year(y + (m > 2)) ? 366 : 365;
}
CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept {
CONSTEXPR_D int k_days_per_month[1 + 12] = {
-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // non leap year
};
return k_days_per_month[m] + (m == 2 && is_leap_year(y));
}
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;
cd %= 146097;
if (cd < 0) {
y -= 400;
cd += 146097;
}
y += (d / 146097) * 400;
d = d % 146097 + cd;
if (d > 0) {
if (d > 146097) {
y += 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);
} else {
y -= 400;
d += 146097;
}
}
if (d > 365) {
for (int n = days_per_century(y, m); d > n; n = days_per_century(y, m)) {
d -= n;
y += 100;
}
for (int n = days_per_4years(y, m); d > n; n = days_per_4years(y, m)) {
d -= n;
y += 4;
}
for (int n = days_per_year(y, m); d > n; n = days_per_year(y, m)) {
d -= n;
++y;
}
}
if (d > 28) {
for (int n = days_per_month(y, m); d > n; n = days_per_month(y, m)) {
d -= n;
if (++m > 12) {
++y;
m = 1;
}
}
}
return fields(y, 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 {
if (m != 12) {
y += m / 12;
m %= 12;
if (m <= 0) {
y -= 1;
m += 12;
}
}
return n_day(y, static_cast<month_t>(m), d, cd, hh, mm, ss);
}
CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd,
diff_t hh, minute_t mm, second_t ss) noexcept {
cd += hh / 24;
hh %= 24;
if (hh < 0) {
cd -= 1;
hh += 24;
}
return n_mon(y, m, d, cd, static_cast<hour_t>(hh), mm, ss);
}
CONSTEXPR_F fields n_min(year_t y, diff_t m, diff_t d, diff_t hh, diff_t ch,
diff_t mm, second_t ss) noexcept {
ch += mm / 60;
mm %= 60;
if (mm < 0) {
ch -= 1;
mm += 60;
}
return n_hour(y, m, d, hh / 24 + ch / 24, hh % 24 + ch % 24,
static_cast<minute_t>(mm), ss);
}
CONSTEXPR_F fields n_sec(year_t y, diff_t m, diff_t d, diff_t hh, diff_t mm,
diff_t ss) noexcept {
// Optimization for when (non-constexpr) fields are already normalized.
if (0 <= ss && ss < 60) {
const second_t nss = static_cast<second_t>(ss);
if (0 <= mm && mm < 60) {
const minute_t nmm = static_cast<minute_t>(mm);
if (0 <= hh && hh < 24) {
const hour_t nhh = static_cast<hour_t>(hh);
if (1 <= d && d <= 28 && 1 <= m && m <= 12) {
const day_t nd = static_cast<day_t>(d);
const month_t nm = static_cast<month_t>(m);
return fields(y, nm, nd, nhh, nmm, nss);
}
return n_mon(y, m, d, 0, nhh, nmm, nss);
}
return n_hour(y, m, d, hh / 24, hh % 24, nmm, nss);
}
return n_min(y, m, d, hh, mm / 60, mm % 60, nss);
}
diff_t cm = ss / 60;
ss %= 60;
if (ss < 0) {
cm -= 1;
ss += 60;
}
return n_min(y, m, d, hh, mm / 60 + cm / 60, mm % 60 + cm % 60,
static_cast<second_t>(ss));
}
} // namespace impl
////////////////////////////////////////////////////////////////////////
// Increments the indicated (normalized) field by "n".
CONSTEXPR_F fields step(second_tag, fields f, diff_t n) noexcept {
return impl::n_sec(f.y, f.m, f.d, f.hh, f.mm + n / 60, f.ss + n % 60);
}
CONSTEXPR_F fields step(minute_tag, fields f, diff_t n) noexcept {
return impl::n_min(f.y, f.m, f.d, f.hh + n / 60, 0, f.mm + n % 60, f.ss);
}
CONSTEXPR_F fields step(hour_tag, fields f, diff_t n) noexcept {
return impl::n_hour(f.y, f.m, f.d + n / 24, 0, f.hh + n % 24, f.mm, f.ss);
}
CONSTEXPR_F fields step(day_tag, fields f, diff_t n) noexcept {
return impl::n_day(f.y, f.m, f.d, n, f.hh, f.mm, f.ss);
}
CONSTEXPR_F fields step(month_tag, fields f, diff_t n) noexcept {
return impl::n_mon(f.y + n / 12, f.m + n % 12, f.d, 0, f.hh, f.mm, f.ss);
}
CONSTEXPR_F fields step(year_tag, fields f, diff_t n) noexcept {
return fields(f.y + n, f.m, f.d, f.hh, f.mm, f.ss);
}
////////////////////////////////////////////////////////////////////////
namespace impl {
// Returns (v * f + a) but avoiding intermediate overflow when possible.
CONSTEXPR_F diff_t scale_add(diff_t v, diff_t f, diff_t a) noexcept {
return (v < 0) ? ((v + 1) * f + a) - f : ((v - 1) * f + a) + f;
}
// Map a (normalized) Y/M/D to the number of days before/after 1970-01-01.
// Probably overflows for years outside [-292277022656:292277026595].
CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept {
const diff_t eyear = (m <= 2) ? y - 1 : y;
const diff_t era = (eyear >= 0 ? eyear : eyear - 399) / 400;
const diff_t yoe = eyear - era * 400;
const diff_t doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
const diff_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
return era * 146097 + doe - 719468;
}
// Returns the difference in days between two normalized Y-M-D tuples.
// ymd_ord() will encounter integer overflow given extreme year values,
// yet the difference between two such extreme values may actually be
// small, so we take a little care to avoid overflow when possible by
// exploiting the 146097-day cycle.
CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1,
year_t y2, month_t m2, day_t d2) noexcept {
const diff_t a_c4_off = y1 % 400;
const diff_t b_c4_off = y2 % 400;
diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off);
diff_t delta = ymd_ord(a_c4_off, m1, d1) - ymd_ord(b_c4_off, m2, d2);
if (c4_diff > 0 && delta < 0) {
delta += 2 * 146097;
c4_diff -= 2 * 400;
} else if (c4_diff < 0 && delta > 0) {
delta -= 2 * 146097;
c4_diff += 2 * 400;
}
return (c4_diff / 400 * 146097) + delta;
}
} // namespace impl
// Returns the difference between fields structs using the indicated unit.
CONSTEXPR_F diff_t difference(year_tag, fields f1, fields f2) noexcept {
return f1.y - f2.y;
}
CONSTEXPR_F diff_t difference(month_tag, fields f1, fields f2) noexcept {
return impl::scale_add(difference(year_tag{}, f1, f2), 12, (f1.m - f2.m));
}
CONSTEXPR_F diff_t difference(day_tag, fields f1, fields f2) noexcept {
return impl::day_difference(f1.y, f1.m, f1.d, f2.y, f2.m, f2.d);
}
CONSTEXPR_F diff_t difference(hour_tag, fields f1, fields f2) noexcept {
return impl::scale_add(difference(day_tag{}, f1, f2), 24, (f1.hh - f2.hh));
}
CONSTEXPR_F diff_t difference(minute_tag, fields f1, fields f2) noexcept {
return impl::scale_add(difference(hour_tag{}, f1, f2), 60, (f1.mm - f2.mm));
}
CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept {
return impl::scale_add(difference(minute_tag{}, f1, f2), 60, f1.ss - f2.ss);
}
////////////////////////////////////////////////////////////////////////
// Aligns the (normalized) fields struct to the indicated field.
CONSTEXPR_F fields align(second_tag, fields f) noexcept {
return f;
}
CONSTEXPR_F fields align(minute_tag, fields f) noexcept {
return fields{f.y, f.m, f.d, f.hh, f.mm, 0};
}
CONSTEXPR_F fields align(hour_tag, fields f) noexcept {
return fields{f.y, f.m, f.d, f.hh, 0, 0};
}
CONSTEXPR_F fields align(day_tag, fields f) noexcept {
return fields{f.y, f.m, f.d, 0, 0, 0};
}
CONSTEXPR_F fields align(month_tag, fields f) noexcept {
return fields{f.y, f.m, 1, 0, 0, 0};
}
CONSTEXPR_F fields align(year_tag, fields f) noexcept {
return fields{f.y, 1, 1, 0, 0, 0};
}
////////////////////////////////////////////////////////////////////////
template <typename T>
class civil_time {
public:
explicit CONSTEXPR_M civil_time(year_t y, diff_t m = 1, diff_t d = 1,
diff_t hh = 0, diff_t mm = 0,
diff_t ss = 0) noexcept
: civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {}
CONSTEXPR_M civil_time() noexcept : f_{1970, 1, 1, 0, 0, 0} {}
civil_time(const civil_time&) = default;
civil_time& operator=(const civil_time&) = default;
// Conversion between civil times of different alignment. Conversion to
// a more precise alignment is allowed implicitly (e.g., day -> hour),
// but conversion where information is discarded must be explicit
// (e.g., second -> minute).
template <typename U, typename S>
using preserves_data =
typename std::enable_if<std::is_base_of<U, S>::value>::type;
template <typename U>
CONSTEXPR_M civil_time(const civil_time<U>& ct,
preserves_data<T, U>* = nullptr) noexcept
: civil_time(ct.f_) {}
template <typename U>
explicit CONSTEXPR_M civil_time(const civil_time<U>& ct,
preserves_data<U, T>* = nullptr) noexcept
: civil_time(ct.f_) {}
// Factories for the maximum/minimum representable civil_time.
static civil_time max() {
const auto max_year = std::numeric_limits<std::int_least64_t>::max();
return civil_time(max_year, 12, 31, 23, 59, 59);
}
static civil_time min() {
const auto min_year = std::numeric_limits<std::int_least64_t>::min();
return civil_time(min_year, 1, 1, 0, 0, 0);
}
// Field accessors. Note: All but year() return an int.
CONSTEXPR_M year_t year() const noexcept { return f_.y; }
CONSTEXPR_M int month() const noexcept { return f_.m; }
CONSTEXPR_M int day() const noexcept { return f_.d; }
CONSTEXPR_M int hour() const noexcept { return f_.hh; }
CONSTEXPR_M int minute() const noexcept { return f_.mm; }
CONSTEXPR_M int second() const noexcept { return f_.ss; }
// Assigning arithmetic.
CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept {
f_ = step(T{}, f_, n);
return *this;
}
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;
}
CONSTEXPR_M civil_time& operator++() noexcept {
return *this += 1;
}
CONSTEXPR_M civil_time operator++(int) noexcept {
const civil_time a = *this;
++*this;
return a;
}
CONSTEXPR_M civil_time& operator--() noexcept {
return *this -= 1;
}
CONSTEXPR_M civil_time operator--(int) noexcept {
const civil_time a = *this;
--*this;
return a;
}
// Binary arithmetic operators.
inline friend CONSTEXPR_M civil_time operator+(civil_time a,
diff_t n) noexcept {
return a += n;
}
inline friend CONSTEXPR_M civil_time operator+(diff_t n,
civil_time a) noexcept {
return a += n;
}
inline friend CONSTEXPR_M civil_time operator-(civil_time a,
diff_t n) noexcept {
return a -= n;
}
inline friend CONSTEXPR_M diff_t operator-(const civil_time& lhs,
const civil_time& rhs) noexcept {
return difference(T{}, lhs.f_, rhs.f_);
}
private:
// All instantiations of this template are allowed to call the following
// private constructor and access the private fields member.
template <typename U>
friend class civil_time;
// The designated constructor that all others eventually call.
explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {}
fields f_;
};
// Disallows difference between differently aligned types.
// auto n = civil_day(...) - civil_hour(...); // would be confusing.
template <typename Tag1, typename Tag2>
CONSTEXPR_F diff_t operator-(civil_time<Tag1>, civil_time<Tag2>) = delete;
using civil_year = civil_time<year_tag>;
using civil_month = civil_time<month_tag>;
using civil_day = civil_time<day_tag>;
using civil_hour = civil_time<hour_tag>;
using civil_minute = civil_time<minute_tag>;
using civil_second = civil_time<second_tag>;
////////////////////////////////////////////////////////////////////////
// Relational operators that work with differently aligned objects.
// Always compares all six fields.
template <typename T1, typename T2>
CONSTEXPR_F bool operator<(const civil_time<T1>& lhs,
const civil_time<T2>& rhs) noexcept {
return (lhs.year() < rhs.year() ||
(lhs.year() == rhs.year() &&
(lhs.month() < rhs.month() ||
(lhs.month() == rhs.month() &&
(lhs.day() < rhs.day() ||
(lhs.day() == rhs.day() &&
(lhs.hour() < rhs.hour() ||
(lhs.hour() == rhs.hour() &&
(lhs.minute() < rhs.minute() ||
(lhs.minute() == rhs.minute() &&
(lhs.second() < rhs.second())))))))))));
}
template <typename T1, typename T2>
CONSTEXPR_F bool operator<=(const civil_time<T1>& lhs,
const civil_time<T2>& rhs) noexcept {
return !(rhs < lhs);
}
template <typename T1, typename T2>
CONSTEXPR_F bool operator>=(const civil_time<T1>& lhs,
const civil_time<T2>& rhs) noexcept {
return !(lhs < rhs);
}
template <typename T1, typename T2>
CONSTEXPR_F bool operator>(const civil_time<T1>& lhs,
const civil_time<T2>& rhs) noexcept {
return rhs < lhs;
}
template <typename T1, typename T2>
CONSTEXPR_F bool operator==(const civil_time<T1>& lhs,
const civil_time<T2>& rhs) noexcept {
return lhs.year() == rhs.year() && lhs.month() == rhs.month() &&
lhs.day() == rhs.day() && lhs.hour() == rhs.hour() &&
lhs.minute() == rhs.minute() && lhs.second() == rhs.second();
}
template <typename T1, typename T2>
CONSTEXPR_F bool operator!=(const civil_time<T1>& lhs,
const civil_time<T2>& rhs) noexcept {
return !(lhs == rhs);
}
////////////////////////////////////////////////////////////////////////
enum class weekday {
monday,
tuesday,
wednesday,
thursday,
friday,
saturday,
sunday,
};
CONSTEXPR_F weekday get_weekday(const civil_day& cd) noexcept {
CONSTEXPR_D weekday k_weekday_by_sun_off[7] = {
weekday::sunday, weekday::monday, weekday::tuesday,
weekday::wednesday, weekday::thursday, weekday::friday,
weekday::saturday,
};
CONSTEXPR_D int k_weekday_offsets[1 + 12] = {
-1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4,
};
year_t wd = cd.year() - (cd.month() < 3);
if (wd >= 0) {
wd += wd / 4 - wd / 100 + wd / 400;
} else {
wd += (wd - 3) / 4 - (wd - 99) / 100 + (wd - 399) / 400;
}
wd += k_weekday_offsets[cd.month()] + cd.day();
return k_weekday_by_sun_off[(wd % 7 + 7) % 7];
}
////////////////////////////////////////////////////////////////////////
CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept {
do { cd += 1; } while (get_weekday(cd) != wd);
return cd;
}
CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept {
do { cd -= 1; } while (get_weekday(cd) != wd);
return cd;
}
CONSTEXPR_F int get_yearday(const civil_day& cd) 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();
}
////////////////////////////////////////////////////////////////////////
std::ostream& operator<<(std::ostream& os, const civil_year& y);
std::ostream& operator<<(std::ostream& os, const civil_month& m);
std::ostream& operator<<(std::ostream& os, const civil_day& d);
std::ostream& operator<<(std::ostream& os, const civil_hour& h);
std::ostream& operator<<(std::ostream& os, const civil_minute& m);
std::ostream& operator<<(std::ostream& os, const civil_second& s);
std::ostream& operator<<(std::ostream& os, weekday wd);
} // namespace detail
} // namespace cctz
} // namespace time_internal
} // namespace absl
#undef CONSTEXPR_M
#undef CONSTEXPR_F
#undef CONSTEXPR_D
#endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// A library for translating between absolute times (represented by
// std::chrono::time_points of the std::chrono::system_clock) and civil
// times (represented by cctz::civil_second) using the rules defined by
// a time zone (cctz::time_zone).
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
#include <chrono>
#include <cstdint>
#include <string>
#include <utility>
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
namespace absl {
namespace time_internal {
namespace cctz {
// Convenience aliases. Not intended as public API points.
template <typename D>
using time_point = std::chrono::time_point<std::chrono::system_clock, D>;
using sys_seconds = std::chrono::duration<std::int_fast64_t>;
namespace detail {
template <typename D>
inline std::pair<time_point<sys_seconds>, D>
split_seconds(const time_point<D>& tp) {
auto sec = std::chrono::time_point_cast<sys_seconds>(tp);
auto sub = tp - sec;
if (sub.count() < 0) {
sec -= sys_seconds(1);
sub += sys_seconds(1);
}
return {sec, std::chrono::duration_cast<D>(sub)};
}
inline std::pair<time_point<sys_seconds>, sys_seconds>
split_seconds(const time_point<sys_seconds>& tp) {
return {tp, sys_seconds(0)};
}
} // namespace detail
// cctz::time_zone is an opaque, small, value-type class representing a
// geo-political region within which particular rules are used for mapping
// between absolute and civil times. Time zones are named using the TZ
// identifiers from the IANA Time Zone Database, such as "America/Los_Angeles"
// or "Australia/Sydney". Time zones are created from factory functions such
// as load_time_zone(). Note: strings like "PST" and "EDT" are not valid TZ
// identifiers.
//
// Example:
// cctz::time_zone utc = cctz::utc_time_zone();
// cctz::time_zone pst = cctz::fixed_time_zone(std::chrono::hours(-8));
// cctz::time_zone loc = cctz::local_time_zone();
// cctz::time_zone lax;
// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
//
// See also:
// - http://www.iana.org/time-zones
// - http://en.wikipedia.org/wiki/Zoneinfo
class time_zone {
public:
time_zone() : time_zone(nullptr) {} // Equivalent to UTC
time_zone(const time_zone&) = default;
time_zone& operator=(const time_zone&) = default;
std::string name() const;
// An absolute_lookup represents the civil time (cctz::civil_second) within
// this time_zone at the given absolute time (time_point). There are
// additionally a few other fields that may be useful when working with
// older APIs, such as std::tm.
//
// Example:
// const cctz::time_zone tz = ...
// const auto tp = std::chrono::system_clock::now();
// const cctz::time_zone::absolute_lookup al = tz.lookup(tp);
struct absolute_lookup {
civil_second cs;
// Note: The following fields exist for backward compatibility with older
// APIs. Accessing these fields directly is a sign of imprudent logic in
// the calling code. Modern time-related code should only access this data
// indirectly by way of cctz::format().
int offset; // civil seconds east of UTC
bool is_dst; // is offset non-standard?
const char* abbr; // time-zone abbreviation (e.g., "PST")
};
absolute_lookup lookup(const time_point<sys_seconds>& tp) const;
template <typename D>
absolute_lookup lookup(const time_point<D>& tp) const {
return lookup(detail::split_seconds(tp).first);
}
// A civil_lookup represents the absolute time(s) (time_point) that
// correspond to the given civil time (cctz::civil_second) within this
// time_zone. Usually the given civil time represents a unique instant
// in time, in which case the conversion is unambiguous. However,
// within this time zone, the given civil time may be skipped (e.g.,
// during a positive UTC offset shift), or repeated (e.g., during a
// negative UTC offset shift). To account for these possibilities,
// civil_lookup is richer than just a single time_point.
//
// In all cases the civil_lookup::kind enum will indicate the nature
// of the given civil-time argument, and the pre, trans, and post
// members will give the absolute time answers using the pre-transition
// offset, the transition point itself, and the post-transition offset,
// respectively (all three times are equal if kind == UNIQUE). If any
// of these three absolute times is outside the representable range of a
// time_point<sys_seconds> the field is set to its maximum/minimum value.
//
// Example:
// cctz::time_zone lax;
// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
//
// // A unique civil time.
// auto jan01 = lax.lookup(cctz::civil_second(2011, 1, 1, 0, 0, 0));
// // jan01.kind == cctz::time_zone::civil_lookup::UNIQUE
// // jan01.pre is 2011/01/01 00:00:00 -0800
// // jan01.trans is 2011/01/01 00:00:00 -0800
// // jan01.post is 2011/01/01 00:00:00 -0800
//
// // A Spring DST transition, when there is a gap in civil time.
// auto mar13 = lax.lookup(cctz::civil_second(2011, 3, 13, 2, 15, 0));
// // mar13.kind == cctz::time_zone::civil_lookup::SKIPPED
// // mar13.pre is 2011/03/13 03:15:00 -0700
// // mar13.trans is 2011/03/13 03:00:00 -0700
// // mar13.post is 2011/03/13 01:15:00 -0800
//
// // A Fall DST transition, when civil times are repeated.
// auto nov06 = lax.lookup(cctz::civil_second(2011, 11, 6, 1, 15, 0));
// // nov06.kind == cctz::time_zone::civil_lookup::REPEATED
// // nov06.pre is 2011/11/06 01:15:00 -0700
// // nov06.trans is 2011/11/06 01:00:00 -0800
// // nov06.post is 2011/11/06 01:15:00 -0800
struct civil_lookup {
enum civil_kind {
UNIQUE, // the civil time was singular (pre == trans == post)
SKIPPED, // the civil time did not exist (pre >= trans > post)
REPEATED, // the civil time was ambiguous (pre < trans <= post)
} kind;
time_point<sys_seconds> pre; // uses the pre-transition offset
time_point<sys_seconds> trans; // instant of civil-offset change
time_point<sys_seconds> post; // uses the post-transition offset
};
civil_lookup lookup(const civil_second& cs) const;
class Impl;
private:
explicit time_zone(const Impl* impl) : impl_(impl) {}
const Impl* impl_;
};
// Relational operators.
bool operator==(time_zone lhs, time_zone rhs);
inline bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); }
// Loads the named time zone. May perform I/O on the initial load.
// If the name is invalid, or some other kind of error occurs, returns
// false and "*tz" is set to the UTC time zone.
bool load_time_zone(const std::string& name, time_zone* tz);
// Returns a time_zone representing UTC. Cannot fail.
time_zone utc_time_zone();
// Returns a time zone that is a fixed offset (seconds east) from UTC.
// Note: If the absolute value of the offset is greater than 24 hours
// you'll get UTC (i.e., zero offset) instead.
time_zone fixed_time_zone(const sys_seconds& offset);
// Returns a time zone representing the local time zone. Falls back to UTC.
time_zone local_time_zone();
// Returns the civil time (cctz::civil_second) within the given time zone at
// the given absolute time (time_point). Since the additional fields provided
// by the time_zone::absolute_lookup struct should rarely be needed in modern
// code, this convert() function is simpler and should be preferred.
template <typename D>
inline civil_second convert(const time_point<D>& tp, const time_zone& tz) {
return tz.lookup(tp).cs;
}
// Returns the absolute time (time_point) that corresponds to the given civil
// time within the given time zone. If the civil time is not unique (i.e., if
// it was either repeated or non-existent), then the returned time_point is
// the best estimate that preserves relative order. That is, this function
// guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz).
inline time_point<sys_seconds> convert(const civil_second& cs,
const time_zone& tz) {
const time_zone::civil_lookup cl = tz.lookup(cs);
if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans;
return cl.pre;
}
namespace detail {
using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>;
std::string format(const std::string&, const time_point<sys_seconds>&,
const femtoseconds&, const time_zone&);
bool parse(const std::string&, const std::string&, const time_zone&,
time_point<sys_seconds>*, femtoseconds*, std::string* err = nullptr);
} // namespace detail
// Formats the given time_point in the given cctz::time_zone according to
// the provided format std::string. Uses strftime()-like formatting options,
// with the following extensions:
//
// - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
// - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
// - %E#S - Seconds with # digits of fractional precision
// - %E*S - Seconds with full fractional precision (a literal '*')
// - %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)
//
// 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'.
//
// Note that %Y produces as many characters as it takes to fully render the
// year. A year outside of [-999:9999] when formatted with %E4Y will produce
// more than four characters, just like %Y.
//
// Tip: Format strings should include the UTC offset (e.g., %z, %Ez, or %E*z)
// so that the resulting std::string uniquely identifies an absolute time.
//
// Example:
// cctz::time_zone lax;
// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
// auto tp = cctz::convert(cctz::civil_second(2013, 1, 2, 3, 4, 5), lax);
// std::string f = cctz::format("%H:%M:%S", tp, lax); // "03:04:05"
// f = cctz::format("%H:%M:%E3S", tp, lax); // "03:04:05.000"
template <typename D>
inline std::string format(const std::string& fmt, const time_point<D>& tp,
const time_zone& tz) {
const auto p = detail::split_seconds(tp);
const auto n = std::chrono::duration_cast<detail::femtoseconds>(p.second);
return detail::format(fmt, p.first, n, tz);
}
// Parses an input std::string according to the provided format std::string and
// 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.
//
// %Y consumes as many numeric characters as it can, so the matching data
// should always be terminated with a non-numeric. %E4Y always consumes
// exactly four characters, including any sign.
//
// Unspecified fields are taken from the default date and time of ...
//
// "1970-01-01 00:00:00.0 +0000"
//
// For example, parsing a std::string of "15:45" (%H:%M) will return a time_point
// that represents "1970-01-01 15:45:00.0 +0000".
//
// Note that parse() returns time instants, so it makes most sense to parse
// fully-specified date/time strings that include a UTC offset (%z, %Ez, or
// %E*z).
//
// Note also that parse() only heeds the fields year, month, day, hour,
// minute, (fractional) second, and UTC offset. Other fields, like weekday (%a
// or %A), while parsed for syntactic validity, are ignored in the conversion.
//
// Date and time fields that are out-of-range will be treated as errors rather
// than normalizing them like cctz::civil_second() would do. For example, it
// is an error to parse the date "Oct 32, 2013" because 32 is out of range.
//
// A second of ":60" is normalized to ":00" of the following minute with
// fractional seconds discarded. The following table shows how the given
// seconds and subseconds will be parsed:
//
// "59.x" -> 59.x // exact
// "60.x" -> 00.0 // normalized
// "00.x" -> 00.x // exact
//
// Errors are indicated by returning false.
//
// Example:
// const cctz::time_zone tz = ...
// std::chrono::system_clock::time_point tp;
// if (cctz::parse("%Y-%m-%d", "2015-10-09", tz, &tp)) {
// ...
// }
template <typename D>
inline bool parse(const std::string& fmt, const std::string& input,
const time_zone& tz, time_point<D>* tpp) {
time_point<sys_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 b;
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
#define ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
#include <cstddef>
#include <functional>
#include <memory>
#include <string>
namespace absl {
namespace time_internal {
namespace cctz {
// A stdio-like interface for providing zoneinfo data for a particular zone.
class ZoneInfoSource {
public:
virtual ~ZoneInfoSource();
virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread()
virtual int Skip(std::size_t offset) = 0; // like fseek()
};
} // namespace cctz
} // namespace time_internal
} // namespace absl
namespace absl {
namespace time_internal {
namespace cctz_extension {
// A function-pointer type for a factory that returns a ZoneInfoSource
// given the name of a time zone and a fallback factory. Returns null
// when the data for the named zone cannot be found.
using ZoneInfoSourceFactory =
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> (*)(
const std::string&,
const std::function<std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>(
const std::string&)>&);
// The user can control the mapping of zone names to zoneinfo data by
// providing a definition for cctz_extension::zone_info_source_factory.
// For example, given functions my_factory() and my_other_factory() that
// can return a ZoneInfoSource for a named zone, we could inject them into
// cctz::load_time_zone() with:
//
// namespace cctz_extension {
// namespace {
// std::unique_ptr<cctz::ZoneInfoSource> CustomFactory(
// const std::string& name,
// const std::function<std::unique_ptr<cctz::ZoneInfoSource>(
// const std::string& name)>& fallback_factory) {
// if (auto zip = my_factory(name)) return zip;
// if (auto zip = fallback_factory(name)) return zip;
// if (auto zip = my_other_factory(name)) return zip;
// return nullptr;
// }
// } // namespace
// ZoneInfoSourceFactory zone_info_source_factory = CustomFactory;
// } // namespace cctz_extension
//
// This might be used, say, to use zoneinfo data embedded in the program,
// or read from a (possibly compressed) file archive, or both.
//
// cctz_extension::zone_info_source_factory() will be called:
// (1) from the same thread as the cctz::load_time_zone() call,
// (2) only once for any zone name, and
// (3) serially (i.e., no concurrent execution).
//
// The fallback factory obtains zoneinfo data by reading files in ${TZDIR},
// and it is used automatically when no zone_info_source_factory definition
// is linked into the program.
extern ZoneInfoSourceFactory zone_info_source_factory;
} // namespace cctz_extension
} // namespace time_internal
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h"
#include <iomanip>
#include <ostream>
#include <sstream>
namespace absl {
namespace time_internal {
namespace cctz {
namespace detail {
// Output stream operators output a format matching YYYY-MM-DDThh:mm:ss,
// while omitting fields inferior to the type's alignment. For example,
// civil_day is formatted only as YYYY-MM-DD.
std::ostream& operator<<(std::ostream& os, const civil_year& y) {
std::stringstream ss;
ss << y.year(); // No padding.
return os << ss.str();
}
std::ostream& operator<<(std::ostream& os, const civil_month& m) {
std::stringstream ss;
ss << civil_year(m) << '-';
ss << std::setfill('0') << std::setw(2) << m.month();
return os << ss.str();
}
std::ostream& operator<<(std::ostream& os, const civil_day& d) {
std::stringstream ss;
ss << civil_month(d) << '-';
ss << std::setfill('0') << std::setw(2) << d.day();
return os << ss.str();
}
std::ostream& operator<<(std::ostream& os, const civil_hour& h) {
std::stringstream ss;
ss << civil_day(h) << 'T';
ss << std::setfill('0') << std::setw(2) << h.hour();
return os << ss.str();
}
std::ostream& operator<<(std::ostream& os, const civil_minute& m) {
std::stringstream ss;
ss << civil_hour(m) << ':';
ss << std::setfill('0') << std::setw(2) << m.minute();
return os << ss.str();
}
std::ostream& operator<<(std::ostream& os, const civil_second& s) {
std::stringstream ss;
ss << civil_minute(s) << ':';
ss << std::setfill('0') << std::setw(2) << s.second();
return os << ss.str();
}
////////////////////////////////////////////////////////////////////////
std::ostream& operator<<(std::ostream& os, weekday wd) {
switch (wd) {
case weekday::monday:
return os << "Monday";
case weekday::tuesday:
return os << "Tuesday";
case weekday::wednesday:
return os << "Wednesday";
case weekday::thursday:
return os << "Thursday";
case weekday::friday:
return os << "Friday";
case weekday::saturday:
return os << "Saturday";
case weekday::sunday:
return os << "Sunday";
}
return os; // Should never get here, but -Wreturn-type may warn without this.
}
} // namespace detail
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include <iomanip>
#include <limits>
#include <sstream>
#include <string>
#include <type_traits>
#include "gtest/gtest.h"
namespace absl {
namespace time_internal {
namespace cctz {
namespace {
template <typename T>
std::string Format(const T& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
} // namespace
#if __clang__ && __cpp_constexpr >= 201304
// Construction constexpr tests
TEST(CivilTime, Normal) {
constexpr civil_second css(2016, 1, 28, 17, 14, 12);
static_assert(css.second() == 12, "Normal.second");
constexpr civil_minute cmm(2016, 1, 28, 17, 14);
static_assert(cmm.minute() == 14, "Normal.minute");
constexpr civil_hour chh(2016, 1, 28, 17);
static_assert(chh.hour() == 17, "Normal.hour");
constexpr civil_day cd(2016, 1, 28);
static_assert(cd.day() == 28, "Normal.day");
constexpr civil_month cm(2016, 1);
static_assert(cm.month() == 1, "Normal.month");
constexpr civil_year cy(2016);
static_assert(cy.year() == 2016, "Normal.year");
}
TEST(CivilTime, Conversion) {
constexpr civil_year cy(2016);
static_assert(cy.year() == 2016, "Conversion.year");
constexpr civil_month cm(cy);
static_assert(cm.month() == 1, "Conversion.month");
constexpr civil_day cd(cm);
static_assert(cd.day() == 1, "Conversion.day");
constexpr civil_hour chh(cd);
static_assert(chh.hour() == 0, "Conversion.hour");
constexpr civil_minute cmm(chh);
static_assert(cmm.minute() == 0, "Conversion.minute");
constexpr civil_second css(cmm);
static_assert(css.second() == 0, "Conversion.second");
}
// Normalization constexpr tests
TEST(CivilTime, Normalized) {
constexpr civil_second cs(2016, 1, 28, 17, 14, 12);
static_assert(cs.year() == 2016, "Normalized.year");
static_assert(cs.month() == 1, "Normalized.month");
static_assert(cs.day() == 28, "Normalized.day");
static_assert(cs.hour() == 17, "Normalized.hour");
static_assert(cs.minute() == 14, "Normalized.minute");
static_assert(cs.second() == 12, "Normalized.second");
}
TEST(CivilTime, SecondOverflow) {
constexpr civil_second cs(2016, 1, 28, 17, 14, 121);
static_assert(cs.year() == 2016, "SecondOverflow.year");
static_assert(cs.month() == 1, "SecondOverflow.month");
static_assert(cs.day() == 28, "SecondOverflow.day");
static_assert(cs.hour() == 17, "SecondOverflow.hour");
static_assert(cs.minute() == 16, "SecondOverflow.minute");
static_assert(cs.second() == 1, "SecondOverflow.second");
}
TEST(CivilTime, SecondUnderflow) {
constexpr civil_second cs(2016, 1, 28, 17, 14, -121);
static_assert(cs.year() == 2016, "SecondUnderflow.year");
static_assert(cs.month() == 1, "SecondUnderflow.month");
static_assert(cs.day() == 28, "SecondUnderflow.day");
static_assert(cs.hour() == 17, "SecondUnderflow.hour");
static_assert(cs.minute() == 11, "SecondUnderflow.minute");
static_assert(cs.second() == 59, "SecondUnderflow.second");
}
TEST(CivilTime, MinuteOverflow) {
constexpr civil_second cs(2016, 1, 28, 17, 121, 12);
static_assert(cs.year() == 2016, "MinuteOverflow.year");
static_assert(cs.month() == 1, "MinuteOverflow.month");
static_assert(cs.day() == 28, "MinuteOverflow.day");
static_assert(cs.hour() == 19, "MinuteOverflow.hour");
static_assert(cs.minute() == 1, "MinuteOverflow.minute");
static_assert(cs.second() == 12, "MinuteOverflow.second");
}
TEST(CivilTime, MinuteUnderflow) {
constexpr civil_second cs(2016, 1, 28, 17, -121, 12);
static_assert(cs.year() == 2016, "MinuteUnderflow.year");
static_assert(cs.month() == 1, "MinuteUnderflow.month");
static_assert(cs.day() == 28, "MinuteUnderflow.day");
static_assert(cs.hour() == 14, "MinuteUnderflow.hour");
static_assert(cs.minute() == 59, "MinuteUnderflow.minute");
static_assert(cs.second() == 12, "MinuteUnderflow.second");
}
TEST(CivilTime, HourOverflow) {
constexpr civil_second cs(2016, 1, 28, 49, 14, 12);
static_assert(cs.year() == 2016, "HourOverflow.year");
static_assert(cs.month() == 1, "HourOverflow.month");
static_assert(cs.day() == 30, "HourOverflow.day");
static_assert(cs.hour() == 1, "HourOverflow.hour");
static_assert(cs.minute() == 14, "HourOverflow.minute");
static_assert(cs.second() == 12, "HourOverflow.second");
}
TEST(CivilTime, HourUnderflow) {
constexpr civil_second cs(2016, 1, 28, -49, 14, 12);
static_assert(cs.year() == 2016, "HourUnderflow.year");
static_assert(cs.month() == 1, "HourUnderflow.month");
static_assert(cs.day() == 25, "HourUnderflow.day");
static_assert(cs.hour() == 23, "HourUnderflow.hour");
static_assert(cs.minute() == 14, "HourUnderflow.minute");
static_assert(cs.second() == 12, "HourUnderflow.second");
}
TEST(CivilTime, MonthOverflow) {
constexpr civil_second cs(2016, 25, 28, 17, 14, 12);
static_assert(cs.year() == 2018, "MonthOverflow.year");
static_assert(cs.month() == 1, "MonthOverflow.month");
static_assert(cs.day() == 28, "MonthOverflow.day");
static_assert(cs.hour() == 17, "MonthOverflow.hour");
static_assert(cs.minute() == 14, "MonthOverflow.minute");
static_assert(cs.second() == 12, "MonthOverflow.second");
}
TEST(CivilTime, MonthUnderflow) {
constexpr civil_second cs(2016, -25, 28, 17, 14, 12);
static_assert(cs.year() == 2013, "MonthUnderflow.year");
static_assert(cs.month() == 11, "MonthUnderflow.month");
static_assert(cs.day() == 28, "MonthUnderflow.day");
static_assert(cs.hour() == 17, "MonthUnderflow.hour");
static_assert(cs.minute() == 14, "MonthUnderflow.minute");
static_assert(cs.second() == 12, "MonthUnderflow.second");
}
TEST(CivilTime, C4Overflow) {
constexpr civil_second cs(2016, 1, 292195, 17, 14, 12);
static_assert(cs.year() == 2816, "C4Overflow.year");
static_assert(cs.month() == 1, "C4Overflow.month");
static_assert(cs.day() == 1, "C4Overflow.day");
static_assert(cs.hour() == 17, "C4Overflow.hour");
static_assert(cs.minute() == 14, "C4Overflow.minute");
static_assert(cs.second() == 12, "C4Overflow.second");
}
TEST(CivilTime, C4Underflow) {
constexpr civil_second cs(2016, 1, -292195, 17, 14, 12);
static_assert(cs.year() == 1215, "C4Underflow.year");
static_assert(cs.month() == 12, "C4Underflow.month");
static_assert(cs.day() == 30, "C4Underflow.day");
static_assert(cs.hour() == 17, "C4Underflow.hour");
static_assert(cs.minute() == 14, "C4Underflow.minute");
static_assert(cs.second() == 12, "C4Underflow.second");
}
TEST(CivilTime, MixedNormalization) {
constexpr civil_second cs(2016, -42, 122, 99, -147, 4949);
static_assert(cs.year() == 2012, "MixedNormalization.year");
static_assert(cs.month() == 10, "MixedNormalization.month");
static_assert(cs.day() == 4, "MixedNormalization.day");
static_assert(cs.hour() == 1, "MixedNormalization.hour");
static_assert(cs.minute() == 55, "MixedNormalization.minute");
static_assert(cs.second() == 29, "MixedNormalization.second");
}
// Relational constexpr tests
TEST(CivilTime, Less) {
constexpr civil_second cs1(2016, 1, 28, 17, 14, 12);
constexpr civil_second cs2(2016, 1, 28, 17, 14, 13);
constexpr bool less = cs1 < cs2;
static_assert(less, "Less");
}
// Arithmetic constexpr tests
TEST(CivilTime, Addition) {
constexpr civil_second cs1(2016, 1, 28, 17, 14, 12);
constexpr civil_second cs2 = cs1 + 50;
static_assert(cs2.year() == 2016, "Addition.year");
static_assert(cs2.month() == 1, "Addition.month");
static_assert(cs2.day() == 28, "Addition.day");
static_assert(cs2.hour() == 17, "Addition.hour");
static_assert(cs2.minute() == 15, "Addition.minute");
static_assert(cs2.second() == 2, "Addition.second");
}
TEST(CivilTime, Subtraction) {
constexpr civil_second cs1(2016, 1, 28, 17, 14, 12);
constexpr civil_second cs2 = cs1 - 50;
static_assert(cs2.year() == 2016, "Subtraction.year");
static_assert(cs2.month() == 1, "Subtraction.month");
static_assert(cs2.day() == 28, "Subtraction.day");
static_assert(cs2.hour() == 17, "Subtraction.hour");
static_assert(cs2.minute() == 13, "Subtraction.minute");
static_assert(cs2.second() == 22, "Subtraction.second");
}
TEST(CivilTime, Difference) {
constexpr civil_day cd1(2016, 1, 28);
constexpr civil_day cd2(2015, 1, 28);
constexpr int diff = cd1 - cd2;
static_assert(diff == 365, "Difference");
}
// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
TEST(CivilTime, DifferenceWithHugeYear) {
{
constexpr civil_day d1(9223372036854775807, 1, 1);
constexpr civil_day d2(9223372036854775807, 12, 31);
static_assert(d2 - d1 == 364, "DifferenceWithHugeYear");
}
{
constexpr civil_day d1(-9223372036854775807 - 1, 1, 1);
constexpr civil_day d2(-9223372036854775807 - 1, 12, 31);
static_assert(d2 - d1 == 365, "DifferenceWithHugeYear");
}
{
// Check the limits of the return value at the end of the year range.
constexpr civil_day d1(9223372036854775807, 1, 1);
constexpr civil_day d2(9198119301927009252, 6, 6);
static_assert(d1 - d2 == 9223372036854775807, "DifferenceWithHugeYear");
static_assert((d2 - 1) - d1 == -9223372036854775807 - 1,
"DifferenceWithHugeYear");
}
{
// Check the limits of the return value at the start of the year range.
constexpr civil_day d1(-9223372036854775807 - 1, 1, 1);
constexpr civil_day d2(-9198119301927009254, 7, 28);
static_assert(d2 - d1 == 9223372036854775807, "DifferenceWithHugeYear");
static_assert(d1 - (d2 + 1) == -9223372036854775807 - 1,
"DifferenceWithHugeYear");
}
{
// Check the limits of the return value from either side of year 0.
constexpr civil_day d1(-12626367463883278, 9, 3);
constexpr civil_day d2(12626367463883277, 3, 28);
static_assert(d2 - d1 == 9223372036854775807, "DifferenceWithHugeYear");
static_assert(d1 - (d2 + 1) == -9223372036854775807 - 1,
"DifferenceWithHugeYear");
}
}
// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
TEST(CivilTime, DifferenceNoIntermediateOverflow) {
{
// The difference up to the minute field would be below the minimum
// diff_t, but the 52 extra seconds brings us back to the minimum.
constexpr civil_second s1(-292277022657, 1, 27, 8, 29 - 1, 52);
constexpr civil_second s2(1970, 1, 1, 0, 0 - 1, 0);
static_assert(s1 - s2 == -9223372036854775807 - 1,
"DifferenceNoIntermediateOverflow");
}
{
// The difference up to the minute field would be above the maximum
// diff_t, but the -53 extra seconds brings us back to the maximum.
constexpr civil_second s1(292277026596, 12, 4, 15, 30, 7 - 7);
constexpr civil_second s2(1970, 1, 1, 0, 0, 0 - 7);
static_assert(s1 - s2 == 9223372036854775807,
"DifferenceNoIntermediateOverflow");
}
}
// Helper constexpr tests
TEST(CivilTime, WeekDay) {
constexpr civil_day cd(2016, 1, 28);
constexpr weekday wd = get_weekday(cd);
static_assert(wd == weekday::thursday, "Weekday");
}
TEST(CivilTime, NextWeekDay) {
constexpr civil_day cd(2016, 1, 28);
constexpr civil_day next = next_weekday(cd, weekday::thursday);
static_assert(next.year() == 2016, "NextWeekDay.year");
static_assert(next.month() == 2, "NextWeekDay.month");
static_assert(next.day() == 4, "NextWeekDay.day");
}
TEST(CivilTime, PrevWeekDay) {
constexpr civil_day cd(2016, 1, 28);
constexpr civil_day prev = prev_weekday(cd, weekday::thursday);
static_assert(prev.year() == 2016, "PrevWeekDay.year");
static_assert(prev.month() == 1, "PrevWeekDay.month");
static_assert(prev.day() == 21, "PrevWeekDay.day");
}
TEST(CivilTime, YearDay) {
constexpr civil_day cd(2016, 1, 28);
constexpr int yd = get_yearday(cd);
static_assert(yd == 28, "YearDay");
}
#endif // __clang__ && __cpp_constexpr >= 201304
// The remaining tests do not use constexpr.
TEST(CivilTime, DefaultConstruction) {
civil_second ss;
EXPECT_EQ("1970-01-01T00:00:00", Format(ss));
civil_minute mm;
EXPECT_EQ("1970-01-01T00:00", Format(mm));
civil_hour hh;
EXPECT_EQ("1970-01-01T00", Format(hh));
civil_day d;
EXPECT_EQ("1970-01-01", Format(d));
civil_month m;
EXPECT_EQ("1970-01", Format(m));
civil_year y;
EXPECT_EQ("1970", Format(y));
}
TEST(CivilTime, StructMember) {
struct S {
civil_day day;
};
S s = {};
EXPECT_EQ(civil_day{}, s.day);
}
TEST(CivilTime, FieldsConstruction) {
EXPECT_EQ("2015-01-02T03:04:05", Format(civil_second(2015, 1, 2, 3, 4, 5)));
EXPECT_EQ("2015-01-02T03:04:00", Format(civil_second(2015, 1, 2, 3, 4)));
EXPECT_EQ("2015-01-02T03:00:00", Format(civil_second(2015, 1, 2, 3)));
EXPECT_EQ("2015-01-02T00:00:00", Format(civil_second(2015, 1, 2)));
EXPECT_EQ("2015-01-01T00:00:00", Format(civil_second(2015, 1)));
EXPECT_EQ("2015-01-01T00:00:00", Format(civil_second(2015)));
EXPECT_EQ("2015-01-02T03:04", Format(civil_minute(2015, 1, 2, 3, 4, 5)));
EXPECT_EQ("2015-01-02T03:04", Format(civil_minute(2015, 1, 2, 3, 4)));
EXPECT_EQ("2015-01-02T03:00", Format(civil_minute(2015, 1, 2, 3)));
EXPECT_EQ("2015-01-02T00:00", Format(civil_minute(2015, 1, 2)));
EXPECT_EQ("2015-01-01T00:00", Format(civil_minute(2015, 1)));
EXPECT_EQ("2015-01-01T00:00", Format(civil_minute(2015)));
EXPECT_EQ("2015-01-02T03", Format(civil_hour(2015, 1, 2, 3, 4, 5)));
EXPECT_EQ("2015-01-02T03", Format(civil_hour(2015, 1, 2, 3, 4)));
EXPECT_EQ("2015-01-02T03", Format(civil_hour(2015, 1, 2, 3)));
EXPECT_EQ("2015-01-02T00", Format(civil_hour(2015, 1, 2)));
EXPECT_EQ("2015-01-01T00", Format(civil_hour(2015, 1)));
EXPECT_EQ("2015-01-01T00", Format(civil_hour(2015)));
EXPECT_EQ("2015-01-02", Format(civil_day(2015, 1, 2, 3, 4, 5)));
EXPECT_EQ("2015-01-02", Format(civil_day(2015, 1, 2, 3, 4)));
EXPECT_EQ("2015-01-02", Format(civil_day(2015, 1, 2, 3)));
EXPECT_EQ("2015-01-02", Format(civil_day(2015, 1, 2)));
EXPECT_EQ("2015-01-01", Format(civil_day(2015, 1)));
EXPECT_EQ("2015-01-01", Format(civil_day(2015)));
EXPECT_EQ("2015-01", Format(civil_month(2015, 1, 2, 3, 4, 5)));
EXPECT_EQ("2015-01", Format(civil_month(2015, 1, 2, 3, 4)));
EXPECT_EQ("2015-01", Format(civil_month(2015, 1, 2, 3)));
EXPECT_EQ("2015-01", Format(civil_month(2015, 1, 2)));
EXPECT_EQ("2015-01", Format(civil_month(2015, 1)));
EXPECT_EQ("2015-01", Format(civil_month(2015)));
EXPECT_EQ("2015", Format(civil_year(2015, 1, 2, 3, 4, 5)));
EXPECT_EQ("2015", Format(civil_year(2015, 1, 2, 3, 4)));
EXPECT_EQ("2015", Format(civil_year(2015, 1, 2, 3)));
EXPECT_EQ("2015", Format(civil_year(2015, 1, 2)));
EXPECT_EQ("2015", Format(civil_year(2015, 1)));
EXPECT_EQ("2015", Format(civil_year(2015)));
}
TEST(CivilTime, FieldsConstructionLimits) {
const int kIntMax = std::numeric_limits<int>::max();
EXPECT_EQ("2038-01-19T03:14:07",
Format(civil_second(1970, 1, 1, 0, 0, kIntMax)));
EXPECT_EQ("6121-02-11T05:21:07",
Format(civil_second(1970, 1, 1, 0, kIntMax, kIntMax)));
EXPECT_EQ("251104-11-20T12:21:07",
Format(civil_second(1970, 1, 1, kIntMax, kIntMax, kIntMax)));
EXPECT_EQ("6130715-05-30T12:21:07",
Format(civil_second(1970, 1, kIntMax, kIntMax, kIntMax, kIntMax)));
EXPECT_EQ(
"185087685-11-26T12:21:07",
Format(civil_second(1970, kIntMax, kIntMax, kIntMax, kIntMax, kIntMax)));
const int kIntMin = std::numeric_limits<int>::min();
EXPECT_EQ("1901-12-13T20:45:52",
Format(civil_second(1970, 1, 1, 0, 0, kIntMin)));
EXPECT_EQ("-2182-11-20T18:37:52",
Format(civil_second(1970, 1, 1, 0, kIntMin, kIntMin)));
EXPECT_EQ("-247165-02-11T10:37:52",
Format(civil_second(1970, 1, 1, kIntMin, kIntMin, kIntMin)));
EXPECT_EQ("-6126776-08-01T10:37:52",
Format(civil_second(1970, 1, kIntMin, kIntMin, kIntMin, kIntMin)));
EXPECT_EQ(
"-185083747-10-31T10:37:52",
Format(civil_second(1970, kIntMin, kIntMin, kIntMin, kIntMin, kIntMin)));
}
TEST(CivilTime, ImplicitCrossAlignment) {
civil_year year(2015);
civil_month month = year;
civil_day day = month;
civil_hour hour = day;
civil_minute minute = hour;
civil_second second = minute;
second = year;
EXPECT_EQ(second, year);
second = month;
EXPECT_EQ(second, month);
second = day;
EXPECT_EQ(second, day);
second = hour;
EXPECT_EQ(second, hour);
second = minute;
EXPECT_EQ(second, minute);
minute = year;
EXPECT_EQ(minute, year);
minute = month;
EXPECT_EQ(minute, month);
minute = day;
EXPECT_EQ(minute, day);
minute = hour;
EXPECT_EQ(minute, hour);
hour = year;
EXPECT_EQ(hour, year);
hour = month;
EXPECT_EQ(hour, month);
hour = day;
EXPECT_EQ(hour, day);
day = year;
EXPECT_EQ(day, year);
day = month;
EXPECT_EQ(day, month);
month = year;
EXPECT_EQ(month, year);
// Ensures unsafe conversions are not allowed.
EXPECT_FALSE((std::is_convertible<civil_second, civil_minute>::value));
EXPECT_FALSE((std::is_convertible<civil_second, civil_hour>::value));
EXPECT_FALSE((std::is_convertible<civil_second, civil_day>::value));
EXPECT_FALSE((std::is_convertible<civil_second, civil_month>::value));
EXPECT_FALSE((std::is_convertible<civil_second, civil_year>::value));
EXPECT_FALSE((std::is_convertible<civil_minute, civil_hour>::value));
EXPECT_FALSE((std::is_convertible<civil_minute, civil_day>::value));
EXPECT_FALSE((std::is_convertible<civil_minute, civil_month>::value));
EXPECT_FALSE((std::is_convertible<civil_minute, civil_year>::value));
EXPECT_FALSE((std::is_convertible<civil_hour, civil_day>::value));
EXPECT_FALSE((std::is_convertible<civil_hour, civil_month>::value));
EXPECT_FALSE((std::is_convertible<civil_hour, civil_year>::value));
EXPECT_FALSE((std::is_convertible<civil_day, civil_month>::value));
EXPECT_FALSE((std::is_convertible<civil_day, civil_year>::value));
EXPECT_FALSE((std::is_convertible<civil_month, civil_year>::value));
}
TEST(CivilTime, ExplicitCrossAlignment) {
//
// Assign from smaller units -> larger units
//
civil_second second(2015, 1, 2, 3, 4, 5);
EXPECT_EQ("2015-01-02T03:04:05", Format(second));
civil_minute minute(second);
EXPECT_EQ("2015-01-02T03:04", Format(minute));
civil_hour hour(minute);
EXPECT_EQ("2015-01-02T03", Format(hour));
civil_day day(hour);
EXPECT_EQ("2015-01-02", Format(day));
civil_month month(day);
EXPECT_EQ("2015-01", Format(month));
civil_year year(month);
EXPECT_EQ("2015", Format(year));
//
// Now assign from larger units -> smaller units
//
month = civil_month(year);
EXPECT_EQ("2015-01", Format(month));
day = civil_day(month);
EXPECT_EQ("2015-01-01", Format(day));
hour = civil_hour(day);
EXPECT_EQ("2015-01-01T00", Format(hour));
minute = civil_minute(hour);
EXPECT_EQ("2015-01-01T00:00", Format(minute));
second = civil_second(minute);
EXPECT_EQ("2015-01-01T00:00:00", Format(second));
}
// Metafunction to test whether difference is allowed between two types.
template <typename T1, typename T2>
struct HasDifference {
template <typename U1, typename U2>
static std::false_type test(...);
template <typename U1, typename U2>
static std::true_type test(decltype(std::declval<U1>() - std::declval<U2>()));
static constexpr bool value = decltype(test<T1, T2>(0))::value;
};
TEST(CivilTime, DisallowCrossAlignedDifference) {
// Difference is allowed between types with the same alignment.
static_assert(HasDifference<civil_second, civil_second>::value, "");
static_assert(HasDifference<civil_minute, civil_minute>::value, "");
static_assert(HasDifference<civil_hour, civil_hour>::value, "");
static_assert(HasDifference<civil_day, civil_day>::value, "");
static_assert(HasDifference<civil_month, civil_month>::value, "");
static_assert(HasDifference<civil_year, civil_year>::value, "");
// Difference is disallowed between types with different alignments.
static_assert(!HasDifference<civil_second, civil_minute>::value, "");
static_assert(!HasDifference<civil_second, civil_hour>::value, "");
static_assert(!HasDifference<civil_second, civil_day>::value, "");
static_assert(!HasDifference<civil_second, civil_month>::value, "");
static_assert(!HasDifference<civil_second, civil_year>::value, "");
static_assert(!HasDifference<civil_minute, civil_hour>::value, "");
static_assert(!HasDifference<civil_minute, civil_day>::value, "");
static_assert(!HasDifference<civil_minute, civil_month>::value, "");
static_assert(!HasDifference<civil_minute, civil_year>::value, "");
static_assert(!HasDifference<civil_hour, civil_day>::value, "");
static_assert(!HasDifference<civil_hour, civil_month>::value, "");
static_assert(!HasDifference<civil_hour, civil_year>::value, "");
static_assert(!HasDifference<civil_day, civil_month>::value, "");
static_assert(!HasDifference<civil_day, civil_year>::value, "");
static_assert(!HasDifference<civil_month, civil_year>::value, "");
}
TEST(CivilTime, ValueSemantics) {
const civil_hour a(2015, 1, 2, 3);
const civil_hour b = a;
const civil_hour c(b);
civil_hour d;
d = c;
EXPECT_EQ("2015-01-02T03", Format(d));
}
TEST(CivilTime, Relational) {
// Tests that the alignment unit is ignored in comparison.
const civil_year year(2014);
const civil_month month(year);
EXPECT_EQ(year, month);
#define TEST_RELATIONAL(OLDER, YOUNGER) \
do { \
EXPECT_FALSE(OLDER < OLDER); \
EXPECT_FALSE(OLDER > OLDER); \
EXPECT_TRUE(OLDER >= OLDER); \
EXPECT_TRUE(OLDER <= OLDER); \
EXPECT_FALSE(YOUNGER < YOUNGER); \
EXPECT_FALSE(YOUNGER > YOUNGER); \
EXPECT_TRUE(YOUNGER >= YOUNGER); \
EXPECT_TRUE(YOUNGER <= YOUNGER); \
EXPECT_EQ(OLDER, OLDER); \
EXPECT_NE(OLDER, YOUNGER); \
EXPECT_LT(OLDER, YOUNGER); \
EXPECT_LE(OLDER, YOUNGER); \
EXPECT_GT(YOUNGER, OLDER); \
EXPECT_GE(YOUNGER, OLDER); \
} while (0)
// Alignment is ignored in comparison (verified above), so kSecond is used
// to test comparison in all field positions.
TEST_RELATIONAL(civil_second(2014, 1, 1, 0, 0, 0),
civil_second(2015, 1, 1, 0, 0, 0));
TEST_RELATIONAL(civil_second(2014, 1, 1, 0, 0, 0),
civil_second(2014, 2, 1, 0, 0, 0));
TEST_RELATIONAL(civil_second(2014, 1, 1, 0, 0, 0),
civil_second(2014, 1, 2, 0, 0, 0));
TEST_RELATIONAL(civil_second(2014, 1, 1, 0, 0, 0),
civil_second(2014, 1, 1, 1, 0, 0));
TEST_RELATIONAL(civil_second(2014, 1, 1, 1, 0, 0),
civil_second(2014, 1, 1, 1, 1, 0));
TEST_RELATIONAL(civil_second(2014, 1, 1, 1, 1, 0),
civil_second(2014, 1, 1, 1, 1, 1));
// Tests the relational operators of two different CivilTime types.
TEST_RELATIONAL(civil_day(2014, 1, 1), civil_minute(2014, 1, 1, 1, 1));
TEST_RELATIONAL(civil_day(2014, 1, 1), civil_month(2014, 2));
#undef TEST_RELATIONAL
}
TEST(CivilTime, Arithmetic) {
civil_second second(2015, 1, 2, 3, 4, 5);
EXPECT_EQ("2015-01-02T03:04:06", Format(second += 1));
EXPECT_EQ("2015-01-02T03:04:07", Format(second + 1));
EXPECT_EQ("2015-01-02T03:04:08", Format(2 + second));
EXPECT_EQ("2015-01-02T03:04:05", Format(second - 1));
EXPECT_EQ("2015-01-02T03:04:05", Format(second -= 1));
EXPECT_EQ("2015-01-02T03:04:05", Format(second++));
EXPECT_EQ("2015-01-02T03:04:07", Format(++second));
EXPECT_EQ("2015-01-02T03:04:07", Format(second--));
EXPECT_EQ("2015-01-02T03:04:05", Format(--second));
civil_minute minute(2015, 1, 2, 3, 4);
EXPECT_EQ("2015-01-02T03:05", Format(minute += 1));
EXPECT_EQ("2015-01-02T03:06", Format(minute + 1));
EXPECT_EQ("2015-01-02T03:07", Format(2 + minute));
EXPECT_EQ("2015-01-02T03:04", Format(minute - 1));
EXPECT_EQ("2015-01-02T03:04", Format(minute -= 1));
EXPECT_EQ("2015-01-02T03:04", Format(minute++));
EXPECT_EQ("2015-01-02T03:06", Format(++minute));
EXPECT_EQ("2015-01-02T03:06", Format(minute--));
EXPECT_EQ("2015-01-02T03:04", Format(--minute));
civil_hour hour(2015, 1, 2, 3);
EXPECT_EQ("2015-01-02T04", Format(hour += 1));
EXPECT_EQ("2015-01-02T05", Format(hour + 1));
EXPECT_EQ("2015-01-02T06", Format(2 + hour));
EXPECT_EQ("2015-01-02T03", Format(hour - 1));
EXPECT_EQ("2015-01-02T03", Format(hour -= 1));
EXPECT_EQ("2015-01-02T03", Format(hour++));
EXPECT_EQ("2015-01-02T05", Format(++hour));
EXPECT_EQ("2015-01-02T05", Format(hour--));
EXPECT_EQ("2015-01-02T03", Format(--hour));
civil_day day(2015, 1, 2);
EXPECT_EQ("2015-01-03", Format(day += 1));
EXPECT_EQ("2015-01-04", Format(day + 1));
EXPECT_EQ("2015-01-05", Format(2 + day));
EXPECT_EQ("2015-01-02", Format(day - 1));
EXPECT_EQ("2015-01-02", Format(day -= 1));
EXPECT_EQ("2015-01-02", Format(day++));
EXPECT_EQ("2015-01-04", Format(++day));
EXPECT_EQ("2015-01-04", Format(day--));
EXPECT_EQ("2015-01-02", Format(--day));
civil_month month(2015, 1);
EXPECT_EQ("2015-02", Format(month += 1));
EXPECT_EQ("2015-03", Format(month + 1));
EXPECT_EQ("2015-04", Format(2 + month));
EXPECT_EQ("2015-01", Format(month - 1));
EXPECT_EQ("2015-01", Format(month -= 1));
EXPECT_EQ("2015-01", Format(month++));
EXPECT_EQ("2015-03", Format(++month));
EXPECT_EQ("2015-03", Format(month--));
EXPECT_EQ("2015-01", Format(--month));
civil_year year(2015);
EXPECT_EQ("2016", Format(year += 1));
EXPECT_EQ("2017", Format(year + 1));
EXPECT_EQ("2018", Format(2 + year));
EXPECT_EQ("2015", Format(year - 1));
EXPECT_EQ("2015", Format(year -= 1));
EXPECT_EQ("2015", Format(year++));
EXPECT_EQ("2017", Format(++year));
EXPECT_EQ("2017", Format(year--));
EXPECT_EQ("2015", Format(--year));
}
TEST(CivilTime, ArithmeticLimits) {
const int kIntMax = std::numeric_limits<int>::max();
const int kIntMin = std::numeric_limits<int>::min();
civil_second second(1970, 1, 1, 0, 0, 0);
second += kIntMax;
EXPECT_EQ("2038-01-19T03:14:07", Format(second));
second -= kIntMax;
EXPECT_EQ("1970-01-01T00:00:00", Format(second));
second += kIntMin;
EXPECT_EQ("1901-12-13T20:45:52", Format(second));
second -= kIntMin;
EXPECT_EQ("1970-01-01T00:00:00", Format(second));
civil_minute minute(1970, 1, 1, 0, 0);
minute += kIntMax;
EXPECT_EQ("6053-01-23T02:07", Format(minute));
minute -= kIntMax;
EXPECT_EQ("1970-01-01T00:00", Format(minute));
minute += kIntMin;
EXPECT_EQ("-2114-12-08T21:52", Format(minute));
minute -= kIntMin;
EXPECT_EQ("1970-01-01T00:00", Format(minute));
civil_hour hour(1970, 1, 1, 0);
hour += kIntMax;
EXPECT_EQ("246953-10-09T07", Format(hour));
hour -= kIntMax;
EXPECT_EQ("1970-01-01T00", Format(hour));
hour += kIntMin;
EXPECT_EQ("-243014-03-24T16", Format(hour));
hour -= kIntMin;
EXPECT_EQ("1970-01-01T00", Format(hour));
civil_day day(1970, 1, 1);
day += kIntMax;
EXPECT_EQ("5881580-07-11", Format(day));
day -= kIntMax;
EXPECT_EQ("1970-01-01", Format(day));
day += kIntMin;
EXPECT_EQ("-5877641-06-23", Format(day));
day -= kIntMin;
EXPECT_EQ("1970-01-01", Format(day));
civil_month month(1970, 1);
month += kIntMax;
EXPECT_EQ("178958940-08", Format(month));
month -= kIntMax;
EXPECT_EQ("1970-01", Format(month));
month += kIntMin;
EXPECT_EQ("-178955001-05", Format(month));
month -= kIntMin;
EXPECT_EQ("1970-01", Format(month));
civil_year year(0);
year += kIntMax;
EXPECT_EQ("2147483647", Format(year));
year -= kIntMax;
EXPECT_EQ("0", Format(year));
year += kIntMin;
EXPECT_EQ("-2147483648", Format(year));
year -= kIntMin;
EXPECT_EQ("0", Format(year));
}
TEST(CivilTime, ArithmeticDifference) {
civil_second second(2015, 1, 2, 3, 4, 5);
EXPECT_EQ(0, second - second);
EXPECT_EQ(10, (second + 10) - second);
EXPECT_EQ(-10, (second - 10) - second);
civil_minute minute(2015, 1, 2, 3, 4);
EXPECT_EQ(0, minute - minute);
EXPECT_EQ(10, (minute + 10) - minute);
EXPECT_EQ(-10, (minute - 10) - minute);
civil_hour hour(2015, 1, 2, 3);
EXPECT_EQ(0, hour - hour);
EXPECT_EQ(10, (hour + 10) - hour);
EXPECT_EQ(-10, (hour - 10) - hour);
civil_day day(2015, 1, 2);
EXPECT_EQ(0, day - day);
EXPECT_EQ(10, (day + 10) - day);
EXPECT_EQ(-10, (day - 10) - day);
civil_month month(2015, 1);
EXPECT_EQ(0, month - month);
EXPECT_EQ(10, (month + 10) - month);
EXPECT_EQ(-10, (month - 10) - month);
civil_year year(2015);
EXPECT_EQ(0, year - year);
EXPECT_EQ(10, (year + 10) - year);
EXPECT_EQ(-10, (year - 10) - year);
}
TEST(CivilTime, DifferenceLimits) {
const int kIntMax = std::numeric_limits<int>::max();
const int kIntMin = std::numeric_limits<int>::min();
// Check day arithmetic at the end of the year range.
const civil_day max_day(kIntMax, 12, 31);
EXPECT_EQ(1, max_day - (max_day - 1));
EXPECT_EQ(-1, (max_day - 1) - max_day);
// Check day arithmetic at the end of the year range.
const civil_day min_day(kIntMin, 1, 1);
EXPECT_EQ(1, (min_day + 1) - min_day);
EXPECT_EQ(-1, min_day - (min_day + 1));
// Check the limits of the return value.
const civil_day d1(1970, 1, 1);
const civil_day d2(5881580, 7, 11);
EXPECT_EQ(kIntMax, d2 - d1);
EXPECT_EQ(kIntMin, d1 - (d2 + 1));
}
TEST(CivilTime, Properties) {
civil_second ss(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, ss.year());
EXPECT_EQ(2, ss.month());
EXPECT_EQ(3, ss.day());
EXPECT_EQ(4, ss.hour());
EXPECT_EQ(5, ss.minute());
EXPECT_EQ(6, ss.second());
civil_minute mm(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, mm.year());
EXPECT_EQ(2, mm.month());
EXPECT_EQ(3, mm.day());
EXPECT_EQ(4, mm.hour());
EXPECT_EQ(5, mm.minute());
EXPECT_EQ(0, mm.second());
civil_hour hh(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, hh.year());
EXPECT_EQ(2, hh.month());
EXPECT_EQ(3, hh.day());
EXPECT_EQ(4, hh.hour());
EXPECT_EQ(0, hh.minute());
EXPECT_EQ(0, hh.second());
civil_day d(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, d.year());
EXPECT_EQ(2, d.month());
EXPECT_EQ(3, d.day());
EXPECT_EQ(0, d.hour());
EXPECT_EQ(0, d.minute());
EXPECT_EQ(0, d.second());
EXPECT_EQ(weekday::tuesday, get_weekday(d));
EXPECT_EQ(34, get_yearday(d));
civil_month m(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, m.year());
EXPECT_EQ(2, m.month());
EXPECT_EQ(1, m.day());
EXPECT_EQ(0, m.hour());
EXPECT_EQ(0, m.minute());
EXPECT_EQ(0, m.second());
civil_year y(2015, 2, 3, 4, 5, 6);
EXPECT_EQ(2015, y.year());
EXPECT_EQ(1, y.month());
EXPECT_EQ(1, y.day());
EXPECT_EQ(0, y.hour());
EXPECT_EQ(0, y.minute());
EXPECT_EQ(0, y.second());
}
TEST(CivilTime, OutputStream) {
// Tests formatting of civil_year, which does not pad.
EXPECT_EQ("2016", Format(civil_year(2016)));
EXPECT_EQ("123", Format(civil_year(123)));
EXPECT_EQ("0", Format(civil_year(0)));
EXPECT_EQ("-1", Format(civil_year(-1)));
// Tests formatting of sub-year types, which pad to 2 digits
EXPECT_EQ("2016-02", Format(civil_month(2016, 2)));
EXPECT_EQ("2016-02-03", Format(civil_day(2016, 2, 3)));
EXPECT_EQ("2016-02-03T04", Format(civil_hour(2016, 2, 3, 4)));
EXPECT_EQ("2016-02-03T04:05", Format(civil_minute(2016, 2, 3, 4, 5)));
EXPECT_EQ("2016-02-03T04:05:06", Format(civil_second(2016, 2, 3, 4, 5, 6)));
// Tests formatting of weekday.
EXPECT_EQ("Monday", Format(weekday::monday));
EXPECT_EQ("Tuesday", Format(weekday::tuesday));
EXPECT_EQ("Wednesday", Format(weekday::wednesday));
EXPECT_EQ("Thursday", Format(weekday::thursday));
EXPECT_EQ("Friday", Format(weekday::friday));
EXPECT_EQ("Saturday", Format(weekday::saturday));
EXPECT_EQ("Sunday", Format(weekday::sunday));
}
TEST(CivilTime, OutputStreamLeftFillWidth) {
civil_second cs(2016, 2, 3, 4, 5, 6);
{
std::stringstream ss;
ss << std::left << std::setfill('.');
ss << std::setw(3) << 'X';
ss << std::setw(21) << civil_year(cs);
ss << std::setw(3) << 'X';
EXPECT_EQ("X..2016.................X..", ss.str());
}
{
std::stringstream ss;
ss << std::left << std::setfill('.');
ss << std::setw(3) << 'X';
ss << std::setw(21) << civil_month(cs);
ss << std::setw(3) << 'X';
EXPECT_EQ("X..2016-02..............X..", ss.str());
}
{
std::stringstream ss;
ss << std::left << std::setfill('.');
ss << std::setw(3) << 'X';
ss << std::setw(21) << civil_day(cs);
ss << std::setw(3) << 'X';
EXPECT_EQ("X..2016-02-03...........X..", ss.str());
}
{
std::stringstream ss;
ss << std::left << std::setfill('.');
ss << std::setw(3) << 'X';
ss << std::setw(21) << civil_hour(cs);
ss << std::setw(3) << 'X';
EXPECT_EQ("X..2016-02-03T04........X..", ss.str());
}
{
std::stringstream ss;
ss << std::left << std::setfill('.');
ss << std::setw(3) << 'X';
ss << std::setw(21) << civil_minute(cs);
ss << std::setw(3) << 'X';
EXPECT_EQ("X..2016-02-03T04:05.....X..", ss.str());
}
{
std::stringstream ss;
ss << std::left << std::setfill('.');
ss << std::setw(3) << 'X';
ss << std::setw(21) << civil_second(cs);
ss << std::setw(3) << 'X';
EXPECT_EQ("X..2016-02-03T04:05:06..X..", ss.str());
}
}
TEST(CivilTime, NextPrevWeekday) {
// Jan 1, 1970 was a Thursday.
const civil_day thursday(1970, 1, 1);
EXPECT_EQ(weekday::thursday, get_weekday(thursday));
// Thursday -> Thursday
civil_day d = next_weekday(thursday, weekday::thursday);
EXPECT_EQ(7, d - thursday) << Format(d);
EXPECT_EQ(d - 14, prev_weekday(thursday, weekday::thursday));
// Thursday -> Friday
d = next_weekday(thursday, weekday::friday);
EXPECT_EQ(1, d - thursday) << Format(d);
EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::friday));
// Thursday -> Saturday
d = next_weekday(thursday, weekday::saturday);
EXPECT_EQ(2, d - thursday) << Format(d);
EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::saturday));
// Thursday -> Sunday
d = next_weekday(thursday, weekday::sunday);
EXPECT_EQ(3, d - thursday) << Format(d);
EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::sunday));
// Thursday -> Monday
d = next_weekday(thursday, weekday::monday);
EXPECT_EQ(4, d - thursday) << Format(d);
EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::monday));
// Thursday -> Tuesday
d = next_weekday(thursday, weekday::tuesday);
EXPECT_EQ(5, d - thursday) << Format(d);
EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::tuesday));
// Thursday -> Wednesday
d = next_weekday(thursday, weekday::wednesday);
EXPECT_EQ(6, d - thursday) << Format(d);
EXPECT_EQ(d - 7, prev_weekday(thursday, weekday::wednesday));
}
TEST(CivilTime, NormalizeWithHugeYear) {
civil_month c(9223372036854775807, 1);
EXPECT_EQ("9223372036854775807-01", Format(c));
c = c - 1; // Causes normalization
EXPECT_EQ("9223372036854775806-12", Format(c));
c = civil_month(-9223372036854775807 - 1, 1);
EXPECT_EQ("-9223372036854775808-01", Format(c));
c = c + 12; // Causes normalization
EXPECT_EQ("-9223372036854775807-01", Format(c));
}
TEST(CivilTime, LeapYears) {
// Test data for leap years.
const struct {
int year;
int days;
struct {
int month;
int day;
} leap_day; // The date of the day after Feb 28.
} kLeapYearTable[]{
{1900, 365, {3, 1}},
{1999, 365, {3, 1}},
{2000, 366, {2, 29}}, // leap year
{2001, 365, {3, 1}},
{2002, 365, {3, 1}},
{2003, 365, {3, 1}},
{2004, 366, {2, 29}}, // leap year
{2005, 365, {3, 1}},
{2006, 365, {3, 1}},
{2007, 365, {3, 1}},
{2008, 366, {2, 29}}, // leap year
{2009, 365, {3, 1}},
{2100, 365, {3, 1}},
};
for (const auto& e : kLeapYearTable) {
// Tests incrementing through the leap day.
const civil_day feb28(e.year, 2, 28);
const civil_day next_day = feb28 + 1;
EXPECT_EQ(e.leap_day.month, next_day.month());
EXPECT_EQ(e.leap_day.day, next_day.day());
// Tests difference in days of leap years.
const civil_year year(feb28);
const civil_year next_year = year + 1;
EXPECT_EQ(e.days, civil_day(next_year) - civil_day(year));
}
}
TEST(CivilTime, FirstThursdayInMonth) {
const civil_day nov1(2014, 11, 1);
const civil_day thursday = prev_weekday(nov1, weekday::thursday) + 7;
EXPECT_EQ("2014-11-06", Format(thursday));
// Bonus: Date of Thanksgiving in the United States
// Rule: Fourth Thursday of November
const civil_day thanksgiving = thursday + 7 * 3;
EXPECT_EQ("2014-11-27", Format(thanksgiving));
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "time_zone_fixed.h"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <string>
namespace absl {
namespace time_internal {
namespace cctz {
namespace {
// The prefix used for the internal names of fixed-offset zones.
const char kFixedOffsetPrefix[] = "Fixed/";
int Parse02d(const char* p) {
static const char kDigits[] = "0123456789";
if (const char* ap = std::strchr(kDigits, *p)) {
int v = static_cast<int>(ap - kDigits);
if (const char* bp = std::strchr(kDigits, *++p)) {
return (v * 10) + static_cast<int>(bp - kDigits);
}
}
return -1;
}
} // namespace
bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) {
if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
*offset = sys_seconds::zero();
return true;
}
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
const char* const ep = kFixedOffsetPrefix + prefix_len;
if (name.size() != prefix_len + 12) // "<prefix>UTC+99:99:99"
return false;
if (!std::equal(kFixedOffsetPrefix, ep, name.begin()))
return false;
const char* np = name.data() + prefix_len;
if (*np++ != 'U' || *np++ != 'T' || *np++ != 'C')
return false;
if (np[0] != '+' && np[0] != '-')
return false;
if (np[3] != ':' || np[6] != ':') // see note below about large offsets
return false;
int hours = Parse02d(np + 1);
if (hours == -1) return false;
int mins = Parse02d(np + 4);
if (mins == -1) return false;
int secs = Parse02d(np + 7);
if (secs == -1) return false;
secs += ((hours * 60) + mins) * 60;
if (secs > 24 * 60 * 60) return false; // outside supported offset range
*offset = sys_seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
return true;
}
std::string FixedOffsetToName(const sys_seconds& offset) {
if (offset == sys_seconds::zero()) return "UTC";
if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) {
// We don't support fixed-offset zones more than 24 hours
// away from UTC to avoid complications in rendering such
// 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;
if (sign == '-') {
if (seconds > 0) {
seconds -= 60;
minutes += 1;
}
seconds = -seconds;
minutes = -minutes;
}
int hours = minutes / 60;
minutes %= 60;
char buf[sizeof(kFixedOffsetPrefix) + sizeof("UTC-24:00:00")];
snprintf(buf, sizeof(buf), "%sUTC%c%02d:%02d:%02d",
kFixedOffsetPrefix, sign, hours, minutes, seconds);
return buf;
}
std::string FixedOffsetToAbbr(const sys_seconds& offset) {
std::string abbr = FixedOffsetToName(offset);
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
const char* const ep = kFixedOffsetPrefix + prefix_len;
if (abbr.size() >= prefix_len) {
if (std::equal(kFixedOffsetPrefix, ep, abbr.begin())) {
abbr.erase(0, prefix_len);
if (abbr.size() == 12) { // UTC+99:99:99
abbr.erase(9, 1); // UTC+99:9999
abbr.erase(6, 1); // UTC+999999
if (abbr[8] == '0' && abbr[9] == '0') { // UTC+999900
abbr.erase(8, 2); // UTC+9999
if (abbr[6] == '0' && abbr[7] == '0') { // UTC+9900
abbr.erase(6, 2); // UTC+99
if (abbr[4] == '0') { // UTC+09
abbr.erase(4, 1); // UTC+9
}
}
}
}
}
}
return abbr;
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
#include <string>
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
namespace time_internal {
namespace cctz {
// Helper functions for dealing with the names and abbreviations
// of time zones that are a fixed offset (seconds east) from UTC.
// FixedOffsetFromName() extracts the offset from a valid fixed-offset
// name, while FixedOffsetToName() and FixedOffsetToAbbr() generate
// the canonical zone name and abbreviation respectively for the given
// offset.
//
// A fixed-offset name looks like "Fixed/UTC<+-><hours>:<mins>:<secs>".
// Its abbreviation is of the form "UTC(<+->H?H(MM(SS)?)?)?" where the
// optional pieces are omitted when their values are zero. (Note that
// the sign is the opposite of that used in a POSIX TZ specification.)
//
// Note: FixedOffsetFromName() fails on syntax errors or when the parsed
// offset exceeds 24 hours. FixedOffsetToName() and FixedOffsetToAbbr()
// both produce "UTC" when the argument offset exceeds 24 hours.
bool FixedOffsetFromName(const std::string& name, sys_seconds* offset);
std::string FixedOffsetToName(const sys_seconds& offset);
std::string FixedOffsetToAbbr(const sys_seconds& offset);
} // namespace cctz
} // namespace time_internal
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if !defined(HAS_STRPTIME)
# if !defined(_MSC_VER)
# define HAS_STRPTIME 1 // assume everyone has strptime() except windows
# endif
#endif
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#include <cctype>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <ctime>
#include <limits>
#include <string>
#include <vector>
#if !HAS_STRPTIME
#include <iomanip>
#include <sstream>
#endif
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "time_zone_if.h"
namespace absl {
namespace time_internal {
namespace cctz {
namespace detail {
namespace {
#if !HAS_STRPTIME
// Build a strptime() using C++11's std::get_time().
char* strptime(const char* s, const char* fmt, std::tm* tm) {
std::istringstream input(s);
input >> std::get_time(tm, fmt);
if (input.fail()) return nullptr;
return const_cast<char*>(s) +
(input.eof() ? strlen(s) : static_cast<std::size_t>(input.tellg()));
}
#endif
std::tm ToTM(const time_zone::absolute_lookup& al) {
std::tm tm{};
tm.tm_sec = al.cs.second();
tm.tm_min = al.cs.minute();
tm.tm_hour = al.cs.hour();
tm.tm_mday = al.cs.day();
tm.tm_mon = al.cs.month() - 1;
// Saturate tm.tm_year is cases of over/underflow.
if (al.cs.year() < std::numeric_limits<int>::min() + 1900) {
tm.tm_year = std::numeric_limits<int>::min();
} else if (al.cs.year() - 1900 > std::numeric_limits<int>::max()) {
tm.tm_year = std::numeric_limits<int>::max();
} else {
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_isdst = al.is_dst ? 1 : 0;
return tm;
}
const char kDigits[] = "0123456789";
// Formats a 64-bit integer in the given field width. Note that it is up
// to the caller of Format64() [and Format02d()/FormatOffset()] to ensure
// that there is sufficient space before ep to hold the conversion.
char* Format64(char* ep, int width, std::int_fast64_t v) {
bool neg = false;
if (v < 0) {
--width;
neg = true;
if (v == std::numeric_limits<std::int_fast64_t>::min()) {
// Avoid negating minimum value.
std::int_fast64_t last_digit = -(v % 10);
v /= 10;
if (last_digit < 0) {
++v;
last_digit += 10;
}
--width;
*--ep = kDigits[last_digit];
}
v = -v;
}
do {
--width;
*--ep = kDigits[v % 10];
} while (v /= 10);
while (--width >= 0) *--ep = '0'; // zero pad
if (neg) *--ep = '-';
return ep;
}
// Formats [0 .. 99] as %02d.
char* Format02d(char* ep, int v) {
*--ep = kDigits[v % 10];
*--ep = kDigits[(v / 10) % 10];
return ep;
}
// Formats a UTC offset, like +00:00.
char* FormatOffset(char* ep, int offset, const char* mode) {
char sign = '+';
if (offset < 0) {
offset = -offset; // bounded by 24h so no overflow
sign = '-';
}
char sep = mode[0];
if (sep != '\0' && mode[1] == '*') {
ep = Format02d(ep, offset % 60);
*--ep = sep;
}
int minutes = offset / 60;
ep = Format02d(ep, minutes % 60);
if (sep != '\0') *--ep = sep;
ep = Format02d(ep, minutes / 60);
*--ep = sign;
return ep;
}
// Formats a std::tm using strftime(3).
void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) {
// strftime(3) returns the number of characters placed in the output
// array (which may be 0 characters). It also returns 0 to indicate
// an error, like the array wasn't large enough. To accommodate this,
// the following code grows the buffer size from 2x the format std::string
// length up to 32x.
for (std::size_t i = 2; i != 32; i *= 2) {
std::size_t buf_size = fmt.size() * i;
std::vector<char> buf(buf_size);
if (std::size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) {
out->append(&buf[0], len);
return;
}
}
}
// Used for %E#S/%E#f specifiers and for data values in parse().
template <typename T>
const char* ParseInt(const char* dp, int width, T min, T max, T* vp) {
if (dp != nullptr) {
const T kmin = std::numeric_limits<T>::min();
bool erange = false;
bool neg = false;
T value = 0;
if (*dp == '-') {
neg = true;
if (width <= 0 || --width != 0) {
++dp;
} else {
dp = nullptr; // width was 1
}
}
if (const char* const bp = dp) {
while (const char* cp = strchr(kDigits, *dp)) {
int d = static_cast<int>(cp - kDigits);
if (d >= 10) break;
if (value < kmin / 10) {
erange = true;
break;
}
value *= 10;
if (value < kmin + d) {
erange = true;
break;
}
value -= d;
dp += 1;
if (width > 0 && --width == 0) break;
}
if (dp != bp && !erange && (neg || value != kmin)) {
if (!neg || value != 0) {
if (!neg) value = -value; // make positive
if (min <= value && value <= max) {
*vp = value;
} else {
dp = nullptr;
}
} else {
dp = nullptr;
}
} else {
dp = nullptr;
}
}
}
return dp;
}
// The number of base-10 digits that can be represented by a signed 64-bit
// integer. That is, 10^kDigits10_64 <= 2^63 - 1 < 10^(kDigits10_64 + 1).
const int kDigits10_64 = 18;
// 10^n for everything that can be represented by a signed 64-bit integer.
const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
};
} // namespace
// Uses strftime(3) to format the given Time. The following extended format
// specifiers are also supported:
//
// - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
// - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
// - %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)
//
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
// handled internally for performance reasons. strftime(3) is slow due to
// a POSIX requirement to respect changes to ${TZ}.
//
// The TZ/GNU %s extension is handled internally because strftime() has
// to use mktime() to generate it, and that assumes the local time zone.
//
// We also handle the %z and %Z specifiers to accommodate platforms that do
// not support the tm_gmtoff and tm_zone extensions to std::tm.
//
// Requires that zero() <= fs < seconds(1).
std::string format(const std::string& format, const time_point<sys_seconds>& tp,
const detail::femtoseconds& fs, const time_zone& tz) {
std::string result;
result.reserve(format.size()); // A reasonable guess for the result size.
const time_zone::absolute_lookup al = tz.lookup(tp);
const std::tm tm = ToTM(al);
// Scratch buffer for internal conversions.
char buf[3 + kDigits10_64]; // enough for longest conversion
char* const ep = buf + sizeof(buf);
char* bp; // works back from ep
// Maintain three, disjoint subsequences that span format.
// [format.begin() ... pending) : already formatted into result
// [pending ... cur) : formatting pending, but no special cases
// [cur ... format.end()) : unexamined
// Initially, everything is in the unexamined part.
const char* pending = format.c_str(); // NUL terminated
const char* cur = pending;
const char* end = pending + format.length();
while (cur != end) { // while something is unexamined
// Moves cur to the next percent sign.
const char* start = cur;
while (cur != end && *cur != '%') ++cur;
// If the new pending text is all ordinary, copy it out.
if (cur != start && pending == start) {
result.append(pending, static_cast<std::size_t>(cur - pending));
pending = start = cur;
}
// Span the sequential percent signs.
const char* percent = cur;
while (cur != end && *cur == '%') ++cur;
// If the new pending text is all percents, copy out one
// percent for every matched pair, then skip those pairs.
if (cur != start && pending == start) {
std::size_t escaped = static_cast<std::size_t>(cur - pending) / 2;
result.append(pending, escaped);
pending += escaped * 2;
// Also copy out a single trailing percent.
if (pending != cur && cur == end) {
result.push_back(*pending++);
}
}
// Loop unless we have an unescaped percent.
if (cur == end || (cur - percent) % 2 == 0) continue;
// Simple specifiers that we handle ourselves.
if (strchr("YmdeHMSzZs%", *cur)) {
if (cur - 1 != pending) {
FormatTM(&result, std::string(pending, cur - 1), tm);
}
switch (*cur) {
case 'Y':
// This avoids the tm.tm_year overflow problem for %Y, however
// tm.tm_year will still be used by other specifiers like %D.
bp = Format64(ep, 0, al.cs.year());
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'm':
bp = Format02d(ep, al.cs.month());
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'd':
case 'e':
bp = Format02d(ep, al.cs.day());
if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows
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));
break;
case 'M':
bp = Format02d(ep, al.cs.minute());
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'S':
bp = Format02d(ep, al.cs.second());
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'z':
bp = FormatOffset(ep, al.offset, "");
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case 'Z':
result.append(al.abbr);
break;
case 's':
bp = Format64(ep, 0, ToUnixSeconds(tp));
result.append(bp, static_cast<std::size_t>(ep - bp));
break;
case '%':
result.push_back('%');
break;
}
pending = ++cur;
continue;
}
// Loop if there is no E modifier.
if (*cur != 'E' || ++cur == end) continue;
// Format our extensions.
if (*cur == 'z') {
// Formats %Ez.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
bp = FormatOffset(ep, al.offset, ":");
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = ++cur;
} else if (*cur == '*' && cur + 1 != end && *(cur + 1) == 'z') {
// Formats %E*z.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
bp = FormatOffset(ep, al.offset, ":*");
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = cur += 2;
} else if (*cur == '*' && cur + 1 != end &&
(*(cur + 1) == 'S' || *(cur + 1) == 'f')) {
// Formats %E*S or %E*F.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
char* cp = ep;
bp = Format64(cp, 15, fs.count());
while (cp != bp && cp[-1] == '0') --cp;
switch (*(cur + 1)) {
case 'S':
if (cp != bp) *--bp = '.';
bp = Format02d(bp, al.cs.second());
break;
case 'f':
if (cp == bp) *--bp = '0';
break;
}
result.append(bp, static_cast<std::size_t>(cp - bp));
pending = cur += 2;
} else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') {
// Formats %E4Y.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
bp = Format64(ep, 4, al.cs.year());
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = cur += 2;
} else if (std::isdigit(*cur)) {
// Possibly found %E#S or %E#f.
int n = 0;
if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) {
if (*np == 'S' || *np == 'f') {
// Formats %E#S or %E#f.
if (cur - 2 != pending) {
FormatTM(&result, std::string(pending, cur - 2), tm);
}
bp = ep;
if (n > 0) {
if (n > kDigits10_64) n = kDigits10_64;
bp = Format64(bp, n, (n > 15) ? fs.count() * kExp10[n - 15]
: fs.count() / kExp10[15 - n]);
if (*np == 'S') *--bp = '.';
}
if (*np == 'S') bp = Format02d(bp, al.cs.second());
result.append(bp, static_cast<std::size_t>(ep - bp));
pending = cur = ++np;
}
}
}
}
// Formats any remaining data.
if (end != pending) {
FormatTM(&result, std::string(pending, end), tm);
}
return result;
}
namespace {
const char* ParseOffset(const char* dp, const char* mode, int* offset) {
if (dp != nullptr) {
const char first = *dp++;
if (first == '+' || first == '-') {
char sep = mode[0];
int hours = 0;
int minutes = 0;
int seconds = 0;
const char* ap = ParseInt(dp, 2, 0, 23, &hours);
if (ap != nullptr && ap - dp == 2) {
dp = ap;
if (sep != '\0' && *ap == sep) ++ap;
const char* bp = ParseInt(ap, 2, 0, 59, &minutes);
if (bp != nullptr && bp - ap == 2) {
dp = bp;
if (sep != '\0' && *bp == sep) ++bp;
const char* cp = ParseInt(bp, 2, 0, 59, &seconds);
if (cp != nullptr && cp - bp == 2) dp = cp;
}
*offset = ((hours * 60 + minutes) * 60) + seconds;
if (first == '-') *offset = -*offset;
} else {
dp = nullptr;
}
} else if (first == 'Z') { // Zulu
*offset = 0;
} else {
dp = nullptr;
}
}
return dp;
}
const char* ParseZone(const char* dp, std::string* zone) {
zone->clear();
if (dp != nullptr) {
while (*dp != '\0' && !std::isspace(*dp)) zone->push_back(*dp++);
if (zone->empty()) dp = nullptr;
}
return dp;
}
const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) {
if (dp != nullptr) {
std::int_fast64_t v = 0;
std::int_fast64_t exp = 0;
const char* const bp = dp;
while (const char* cp = strchr(kDigits, *dp)) {
int d = static_cast<int>(cp - kDigits);
if (d >= 10) break;
if (exp < 15) {
exp += 1;
v *= 10;
v += d;
}
++dp;
}
if (dp != bp) {
v *= kExp10[15 - exp];
*subseconds = detail::femtoseconds(v);
} else {
dp = nullptr;
}
}
return dp;
}
// Parses a std::string into a std::tm using strptime(3).
const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
if (dp != nullptr) {
dp = strptime(dp, fmt, tm);
}
return dp;
}
} // 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 standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
// handled internally so that we can normally avoid strptime() altogether
// (which is particularly helpful when the native implementation is broken).
//
// The TZ/GNU %s extension is handled internally because strptime() has to
// use localtime_r() to generate it, and that assumes the local time zone.
//
// We also handle the %z specifier to accommodate platforms that do not
// support the tm_gmtoff extension to std::tm. %Z is parsed but ignored.
bool parse(const std::string& format, const std::string& input,
const time_zone& tz, time_point<sys_seconds>* sec,
detail::femtoseconds* fs, std::string* err) {
// The unparsed input.
const char* data = input.c_str(); // NUL terminated
// Skips leading whitespace.
while (std::isspace(*data)) ++data;
const year_t kyearmax = std::numeric_limits<year_t>::max();
const year_t kyearmin = std::numeric_limits<year_t>::min();
// Sets default values for unspecified fields.
bool saw_year = false;
year_t year = 1970;
std::tm tm{};
tm.tm_year = 1970 - 1900;
tm.tm_mon = 1 - 1; // Jan
tm.tm_mday = 1;
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
tm.tm_wday = 4; // Thu
tm.tm_yday = 0;
tm.tm_isdst = 0;
auto subseconds = detail::femtoseconds::zero();
bool saw_offset = false;
int offset = 0; // No offset from passed tz.
std::string zone = "UTC";
const char* fmt = format.c_str(); // NUL terminated
bool twelve_hour = false;
bool afternoon = false;
bool saw_percent_s = false;
std::int_fast64_t percent_s = 0;
// Steps through format, one specifier at a time.
while (data != nullptr && *fmt != '\0') {
if (std::isspace(*fmt)) {
while (std::isspace(*data)) ++data;
while (std::isspace(*++fmt)) continue;
continue;
}
if (*fmt != '%') {
if (*data == *fmt) {
++data;
++fmt;
} else {
data = nullptr;
}
continue;
}
const char* percent = fmt;
if (*++fmt == '\0') {
data = nullptr;
continue;
}
switch (*fmt++) {
case 'Y':
// Symmetrically with FormatTime(), directly handing %Y avoids the
// tm.tm_year overflow problem. However, tm.tm_year will still be
// used by other specifiers like %D.
data = ParseInt(data, 0, kyearmin, kyearmax, &year);
if (data != nullptr) saw_year = true;
continue;
case 'm':
data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
if (data != nullptr) tm.tm_mon -= 1;
continue;
case 'd':
case 'e':
data = ParseInt(data, 2, 1, 31, &tm.tm_mday);
continue;
case 'H':
data = ParseInt(data, 2, 0, 23, &tm.tm_hour);
twelve_hour = false;
continue;
case 'M':
data = ParseInt(data, 2, 0, 59, &tm.tm_min);
continue;
case 'S':
data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
continue;
case 'I':
case 'l':
case 'r': // probably uses %I
twelve_hour = true;
break;
case 'R': // uses %H
case 'T': // uses %H
case 'c': // probably uses %H
case 'X': // probably uses %H
twelve_hour = false;
break;
case 'z':
data = ParseOffset(data, "", &offset);
if (data != nullptr) saw_offset = true;
continue;
case 'Z': // ignored; zone abbreviations are ambiguous
data = ParseZone(data, &zone);
continue;
case 's':
data = ParseInt(data, 0,
std::numeric_limits<std::int_fast64_t>::min(),
std::numeric_limits<std::int_fast64_t>::max(),
&percent_s);
if (data != nullptr) saw_percent_s = true;
continue;
case '%':
data = (*data == '%' ? data + 1 : nullptr);
continue;
case 'E':
if (*fmt == 'z' || (*fmt == '*' && *(fmt + 1) == 'z')) {
data = ParseOffset(data, ":", &offset);
if (data != nullptr) saw_offset = true;
fmt += (*fmt == 'z') ? 1 : 2;
continue;
}
if (*fmt == '*' && *(fmt + 1) == 'S') {
data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
if (data != nullptr && *data == '.') {
data = ParseSubSeconds(data + 1, &subseconds);
}
fmt += 2;
continue;
}
if (*fmt == '*' && *(fmt + 1) == 'f') {
if (data != nullptr && std::isdigit(*data)) {
data = ParseSubSeconds(data, &subseconds);
}
fmt += 2;
continue;
}
if (*fmt == '4' && *(fmt + 1) == 'Y') {
const char* bp = data;
data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year);
if (data != nullptr) {
if (data - bp == 4) {
saw_year = true;
} else {
data = nullptr; // stopped too soon
}
}
fmt += 2;
continue;
}
if (std::isdigit(*fmt)) {
int n = 0; // value ignored
if (const char* np = ParseInt(fmt, 0, 0, 1024, &n)) {
if (*np == 'S') {
data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
if (data != nullptr && *data == '.') {
data = ParseSubSeconds(data + 1, &subseconds);
}
fmt = ++np;
continue;
}
if (*np == 'f') {
if (data != nullptr && std::isdigit(*data)) {
data = ParseSubSeconds(data, &subseconds);
}
fmt = ++np;
continue;
}
}
}
if (*fmt == 'c') twelve_hour = false; // probably uses %H
if (*fmt == 'X') twelve_hour = false; // probably uses %H
if (*fmt != '\0') ++fmt;
break;
case 'O':
if (*fmt == 'H') twelve_hour = false;
if (*fmt == 'I') twelve_hour = true;
if (*fmt != '\0') ++fmt;
break;
}
// Parses the current specifier.
const char* orig_data = data;
std::string spec(percent, static_cast<std::size_t>(fmt - percent));
data = ParseTM(data, spec.c_str(), &tm);
// If we successfully parsed %p we need to remember whether the result
// was AM or PM so that we can adjust tm_hour before ConvertDateTime().
// So reparse the input with a known AM hour, and check if it is shifted
// to a PM hour.
if (spec == "%p" && data != nullptr) {
std::string test_input = "1";
test_input.append(orig_data, static_cast<std::size_t>(data - orig_data));
const char* test_data = test_input.c_str();
std::tm tmp{};
ParseTM(test_data, "%I%p", &tmp);
afternoon = (tmp.tm_hour == 13);
}
}
// Adjust a 12-hour tm_hour value if it should be in the afternoon.
if (twelve_hour && afternoon && tm.tm_hour < 12) {
tm.tm_hour += 12;
}
if (data == nullptr) {
if (err != nullptr) *err = "Failed to parse input";
return false;
}
// Skip any remaining whitespace.
while (std::isspace(*data)) ++data;
// parse() must consume the entire input std::string.
if (*data != '\0') {
if (err != nullptr) *err = "Illegal trailing data in input string";
return false;
}
// If we saw %s then we ignore anything else and return that time.
if (saw_percent_s) {
*sec = FromUnixSeconds(percent_s);
*fs = detail::femtoseconds::zero();
return true;
}
// If we saw %z, %Ez, or %E*z then we want to interpret the parsed fields
// in UTC and then shift by that offset. Otherwise we want to interpret
// the fields directly in the passed time_zone.
time_zone ptz = saw_offset ? utc_time_zone() : tz;
// Allows a leap second of 60 to normalize forward to the following ":00".
if (tm.tm_sec == 60) {
tm.tm_sec -= 1;
offset -= 1;
subseconds = detail::femtoseconds::zero();
}
if (!saw_year) {
year = year_t{tm.tm_year};
if (year > kyearmax - 1900) {
// Platform-dependent, maybe unreachable.
if (err != nullptr) *err = "Out-of-range year";
return false;
}
year += 1900;
}
const int month = tm.tm_mon + 1;
civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
// parse() should not allow normalization. Due to the restricted field
// ranges above (see ParseInt()), the only possibility is for days to roll
// into months. That is, parsing "Sep 31" should not produce "Oct 1".
if (cs.month() != month || cs.day() != tm.tm_mday) {
if (err != nullptr) *err = "Out-of-range field";
return false;
}
// Accounts for the offset adjustment before converting to absolute time.
if ((offset < 0 && cs > civil_second::max() + offset) ||
(offset > 0 && cs < civil_second::min() + offset)) {
if (err != nullptr) *err = "Out-of-range field";
return false;
}
cs -= offset;
const auto tp = ptz.lookup(cs).pre;
// Checks for overflow/underflow and returns an error as necessary.
if (tp == time_point<sys_seconds>::max()) {
const auto al = ptz.lookup(time_point<sys_seconds>::max());
if (cs > al.cs) {
if (err != nullptr) *err = "Out-of-range field";
return false;
}
}
if (tp == time_point<sys_seconds>::min()) {
const auto al = ptz.lookup(time_point<sys_seconds>::min());
if (cs < al.cs) {
if (err != nullptr) *err = "Out-of-range field";
return false;
}
}
*sec = tp;
*fs = subseconds;
return true;
}
} // namespace detail
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#include <chrono>
#include <iomanip>
#include <sstream>
#include <string>
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using std::chrono::time_point_cast;
using std::chrono::system_clock;
using std::chrono::nanoseconds;
using std::chrono::microseconds;
using std::chrono::milliseconds;
using std::chrono::seconds;
using std::chrono::minutes;
using std::chrono::hours;
using testing::HasSubstr;
namespace absl {
namespace time_internal {
namespace cctz {
namespace {
// This helper is a macro so that failed expectations show up with the
// correct line numbers.
#define ExpectTime(tp, tz, y, m, d, hh, mm, ss, off, isdst, zone) \
do { \
time_zone::absolute_lookup al = tz.lookup(tp); \
EXPECT_EQ(y, al.cs.year()); \
EXPECT_EQ(m, al.cs.month()); \
EXPECT_EQ(d, al.cs.day()); \
EXPECT_EQ(hh, al.cs.hour()); \
EXPECT_EQ(mm, al.cs.minute()); \
EXPECT_EQ(ss, al.cs.second()); \
EXPECT_EQ(off, al.offset); \
EXPECT_TRUE(isdst == al.is_dst); \
EXPECT_STREQ(zone, al.abbr); \
} while (0)
const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
// A helper that tests the given format specifier by itself, and with leading
// and trailing characters. For example: TestFormatSpecifier(tp, "%a", "Thu").
template <typename D>
void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt,
const std::string& ans) {
EXPECT_EQ(ans, format(fmt, tp, tz)) << fmt;
EXPECT_EQ("xxx " + ans, format("xxx " + fmt, tp, tz));
EXPECT_EQ(ans + " yyy", format(fmt + " yyy", tp, tz));
EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz));
}
} // namespace
//
// Testing format()
//
TEST(Format, TimePointResolution) {
const char kFmt[] = "%H:%M:%E*S";
const time_zone utc = utc_time_zone();
const time_point<nanoseconds> t0 = system_clock::from_time_t(1420167845) +
milliseconds(123) + microseconds(456) +
nanoseconds(789);
EXPECT_EQ("03:04:05.123456789",
format(kFmt, time_point_cast<nanoseconds>(t0), utc));
EXPECT_EQ("03:04:05.123456",
format(kFmt, time_point_cast<microseconds>(t0), utc));
EXPECT_EQ("03:04:05.123",
format(kFmt, time_point_cast<milliseconds>(t0), utc));
EXPECT_EQ("03:04:05",
format(kFmt, time_point_cast<seconds>(t0), utc));
EXPECT_EQ("03:04:05",
format(kFmt, time_point_cast<sys_seconds>(t0), utc));
EXPECT_EQ("03:04:00",
format(kFmt, time_point_cast<minutes>(t0), utc));
EXPECT_EQ("03:00:00",
format(kFmt, time_point_cast<hours>(t0), utc));
}
TEST(Format, TimePointExtendedResolution) {
const char kFmt[] = "%H:%M:%E*S";
const time_zone utc = utc_time_zone();
const time_point<sys_seconds> tp =
std::chrono::time_point_cast<sys_seconds>(
std::chrono::system_clock::from_time_t(0)) +
std::chrono::hours(12) + std::chrono::minutes(34) +
std::chrono::seconds(56);
EXPECT_EQ(
"12:34:56.123456789012345",
detail::format(kFmt, tp, detail::femtoseconds(123456789012345), utc));
EXPECT_EQ(
"12:34:56.012345678901234",
detail::format(kFmt, tp, detail::femtoseconds(12345678901234), utc));
EXPECT_EQ(
"12:34:56.001234567890123",
detail::format(kFmt, tp, detail::femtoseconds(1234567890123), utc));
EXPECT_EQ(
"12:34:56.000123456789012",
detail::format(kFmt, tp, detail::femtoseconds(123456789012), utc));
EXPECT_EQ("12:34:56.000000000000123",
detail::format(kFmt, tp, detail::femtoseconds(123), utc));
EXPECT_EQ("12:34:56.000000000000012",
detail::format(kFmt, tp, detail::femtoseconds(12), utc));
EXPECT_EQ("12:34:56.000000000000001",
detail::format(kFmt, tp, detail::femtoseconds(1), utc));
}
TEST(Format, Basics) {
time_zone tz = utc_time_zone();
time_point<nanoseconds> tp = system_clock::from_time_t(0);
// Starts with a couple basic edge cases.
EXPECT_EQ("", format("", tp, tz));
EXPECT_EQ(" ", format(" ", tp, tz));
EXPECT_EQ(" ", format(" ", tp, tz));
EXPECT_EQ("xxx", format("xxx", tp, tz));
std::string big(128, 'x');
EXPECT_EQ(big, format(big, tp, tz));
// Cause the 1024-byte buffer to grow.
std::string bigger(100000, 'x');
EXPECT_EQ(bigger, format(bigger, tp, tz));
tp += hours(13) + minutes(4) + seconds(5);
tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
EXPECT_EQ("1970-01-01", format("%Y-%m-%d", tp, tz));
EXPECT_EQ("13:04:05", format("%H:%M:%S", tp, tz));
EXPECT_EQ("13:04:05.006", format("%H:%M:%E3S", tp, tz));
EXPECT_EQ("13:04:05.006007", format("%H:%M:%E6S", tp, tz));
EXPECT_EQ("13:04:05.006007008", format("%H:%M:%E9S", tp, tz));
}
TEST(Format, PosixConversions) {
const time_zone tz = utc_time_zone();
auto tp = system_clock::from_time_t(0);
TestFormatSpecifier(tp, tz, "%d", "01");
TestFormatSpecifier(tp, tz, "%e", " 1"); // extension but internal support
TestFormatSpecifier(tp, tz, "%H", "00");
TestFormatSpecifier(tp, tz, "%I", "12");
TestFormatSpecifier(tp, tz, "%j", "001");
TestFormatSpecifier(tp, tz, "%m", "01");
TestFormatSpecifier(tp, tz, "%M", "00");
TestFormatSpecifier(tp, tz, "%S", "00");
TestFormatSpecifier(tp, tz, "%U", "00");
TestFormatSpecifier(tp, tz, "%w", "4"); // 4=Thursday
TestFormatSpecifier(tp, tz, "%W", "00");
TestFormatSpecifier(tp, tz, "%y", "70");
TestFormatSpecifier(tp, tz, "%Y", "1970");
TestFormatSpecifier(tp, tz, "%z", "+0000");
TestFormatSpecifier(tp, tz, "%Z", "UTC");
TestFormatSpecifier(tp, tz, "%%", "%");
#if defined(__linux__)
// SU/C99/TZ extensions
TestFormatSpecifier(tp, tz, "%C", "19");
TestFormatSpecifier(tp, tz, "%D", "01/01/70");
TestFormatSpecifier(tp, tz, "%F", "1970-01-01");
TestFormatSpecifier(tp, tz, "%g", "70");
TestFormatSpecifier(tp, tz, "%G", "1970");
TestFormatSpecifier(tp, tz, "%k", " 0");
TestFormatSpecifier(tp, tz, "%l", "12");
TestFormatSpecifier(tp, tz, "%n", "\n");
TestFormatSpecifier(tp, tz, "%R", "00:00");
TestFormatSpecifier(tp, tz, "%t", "\t");
TestFormatSpecifier(tp, tz, "%T", "00:00:00");
TestFormatSpecifier(tp, tz, "%u", "4"); // 4=Thursday
TestFormatSpecifier(tp, tz, "%V", "01");
TestFormatSpecifier(tp, tz, "%s", "0");
#endif
}
TEST(Format, LocaleSpecific) {
const time_zone tz = utc_time_zone();
auto tp = system_clock::from_time_t(0);
TestFormatSpecifier(tp, tz, "%a", "Thu");
TestFormatSpecifier(tp, tz, "%A", "Thursday");
TestFormatSpecifier(tp, tz, "%b", "Jan");
TestFormatSpecifier(tp, tz, "%B", "January");
// %c should at least produce the numeric year and time-of-day.
const std::string s = format("%c", tp, utc_time_zone());
EXPECT_THAT(s, HasSubstr("1970"));
EXPECT_THAT(s, HasSubstr("00:00:00"));
TestFormatSpecifier(tp, tz, "%p", "AM");
TestFormatSpecifier(tp, tz, "%x", "01/01/70");
TestFormatSpecifier(tp, tz, "%X", "00:00:00");
#if defined(__linux__)
// SU/C99/TZ extensions
TestFormatSpecifier(tp, tz, "%h", "Jan"); // Same as %b
TestFormatSpecifier(tp, tz, "%P", "am");
TestFormatSpecifier(tp, tz, "%r", "12:00:00 AM");
// Modified conversion specifiers %E_
TestFormatSpecifier(tp, tz, "%Ec", "Thu Jan 1 00:00:00 1970");
TestFormatSpecifier(tp, tz, "%EC", "19");
TestFormatSpecifier(tp, tz, "%Ex", "01/01/70");
TestFormatSpecifier(tp, tz, "%EX", "00:00:00");
TestFormatSpecifier(tp, tz, "%Ey", "70");
TestFormatSpecifier(tp, tz, "%EY", "1970");
// Modified conversion specifiers %O_
TestFormatSpecifier(tp, tz, "%Od", "01");
TestFormatSpecifier(tp, tz, "%Oe", " 1");
TestFormatSpecifier(tp, tz, "%OH", "00");
TestFormatSpecifier(tp, tz, "%OI", "12");
TestFormatSpecifier(tp, tz, "%Om", "01");
TestFormatSpecifier(tp, tz, "%OM", "00");
TestFormatSpecifier(tp, tz, "%OS", "00");
TestFormatSpecifier(tp, tz, "%Ou", "4"); // 4=Thursday
TestFormatSpecifier(tp, tz, "%OU", "00");
TestFormatSpecifier(tp, tz, "%OV", "01");
TestFormatSpecifier(tp, tz, "%Ow", "4"); // 4=Thursday
TestFormatSpecifier(tp, tz, "%OW", "00");
TestFormatSpecifier(tp, tz, "%Oy", "70");
#endif
}
TEST(Format, Escaping) {
const time_zone tz = utc_time_zone();
auto tp = system_clock::from_time_t(0);
TestFormatSpecifier(tp, tz, "%%", "%");
TestFormatSpecifier(tp, tz, "%%a", "%a");
TestFormatSpecifier(tp, tz, "%%b", "%b");
TestFormatSpecifier(tp, tz, "%%Ea", "%Ea");
TestFormatSpecifier(tp, tz, "%%Es", "%Es");
TestFormatSpecifier(tp, tz, "%%E3S", "%E3S");
TestFormatSpecifier(tp, tz, "%%OS", "%OS");
TestFormatSpecifier(tp, tz, "%%O3S", "%O3S");
// Multiple levels of escaping.
TestFormatSpecifier(tp, tz, "%%%Y", "%1970");
TestFormatSpecifier(tp, tz, "%%%E3S", "%00.000");
TestFormatSpecifier(tp, tz, "%%%%E3S", "%%E3S");
}
TEST(Format, ExtendedSeconds) {
const time_zone tz = utc_time_zone();
// No subseconds.
time_point<nanoseconds> tp = system_clock::from_time_t(0);
tp += seconds(5);
EXPECT_EQ("05", format("%E*S", tp, tz));
EXPECT_EQ("05", format("%E0S", tp, tz));
EXPECT_EQ("05.0", format("%E1S", tp, tz));
EXPECT_EQ("05.00", format("%E2S", tp, tz));
EXPECT_EQ("05.000", format("%E3S", tp, tz));
EXPECT_EQ("05.0000", format("%E4S", tp, tz));
EXPECT_EQ("05.00000", format("%E5S", tp, tz));
EXPECT_EQ("05.000000", format("%E6S", tp, tz));
EXPECT_EQ("05.0000000", format("%E7S", tp, tz));
EXPECT_EQ("05.00000000", format("%E8S", tp, tz));
EXPECT_EQ("05.000000000", format("%E9S", tp, tz));
EXPECT_EQ("05.0000000000", format("%E10S", tp, tz));
EXPECT_EQ("05.00000000000", format("%E11S", tp, tz));
EXPECT_EQ("05.000000000000", format("%E12S", tp, tz));
EXPECT_EQ("05.0000000000000", format("%E13S", tp, tz));
EXPECT_EQ("05.00000000000000", format("%E14S", tp, tz));
EXPECT_EQ("05.000000000000000", format("%E15S", tp, tz));
// With subseconds.
tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
EXPECT_EQ("05.006007008", format("%E*S", tp, tz));
EXPECT_EQ("05", format("%E0S", tp, tz));
EXPECT_EQ("05.0", format("%E1S", tp, tz));
EXPECT_EQ("05.00", format("%E2S", tp, tz));
EXPECT_EQ("05.006", format("%E3S", tp, tz));
EXPECT_EQ("05.0060", format("%E4S", tp, tz));
EXPECT_EQ("05.00600", format("%E5S", tp, tz));
EXPECT_EQ("05.006007", format("%E6S", tp, tz));
EXPECT_EQ("05.0060070", format("%E7S", tp, tz));
EXPECT_EQ("05.00600700", format("%E8S", tp, tz));
EXPECT_EQ("05.006007008", format("%E9S", tp, tz));
EXPECT_EQ("05.0060070080", format("%E10S", tp, tz));
EXPECT_EQ("05.00600700800", format("%E11S", tp, tz));
EXPECT_EQ("05.006007008000", format("%E12S", tp, tz));
EXPECT_EQ("05.0060070080000", format("%E13S", tp, tz));
EXPECT_EQ("05.00600700800000", format("%E14S", tp, tz));
EXPECT_EQ("05.006007008000000", format("%E15S", tp, tz));
// Times before the Unix epoch.
tp = system_clock::from_time_t(0) + microseconds(-1);
EXPECT_EQ("1969-12-31 23:59:59.999999",
format("%Y-%m-%d %H:%M:%E*S", tp, tz));
// Here is a "%E*S" case we got wrong for a while. While the first
// instant below is correctly rendered as "...:07.333304", the second
// one used to appear as "...:07.33330499999999999".
tp = system_clock::from_time_t(0) + microseconds(1395024427333304);
EXPECT_EQ("2014-03-17 02:47:07.333304",
format("%Y-%m-%d %H:%M:%E*S", tp, tz));
tp += microseconds(1);
EXPECT_EQ("2014-03-17 02:47:07.333305",
format("%Y-%m-%d %H:%M:%E*S", tp, tz));
}
TEST(Format, ExtendedSubeconds) {
const time_zone tz = utc_time_zone();
// No subseconds.
time_point<nanoseconds> tp = system_clock::from_time_t(0);
tp += seconds(5);
EXPECT_EQ("0", format("%E*f", tp, tz));
EXPECT_EQ("", format("%E0f", tp, tz));
EXPECT_EQ("0", format("%E1f", tp, tz));
EXPECT_EQ("00", format("%E2f", tp, tz));
EXPECT_EQ("000", format("%E3f", tp, tz));
EXPECT_EQ("0000", format("%E4f", tp, tz));
EXPECT_EQ("00000", format("%E5f", tp, tz));
EXPECT_EQ("000000", format("%E6f", tp, tz));
EXPECT_EQ("0000000", format("%E7f", tp, tz));
EXPECT_EQ("00000000", format("%E8f", tp, tz));
EXPECT_EQ("000000000", format("%E9f", tp, tz));
EXPECT_EQ("0000000000", format("%E10f", tp, tz));
EXPECT_EQ("00000000000", format("%E11f", tp, tz));
EXPECT_EQ("000000000000", format("%E12f", tp, tz));
EXPECT_EQ("0000000000000", format("%E13f", tp, tz));
EXPECT_EQ("00000000000000", format("%E14f", tp, tz));
EXPECT_EQ("000000000000000", format("%E15f", tp, tz));
// With subseconds.
tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
EXPECT_EQ("006007008", format("%E*f", tp, tz));
EXPECT_EQ("", format("%E0f", tp, tz));
EXPECT_EQ("0", format("%E1f", tp, tz));
EXPECT_EQ("00", format("%E2f", tp, tz));
EXPECT_EQ("006", format("%E3f", tp, tz));
EXPECT_EQ("0060", format("%E4f", tp, tz));
EXPECT_EQ("00600", format("%E5f", tp, tz));
EXPECT_EQ("006007", format("%E6f", tp, tz));
EXPECT_EQ("0060070", format("%E7f", tp, tz));
EXPECT_EQ("00600700", format("%E8f", tp, tz));
EXPECT_EQ("006007008", format("%E9f", tp, tz));
EXPECT_EQ("0060070080", format("%E10f", tp, tz));
EXPECT_EQ("00600700800", format("%E11f", tp, tz));
EXPECT_EQ("006007008000", format("%E12f", tp, tz));
EXPECT_EQ("0060070080000", format("%E13f", tp, tz));
EXPECT_EQ("00600700800000", format("%E14f", tp, tz));
EXPECT_EQ("006007008000000", format("%E15f", tp, tz));
// Times before the Unix epoch.
tp = system_clock::from_time_t(0) + microseconds(-1);
EXPECT_EQ("1969-12-31 23:59:59.999999",
format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
// Here is a "%E*S" case we got wrong for a while. While the first
// instant below is correctly rendered as "...:07.333304", the second
// one used to appear as "...:07.33330499999999999".
tp = system_clock::from_time_t(0) + microseconds(1395024427333304);
EXPECT_EQ("2014-03-17 02:47:07.333304",
format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
tp += microseconds(1);
EXPECT_EQ("2014-03-17 02:47:07.333305",
format("%Y-%m-%d %H:%M:%S.%E*f", tp, tz));
}
TEST(Format, CompareExtendSecondsVsSubseconds) {
const time_zone tz = utc_time_zone();
// This test case illustrates the differences/similarities between:
// fmt_A: %E<prec>S
// fmt_B: %S.%E<prec>f
auto fmt_A = [](const std::string& prec) { return "%E" + prec + "S"; };
auto fmt_B = [](const std::string& prec) { return "%S.%E" + prec + "f"; };
// No subseconds:
time_point<nanoseconds> tp = system_clock::from_time_t(0);
tp += seconds(5);
// ... %E*S and %S.%E*f are different.
EXPECT_EQ("05", format(fmt_A("*"), tp, tz));
EXPECT_EQ("05.0", format(fmt_B("*"), tp, tz));
// ... %E0S and %S.%E0f are different.
EXPECT_EQ("05", format(fmt_A("0"), tp, tz));
EXPECT_EQ("05.", format(fmt_B("0"), tp, tz));
// ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15].
for (int prec = 1; prec <= 15; ++prec) {
const std::string a = format(fmt_A(std::to_string(prec)), tp, tz);
const std::string b = format(fmt_B(std::to_string(prec)), tp, tz);
EXPECT_EQ(a, b) << "prec=" << prec;
}
// With subseconds:
// ... %E*S and %S.%E*f are the same.
tp += milliseconds(6) + microseconds(7) + nanoseconds(8);
EXPECT_EQ("05.006007008", format(fmt_A("*"), tp, tz));
EXPECT_EQ("05.006007008", format(fmt_B("*"), tp, tz));
// ... %E0S and %S.%E0f are different.
EXPECT_EQ("05", format(fmt_A("0"), tp, tz));
EXPECT_EQ("05.", format(fmt_B("0"), tp, tz));
// ... %E<prec>S and %S.%E<prec>f are the same for prec in [1:15].
for (int prec = 1; prec <= 15; ++prec) {
const std::string a = format(fmt_A(std::to_string(prec)), tp, tz);
const std::string b = format(fmt_B(std::to_string(prec)), tp, tz);
EXPECT_EQ(a, b) << "prec=" << prec;
}
}
TEST(Format, ExtendedOffset) {
auto tp = system_clock::from_time_t(0);
time_zone tz = utc_time_zone();
TestFormatSpecifier(tp, tz, "%Ez", "+00:00");
EXPECT_TRUE(load_time_zone("America/New_York", &tz));
TestFormatSpecifier(tp, tz, "%Ez", "-05:00");
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
TestFormatSpecifier(tp, tz, "%Ez", "-08:00");
EXPECT_TRUE(load_time_zone("Australia/Sydney", &tz));
TestFormatSpecifier(tp, tz, "%Ez", "+10:00");
EXPECT_TRUE(load_time_zone("Africa/Monrovia", &tz));
// The true offset is -00:44:30 but %z only gives (truncated) minutes.
TestFormatSpecifier(tp, tz, "%z", "-0044");
TestFormatSpecifier(tp, tz, "%Ez", "-00:44");
}
TEST(Format, ExtendedSecondOffset) {
const time_zone utc = utc_time_zone();
time_point<seconds> tp;
time_zone tz;
EXPECT_TRUE(load_time_zone("America/New_York", &tz));
tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc);
if (tz.lookup(tp).offset == -5 * 60 * 60) {
// We're likely dealing with zoneinfo that doesn't support really old
// timestamps, so America/New_York never looks to be on local mean time.
} else {
TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02");
TestFormatSpecifier(tp, tz, "%Ez", "-04:56");
}
tp += seconds(1);
TestFormatSpecifier(tp, tz, "%E*z", "-05:00:00");
EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz));
tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc);
TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
tp += seconds(1);
TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00");
}
TEST(Format, ExtendedYears) {
const time_zone utc = utc_time_zone();
const char e4y_fmt[] = "%E4Y%m%d"; // no separators
// %E4Y zero-pads the year to produce at least 4 chars, including the sign.
auto tp = convert(civil_second(-999, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("-9991127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(-99, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("-0991127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(-9, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("-0091127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(-1, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("-0011127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(0, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("00001127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(1, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("00011127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(9, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("00091127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(99, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("00991127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(999, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("09991127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(9999, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("99991127", format(e4y_fmt, tp, utc));
// When the year is outside [-999:9999], more than 4 chars are produced.
tp = convert(civil_second(-1000, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("-10001127", format(e4y_fmt, tp, utc));
tp = convert(civil_second(10000, 11, 27, 0, 0, 0), utc);
EXPECT_EQ("100001127", format(e4y_fmt, tp, utc));
}
TEST(Format, RFC3339Format) {
time_zone tz;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
time_point<nanoseconds> tp =
convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
tp += milliseconds(100);
EXPECT_EQ("1977-06-28T09:08:07.1-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
tp += milliseconds(20);
EXPECT_EQ("1977-06-28T09:08:07.12-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
tp += milliseconds(3);
EXPECT_EQ("1977-06-28T09:08:07.123-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
tp += microseconds(400);
EXPECT_EQ("1977-06-28T09:08:07.1234-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
tp += microseconds(50);
EXPECT_EQ("1977-06-28T09:08:07.12345-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
tp += microseconds(6);
EXPECT_EQ("1977-06-28T09:08:07.123456-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
tp += nanoseconds(700);
EXPECT_EQ("1977-06-28T09:08:07.1234567-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
tp += nanoseconds(80);
EXPECT_EQ("1977-06-28T09:08:07.12345678-07:00", format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
tp += nanoseconds(9);
EXPECT_EQ("1977-06-28T09:08:07.123456789-07:00",
format(RFC3339_full, tp, tz));
EXPECT_EQ("1977-06-28T09:08:07-07:00", format(RFC3339_sec, tp, tz));
}
TEST(Format, RFC1123Format) { // locale specific
time_zone tz;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
auto tp = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
EXPECT_EQ("Tue, 28 Jun 1977 09:08:07 -0700", format(RFC1123_full, tp, tz));
EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz));
}
//
// Testing parse()
//
TEST(Parse, TimePointResolution) {
const char kFmt[] = "%H:%M:%E*S";
const time_zone utc = utc_time_zone();
time_point<nanoseconds> tp_ns;
EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_ns));
EXPECT_EQ("03:04:05.123456789", format(kFmt, tp_ns, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ns));
EXPECT_EQ("03:04:05.123456", format(kFmt, tp_ns, utc));
time_point<microseconds> tp_us;
EXPECT_TRUE(parse(kFmt, "03:04:05.123456789", utc, &tp_us));
EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_us));
EXPECT_EQ("03:04:05.123456", format(kFmt, tp_us, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_us));
EXPECT_EQ("03:04:05.123", format(kFmt, tp_us, utc));
time_point<milliseconds> tp_ms;
EXPECT_TRUE(parse(kFmt, "03:04:05.123456", utc, &tp_ms));
EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_ms));
EXPECT_EQ("03:04:05.123", format(kFmt, tp_ms, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_ms));
EXPECT_EQ("03:04:05", format(kFmt, tp_ms, utc));
time_point<seconds> tp_s;
EXPECT_TRUE(parse(kFmt, "03:04:05.123", utc, &tp_s));
EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc));
EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_s));
EXPECT_EQ("03:04:05", format(kFmt, tp_s, utc));
time_point<minutes> tp_m;
EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_m));
EXPECT_EQ("03:04:00", format(kFmt, tp_m, utc));
time_point<hours> tp_h;
EXPECT_TRUE(parse(kFmt, "03:04:05", utc, &tp_h));
EXPECT_EQ("03:00:00", format(kFmt, tp_h, utc));
}
TEST(Parse, TimePointExtendedResolution) {
const char kFmt[] = "%H:%M:%E*S";
const time_zone utc = utc_time_zone();
time_point<sys_seconds> tp;
detail::femtoseconds fs;
EXPECT_TRUE(detail::parse(kFmt, "12:34:56.123456789012345", utc, &tp, &fs));
EXPECT_EQ("12:34:56.123456789012345", detail::format(kFmt, tp, fs, utc));
EXPECT_TRUE(detail::parse(kFmt, "12:34:56.012345678901234", utc, &tp, &fs));
EXPECT_EQ("12:34:56.012345678901234", detail::format(kFmt, tp, fs, utc));
EXPECT_TRUE(detail::parse(kFmt, "12:34:56.001234567890123", utc, &tp, &fs));
EXPECT_EQ("12:34:56.001234567890123", detail::format(kFmt, tp, fs, utc));
EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000123", utc, &tp, &fs));
EXPECT_EQ("12:34:56.000000000000123", detail::format(kFmt, tp, fs, utc));
EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000012", utc, &tp, &fs));
EXPECT_EQ("12:34:56.000000000000012", detail::format(kFmt, tp, fs, utc));
EXPECT_TRUE(detail::parse(kFmt, "12:34:56.000000000000001", utc, &tp, &fs));
EXPECT_EQ("12:34:56.000000000000001", detail::format(kFmt, tp, fs, utc));
}
TEST(Parse, Basics) {
time_zone tz = utc_time_zone();
time_point<nanoseconds> tp = system_clock::from_time_t(1234567890);
// Simple edge cases.
EXPECT_TRUE(parse("", "", tz, &tp));
EXPECT_EQ(system_clock::from_time_t(0), tp); // everything defaulted
EXPECT_TRUE(parse(" ", " ", tz, &tp));
EXPECT_TRUE(parse(" ", " ", tz, &tp));
EXPECT_TRUE(parse("x", "x", tz, &tp));
EXPECT_TRUE(parse("xxx", "xxx", tz, &tp));
EXPECT_TRUE(
parse("%Y-%m-%d %H:%M:%S %z", "2013-06-28 19:08:09 -0800", tz, &tp));
ExpectTime(tp, tz, 2013, 6, 29, 3, 8, 9, 0, false, "UTC");
}
TEST(Parse, WithTimeZone) {
time_zone tz;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
time_point<nanoseconds> tp;
// We can parse a std::string without a UTC offset if we supply a timezone.
EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &tp));
ExpectTime(tp, tz, 2013, 6, 28, 19, 8, 9, -7 * 60 * 60, true, "PDT");
// But the timezone is ignored when a UTC offset is present.
EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S %z", "2013-06-28 19:08:09 +0800",
utc_time_zone(), &tp));
ExpectTime(tp, tz, 2013, 6, 28, 19 - 8 - 7, 8, 9, -7 * 60 * 60, true, "PDT");
// Check a skipped time (a Spring DST transition). parse() returns
// the preferred-offset result, as defined for ConvertDateTime().
EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-03-13 02:15:00", tz, &tp));
ExpectTime(tp, tz, 2011, 3, 13, 3, 15, 0, -7 * 60 * 60, true, "PDT");
// Check a repeated time (a Fall DST transition). parse() returns
// the preferred-offset result, as defined for ConvertDateTime().
EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2011-11-06 01:15:00", tz, &tp));
ExpectTime(tp, tz, 2011, 11, 6, 1, 15, 0, -7 * 60 * 60, true, "PDT");
}
TEST(Parse, LeapSecond) {
time_zone tz;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz));
time_point<nanoseconds> tp;
// ":59" -> ":59"
EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59-08:00", tz, &tp));
ExpectTime(tp, tz, 2013, 6, 28, 8, 8, 59, -7 * 60 * 60, true, "PDT");
// ":59.5" -> ":59.5"
EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:59.5-08:00", tz, &tp));
ExpectTime(tp, tz, 2013, 6, 28, 8, 8, 59, -7 * 60 * 60, true, "PDT");
// ":60" -> ":00"
EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:60-08:00", tz, &tp));
ExpectTime(tp, tz, 2013, 6, 28, 8, 9, 0, -7 * 60 * 60, true, "PDT");
// ":60.5" -> ":00.0"
EXPECT_TRUE(parse(RFC3339_full, "2013-06-28T07:08:60.5-08:00", tz, &tp));
ExpectTime(tp, tz, 2013, 6, 28, 8, 9, 0, -7 * 60 * 60, true, "PDT");
// ":61" -> error
EXPECT_FALSE(parse(RFC3339_full, "2013-06-28T07:08:61-08:00", tz, &tp));
}
TEST(Parse, ErrorCases) {
const time_zone tz = utc_time_zone();
auto tp = system_clock::from_time_t(0);
// Illegal trailing data.
EXPECT_FALSE(parse("%S", "123", tz, &tp));
// Can't parse an illegal format specifier.
EXPECT_FALSE(parse("%Q", "x", tz, &tp));
// Fails because of trailing, unparsed data "blah".
EXPECT_FALSE(parse("%m-%d", "2-3 blah", tz, &tp));
// Trailing whitespace is allowed.
EXPECT_TRUE(parse("%m-%d", "2-3 ", tz, &tp));
EXPECT_EQ(2, convert(tp, utc_time_zone()).month());
EXPECT_EQ(3, convert(tp, utc_time_zone()).day());
// Feb 31 requires normalization.
EXPECT_FALSE(parse("%m-%d", "2-31", tz, &tp));
// Check that we cannot have spaces in UTC offsets.
EXPECT_TRUE(parse("%z", "-0203", tz, &tp));
EXPECT_FALSE(parse("%z", "- 2 3", tz, &tp));
EXPECT_TRUE(parse("%Ez", "-02:03", tz, &tp));
EXPECT_FALSE(parse("%Ez", "- 2: 3", tz, &tp));
// Check that we reject other malformed UTC offsets.
EXPECT_FALSE(parse("%Ez", "+-08:00", tz, &tp));
EXPECT_FALSE(parse("%Ez", "-+08:00", tz, &tp));
// Check that we do not accept "-0" in fields that allow zero.
EXPECT_FALSE(parse("%Y", "-0", tz, &tp));
EXPECT_FALSE(parse("%E4Y", "-0", tz, &tp));
EXPECT_FALSE(parse("%H", "-0", tz, &tp));
EXPECT_FALSE(parse("%M", "-0", tz, &tp));
EXPECT_FALSE(parse("%S", "-0", tz, &tp));
EXPECT_FALSE(parse("%z", "+-000", tz, &tp));
EXPECT_FALSE(parse("%Ez", "+-0:00", tz, &tp));
EXPECT_FALSE(parse("%z", "-00-0", tz, &tp));
EXPECT_FALSE(parse("%Ez", "-00:-0", tz, &tp));
}
TEST(Parse, PosixConversions) {
time_zone tz = utc_time_zone();
auto tp = system_clock::from_time_t(0);
const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
tp = reset;
EXPECT_TRUE(parse("%d", "15", tz, &tp));
EXPECT_EQ(15, convert(tp, tz).day());
// %e is an extension, but is supported internally.
tp = reset;
EXPECT_TRUE(parse("%e", "15", tz, &tp));
EXPECT_EQ(15, convert(tp, tz).day()); // Equivalent to %d
tp = reset;
EXPECT_TRUE(parse("%H", "17", tz, &tp));
EXPECT_EQ(17, convert(tp, tz).hour());
tp = reset;
EXPECT_TRUE(parse("%I", "5", tz, &tp));
EXPECT_EQ(5, convert(tp, tz).hour());
// %j is parsed but ignored.
EXPECT_TRUE(parse("%j", "32", tz, &tp));
tp = reset;
EXPECT_TRUE(parse("%m", "11", tz, &tp));
EXPECT_EQ(11, convert(tp, tz).month());
tp = reset;
EXPECT_TRUE(parse("%M", "33", tz, &tp));
EXPECT_EQ(33, convert(tp, tz).minute());
tp = reset;
EXPECT_TRUE(parse("%S", "55", tz, &tp));
EXPECT_EQ(55, convert(tp, tz).second());
// %U is parsed but ignored.
EXPECT_TRUE(parse("%U", "15", tz, &tp));
// %w is parsed but ignored.
EXPECT_TRUE(parse("%w", "2", tz, &tp));
// %W is parsed but ignored.
EXPECT_TRUE(parse("%W", "22", tz, &tp));
tp = reset;
EXPECT_TRUE(parse("%y", "04", tz, &tp));
EXPECT_EQ(2004, convert(tp, tz).year());
tp = reset;
EXPECT_TRUE(parse("%Y", "2004", tz, &tp));
EXPECT_EQ(2004, convert(tp, tz).year());
EXPECT_TRUE(parse("%%", "%", tz, &tp));
#if defined(__linux__)
// SU/C99/TZ extensions
// Because we handle each (non-internal) specifier in a separate call
// to strptime(), there is no way to group %C and %y together. So we
// just skip the %C/%y case.
#if 0
tp = reset;
EXPECT_TRUE(parse("%C %y", "20 04", tz, &tp));
EXPECT_EQ(2004, convert(tp, tz).year());
#endif
tp = reset;
EXPECT_TRUE(parse("%D", "02/03/04", tz, &tp));
EXPECT_EQ(2, convert(tp, tz).month());
EXPECT_EQ(3, convert(tp, tz).day());
EXPECT_EQ(2004, convert(tp, tz).year());
EXPECT_TRUE(parse("%n", "\n", tz, &tp));
tp = reset;
EXPECT_TRUE(parse("%R", "03:44", tz, &tp));
EXPECT_EQ(3, convert(tp, tz).hour());
EXPECT_EQ(44, convert(tp, tz).minute());
EXPECT_TRUE(parse("%t", "\t\v\f\n\r ", tz, &tp));
tp = reset;
EXPECT_TRUE(parse("%T", "03:44:55", tz, &tp));
EXPECT_EQ(3, convert(tp, tz).hour());
EXPECT_EQ(44, convert(tp, tz).minute());
EXPECT_EQ(55, convert(tp, tz).second());
tp = reset;
EXPECT_TRUE(parse("%s", "1234567890", tz, &tp));
EXPECT_EQ(system_clock::from_time_t(1234567890), tp);
// %s conversion, like %z/%Ez, pays no heed to the optional zone.
time_zone lax;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax));
tp = reset;
EXPECT_TRUE(parse("%s", "1234567890", lax, &tp));
EXPECT_EQ(system_clock::from_time_t(1234567890), tp);
// This is most important when the time has the same YMDhms
// breakdown in the zone as some other time. For example, ...
// 1414917000 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PDT)
// 1414920600 in US/Pacific -> Sun Nov 2 01:30:00 2014 (PST)
tp = reset;
EXPECT_TRUE(parse("%s", "1414917000", lax, &tp));
EXPECT_EQ(system_clock::from_time_t(1414917000), tp);
tp = reset;
EXPECT_TRUE(parse("%s", "1414920600", lax, &tp));
EXPECT_EQ(system_clock::from_time_t(1414920600), tp);
#endif
}
TEST(Parse, LocaleSpecific) {
time_zone tz = utc_time_zone();
auto tp = system_clock::from_time_t(0);
const auto reset = convert(civil_second(1977, 6, 28, 9, 8, 7), tz);
// %a is parsed but ignored.
EXPECT_TRUE(parse("%a", "Mon", tz, &tp));
// %A is parsed but ignored.
EXPECT_TRUE(parse("%A", "Monday", tz, &tp));
tp = reset;
EXPECT_TRUE(parse("%b", "Feb", tz, &tp));
EXPECT_EQ(2, convert(tp, tz).month());
tp = reset;
EXPECT_TRUE(parse("%B", "February", tz, &tp));
EXPECT_EQ(2, convert(tp, tz).month());
// %p is parsed but ignored if it's alone. But it's used with %I.
EXPECT_TRUE(parse("%p", "AM", tz, &tp));
tp = reset;
EXPECT_TRUE(parse("%I %p", "5 PM", tz, &tp));
EXPECT_EQ(17, convert(tp, tz).hour());
tp = reset;
EXPECT_TRUE(parse("%x", "02/03/04", tz, &tp));
if (convert(tp, tz).month() == 2) {
EXPECT_EQ(3, convert(tp, tz).day());
} else {
EXPECT_EQ(2, convert(tp, tz).day());
EXPECT_EQ(3, convert(tp, tz).month());
}
EXPECT_EQ(2004, convert(tp, tz).year());
tp = reset;
EXPECT_TRUE(parse("%X", "15:44:55", tz, &tp));
EXPECT_EQ(15, convert(tp, tz).hour());
EXPECT_EQ(44, convert(tp, tz).minute());
EXPECT_EQ(55, convert(tp, tz).second());
#if defined(__linux__)
// SU/C99/TZ extensions
tp = reset;
EXPECT_TRUE(parse("%h", "Feb", tz, &tp));
EXPECT_EQ(2, convert(tp, tz).month()); // Equivalent to %b
tp = reset;
EXPECT_TRUE(parse("%l %p", "5 PM", tz, &tp));
EXPECT_EQ(17, convert(tp, tz).hour());
tp = reset;
EXPECT_TRUE(parse("%r", "03:44:55 PM", tz, &tp));
EXPECT_EQ(15, convert(tp, tz).hour());
EXPECT_EQ(44, convert(tp, tz).minute());
EXPECT_EQ(55, convert(tp, tz).second());
tp = reset;
EXPECT_TRUE(parse("%Ec", "Tue Nov 19 05:06:07 2013", tz, &tp));
EXPECT_EQ(convert(civil_second(2013, 11, 19, 5, 6, 7), tz), tp);
// Modified conversion specifiers %E_
tp = reset;
EXPECT_TRUE(parse("%Ex", "02/03/04", tz, &tp));
EXPECT_EQ(2, convert(tp, tz).month());
EXPECT_EQ(3, convert(tp, tz).day());
EXPECT_EQ(2004, convert(tp, tz).year());
tp = reset;
EXPECT_TRUE(parse("%EX", "15:44:55", tz, &tp));
EXPECT_EQ(15, convert(tp, tz).hour());
EXPECT_EQ(44, convert(tp, tz).minute());
EXPECT_EQ(55, convert(tp, tz).second());
// %Ey, the year offset from %EC, doesn't really make sense alone as there
// is no way to represent it in tm_year (%EC is not simply the century).
// Yet, because we handle each (non-internal) specifier in a separate call
// to strptime(), there is no way to group %EC and %Ey either. So we just
// skip the %EC and %Ey cases.
tp = reset;
EXPECT_TRUE(parse("%EY", "2004", tz, &tp));
EXPECT_EQ(2004, convert(tp, tz).year());
// Modified conversion specifiers %O_
tp = reset;
EXPECT_TRUE(parse("%Od", "15", tz, &tp));
EXPECT_EQ(15, convert(tp, tz).day());
tp = reset;
EXPECT_TRUE(parse("%Oe", "15", tz, &tp));
EXPECT_EQ(15, convert(tp, tz).day()); // Equivalent to %d
tp = reset;
EXPECT_TRUE(parse("%OH", "17", tz, &tp));
EXPECT_EQ(17, convert(tp, tz).hour());
tp = reset;
EXPECT_TRUE(parse("%OI", "5", tz, &tp));
EXPECT_EQ(5, convert(tp, tz).hour());
tp = reset;
EXPECT_TRUE(parse("%Om", "11", tz, &tp));
EXPECT_EQ(11, convert(tp, tz).month());
tp = reset;
EXPECT_TRUE(parse("%OM", "33", tz, &tp));
EXPECT_EQ(33, convert(tp, tz).minute());
tp = reset;
EXPECT_TRUE(parse("%OS", "55", tz, &tp));
EXPECT_EQ(55, convert(tp, tz).second());
// %OU is parsed but ignored.
EXPECT_TRUE(parse("%OU", "15", tz, &tp));
// %Ow is parsed but ignored.
EXPECT_TRUE(parse("%Ow", "2", tz, &tp));
// %OW is parsed but ignored.
EXPECT_TRUE(parse("%OW", "22", tz, &tp));
tp = reset;
EXPECT_TRUE(parse("%Oy", "04", tz, &tp));
EXPECT_EQ(2004, convert(tp, tz).year());
#endif
}
TEST(Parse, ExtendedSeconds) {
const time_zone tz = utc_time_zone();
const time_point<nanoseconds> unix_epoch = system_clock::from_time_t(0);
// All %E<prec>S cases are treated the same as %E*S on input.
auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15"};
for (const std::string& prec : precisions) {
const std::string fmt = "%E" + prec + "S";
SCOPED_TRACE(fmt);
time_point<nanoseconds> tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "5", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.0", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.00", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.6", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.60", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.600", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.67", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(670), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.670", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(670), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "05.678", tz, &tp));
EXPECT_EQ(unix_epoch + seconds(5) + milliseconds(678), tp);
}
// Here is a "%E*S" case we got wrong for a while. The fractional
// part of the first instant is less than 2^31 and was correctly
// parsed, while the second (and any subsecond field >=2^31) failed.
time_point<nanoseconds> tp = unix_epoch;
EXPECT_TRUE(parse("%E*S", "0.2147483647", tz, &tp));
EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
tp = unix_epoch;
EXPECT_TRUE(parse("%E*S", "0.2147483648", tz, &tp));
EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
// We should also be able to specify long strings of digits far
// beyond the current resolution and have them convert the same way.
tp = unix_epoch;
EXPECT_TRUE(parse(
"%E*S", "0.214748364801234567890123456789012345678901234567890123456789",
tz, &tp));
EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
}
TEST(Parse, ExtendedSecondsScan) {
const time_zone tz = utc_time_zone();
time_point<nanoseconds> tp;
for (int ms = 0; ms < 1000; ms += 111) {
for (int us = 0; us < 1000; us += 27) {
const int micros = ms * 1000 + us;
for (int ns = 0; ns < 1000; ns += 9) {
const auto expected =
system_clock::from_time_t(0) + nanoseconds(micros * 1000 + ns);
std::ostringstream oss;
oss << "0." << std::setfill('0') << std::setw(3);
oss << ms << std::setw(3) << us << std::setw(3) << ns;
const std::string input = oss.str();
EXPECT_TRUE(parse("%E*S", input, tz, &tp));
EXPECT_EQ(expected, tp) << input;
}
}
}
}
TEST(Parse, ExtendedSubeconds) {
const time_zone tz = utc_time_zone();
const time_point<nanoseconds> unix_epoch = system_clock::from_time_t(0);
// All %E<prec>f cases are treated the same as %E*f on input.
auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15"};
for (const std::string& prec : precisions) {
const std::string fmt = "%E" + prec + "f";
SCOPED_TRACE(fmt);
time_point<nanoseconds> tp = unix_epoch - seconds(1);
EXPECT_TRUE(parse(fmt, "", tz, &tp));
EXPECT_EQ(unix_epoch, tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "6", tz, &tp));
EXPECT_EQ(unix_epoch + milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "60", tz, &tp));
EXPECT_EQ(unix_epoch + milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "600", tz, &tp));
EXPECT_EQ(unix_epoch + milliseconds(600), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "67", tz, &tp));
EXPECT_EQ(unix_epoch + milliseconds(670), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "670", tz, &tp));
EXPECT_EQ(unix_epoch + milliseconds(670), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "678", tz, &tp));
EXPECT_EQ(unix_epoch + milliseconds(678), tp);
tp = unix_epoch;
EXPECT_TRUE(parse(fmt, "6789", tz, &tp));
EXPECT_EQ(unix_epoch + milliseconds(678) + microseconds(900), tp);
}
// Here is a "%E*f" case we got wrong for a while. The fractional
// part of the first instant is less than 2^31 and was correctly
// parsed, while the second (and any subsecond field >=2^31) failed.
time_point<nanoseconds> tp = unix_epoch;
EXPECT_TRUE(parse("%E*f", "2147483647", tz, &tp));
EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
tp = unix_epoch;
EXPECT_TRUE(parse("%E*f", "2147483648", tz, &tp));
EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
// We should also be able to specify long strings of digits far
// beyond the current resolution and have them convert the same way.
tp = unix_epoch;
EXPECT_TRUE(parse(
"%E*f", "214748364801234567890123456789012345678901234567890123456789",
tz, &tp));
EXPECT_EQ(unix_epoch + nanoseconds(214748364), tp);
}
TEST(Parse, ExtendedSubecondsScan) {
time_point<nanoseconds> tp;
const time_zone tz = utc_time_zone();
for (int ms = 0; ms < 1000; ms += 111) {
for (int us = 0; us < 1000; us += 27) {
const int micros = ms * 1000 + us;
for (int ns = 0; ns < 1000; ns += 9) {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(3) << ms;
oss << std::setw(3) << us << std::setw(3) << ns;
const std::string nanos = oss.str();
const auto expected =
system_clock::from_time_t(0) + nanoseconds(micros * 1000 + ns);
for (int ps = 0; ps < 1000; ps += 250) {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(3) << ps;
const std::string input = nanos + oss.str() + "999";
EXPECT_TRUE(parse("%E*f", input, tz, &tp));
EXPECT_EQ(expected + nanoseconds(ps) / 1000, tp) << input;
}
}
}
}
}
TEST(Parse, ExtendedOffset) {
const time_zone utc = utc_time_zone();
time_point<sys_seconds> tp;
// %z against +-HHMM.
EXPECT_TRUE(parse("%z", "+0000", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%z", "-1234", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp);
EXPECT_TRUE(parse("%z", "+1234", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp);
EXPECT_FALSE(parse("%z", "-123", utc, &tp));
// %z against +-HH.
EXPECT_TRUE(parse("%z", "+00", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%z", "-12", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp);
EXPECT_TRUE(parse("%z", "+12", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp);
EXPECT_FALSE(parse("%z", "-1", utc, &tp));
// %Ez against +-HH:MM.
EXPECT_TRUE(parse("%Ez", "+00:00", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%Ez", "-12:34", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp);
EXPECT_TRUE(parse("%Ez", "+12:34", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp);
EXPECT_FALSE(parse("%Ez", "-12:3", utc, &tp));
// %Ez against +-HHMM.
EXPECT_TRUE(parse("%Ez", "+0000", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%Ez", "-1234", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp);
EXPECT_TRUE(parse("%Ez", "+1234", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp);
EXPECT_FALSE(parse("%Ez", "-123", utc, &tp));
// %Ez against +-HH.
EXPECT_TRUE(parse("%Ez", "+00", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%Ez", "-12", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp);
EXPECT_TRUE(parse("%Ez", "+12", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp);
EXPECT_FALSE(parse("%Ez", "-1", utc, &tp));
}
TEST(Parse, ExtendedSecondOffset) {
const time_zone utc = utc_time_zone();
time_point<sys_seconds> tp;
// %Ez against +-HH:MM:SS.
EXPECT_TRUE(parse("%Ez", "+00:00:00", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%Ez", "-12:34:56", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp);
EXPECT_TRUE(parse("%Ez", "+12:34:56", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp);
EXPECT_FALSE(parse("%Ez", "-12:34:5", utc, &tp));
// %Ez against +-HHMMSS.
EXPECT_TRUE(parse("%Ez", "+000000", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%Ez", "-123456", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp);
EXPECT_TRUE(parse("%Ez", "+123456", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp);
EXPECT_FALSE(parse("%Ez", "-12345", utc, &tp));
// %E*z against +-HH:MM:SS.
EXPECT_TRUE(parse("%E*z", "+00:00:00", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%E*z", "-12:34:56", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp);
EXPECT_TRUE(parse("%E*z", "+12:34:56", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp);
EXPECT_FALSE(parse("%E*z", "-12:34:5", utc, &tp));
// %E*z against +-HHMMSS.
EXPECT_TRUE(parse("%E*z", "+000000", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%E*z", "-123456", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp);
EXPECT_TRUE(parse("%E*z", "+123456", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp);
EXPECT_FALSE(parse("%E*z", "-12345", utc, &tp));
// %E*z against +-HH:MM.
EXPECT_TRUE(parse("%E*z", "+00:00", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%E*z", "-12:34", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp);
EXPECT_TRUE(parse("%E*z", "+12:34", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp);
EXPECT_FALSE(parse("%E*z", "-12:3", utc, &tp));
// %E*z against +-HHMM.
EXPECT_TRUE(parse("%E*z", "+0000", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%E*z", "-1234", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp);
EXPECT_TRUE(parse("%E*z", "+1234", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp);
EXPECT_FALSE(parse("%E*z", "-123", utc, &tp));
// %E*z against +-HH.
EXPECT_TRUE(parse("%E*z", "+00", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse("%E*z", "-12", utc, &tp));
EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp);
EXPECT_TRUE(parse("%E*z", "+12", utc, &tp));
EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp);
EXPECT_FALSE(parse("%E*z", "-1", utc, &tp));
}
TEST(Parse, ExtendedYears) {
const time_zone utc = utc_time_zone();
const char e4y_fmt[] = "%E4Y%m%d"; // no separators
time_point<sys_seconds> tp;
// %E4Y consumes exactly four chars, including any sign.
EXPECT_TRUE(parse(e4y_fmt, "-9991127", utc, &tp));
EXPECT_EQ(convert(civil_second(-999, 11, 27, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse(e4y_fmt, "-0991127", utc, &tp));
EXPECT_EQ(convert(civil_second(-99, 11, 27, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse(e4y_fmt, "-0091127", utc, &tp));
EXPECT_EQ(convert(civil_second(-9, 11, 27, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse(e4y_fmt, "-0011127", utc, &tp));
EXPECT_EQ(convert(civil_second(-1, 11, 27, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse(e4y_fmt, "00001127", utc, &tp));
EXPECT_EQ(convert(civil_second(0, 11, 27, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse(e4y_fmt, "00011127", utc, &tp));
EXPECT_EQ(convert(civil_second(1, 11, 27, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse(e4y_fmt, "00091127", utc, &tp));
EXPECT_EQ(convert(civil_second(9, 11, 27, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse(e4y_fmt, "00991127", utc, &tp));
EXPECT_EQ(convert(civil_second(99, 11, 27, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse(e4y_fmt, "09991127", utc, &tp));
EXPECT_EQ(convert(civil_second(999, 11, 27, 0, 0, 0), utc), tp);
EXPECT_TRUE(parse(e4y_fmt, "99991127", utc, &tp));
EXPECT_EQ(convert(civil_second(9999, 11, 27, 0, 0, 0), utc), tp);
// When the year is outside [-999:9999], the parse fails.
EXPECT_FALSE(parse(e4y_fmt, "-10001127", utc, &tp));
EXPECT_FALSE(parse(e4y_fmt, "100001127", utc, &tp));
}
TEST(Parse, RFC3339Format) {
const time_zone tz = utc_time_zone();
time_point<nanoseconds> tp;
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp));
ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC");
// Check that %Ez also accepts "Z" as a synonym for "+00:00".
time_point<nanoseconds> tp2;
EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp2));
EXPECT_EQ(tp, tp2);
}
TEST(Parse, MaxRange) {
const time_zone utc = utc_time_zone();
time_point<sys_seconds> tp;
// tests the upper limit using +00:00 offset
EXPECT_TRUE(
parse(RFC3339_sec, "292277026596-12-04T15:30:07+00:00", utc, &tp));
EXPECT_EQ(tp, time_point<sys_seconds>::max());
EXPECT_FALSE(
parse(RFC3339_sec, "292277026596-12-04T15:30:08+00:00", utc, &tp));
// tests the upper limit using -01:00 offset
EXPECT_TRUE(
parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp));
EXPECT_EQ(tp, time_point<sys_seconds>::max());
EXPECT_FALSE(
parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp));
// tests the lower limit using +00:00 offset
EXPECT_TRUE(
parse(RFC3339_sec, "-292277022657-01-27T08:29:52+00:00", utc, &tp));
EXPECT_EQ(tp, time_point<sys_seconds>::min());
EXPECT_FALSE(
parse(RFC3339_sec, "-292277022657-01-27T08:29:51+00:00", utc, &tp));
// tests the lower limit using +01:00 offset
EXPECT_TRUE(
parse(RFC3339_sec, "-292277022657-01-27T09:29:52+01:00", utc, &tp));
EXPECT_EQ(tp, time_point<sys_seconds>::min());
EXPECT_FALSE(
parse(RFC3339_sec, "-292277022657-01-27T08:29:51+01:00", utc, &tp));
// tests max/min civil-second overflow
EXPECT_FALSE(parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01",
utc, &tp));
EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01",
utc, &tp));
// TODO: Add tests that parsing times with fractional seconds overflow
// appropriately. This can't be done until cctz::parse() properly detects
// overflow when combining the chrono seconds and femto.
}
//
// Roundtrip test for format()/parse().
//
TEST(FormatParse, RoundTrip) {
time_zone lax;
EXPECT_TRUE(load_time_zone("America/Los_Angeles", &lax));
const auto in = convert(civil_second(1977, 6, 28, 9, 8, 7), lax);
const auto subseconds = nanoseconds(654321);
// RFC3339, which renders subseconds.
{
time_point<nanoseconds> out;
const std::string s = format(RFC3339_full, in + subseconds, lax);
EXPECT_TRUE(parse(RFC3339_full, s, lax, &out)) << s;
EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez
}
// RFC1123, which only does whole seconds.
{
time_point<nanoseconds> out;
const std::string s = format(RFC1123_full, in, lax);
EXPECT_TRUE(parse(RFC1123_full, s, lax, &out)) << s;
EXPECT_EQ(in, out); // RFC1123_full includes %z
}
#if defined(_WIN32) || defined(_WIN64)
// Initial investigations indicate the %c does not roundtrip on Windows.
// TODO: Figure out what is going on here (perhaps a locale problem).
#else
// Even though we don't know what %c will produce, it should roundtrip,
// but only in the 0-offset timezone.
{
time_point<nanoseconds> out;
time_zone utc = utc_time_zone();
const std::string s = format("%c", in, utc);
EXPECT_TRUE(parse("%c", s, utc, &out)) << s;
EXPECT_EQ(in, out);
}
#endif
}
TEST(FormatParse, RoundTripDistantFuture) {
const time_zone utc = utc_time_zone();
const time_point<sys_seconds> in = time_point<sys_seconds>::max();
const std::string s = format(RFC3339_full, in, utc);
time_point<sys_seconds> out;
EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
EXPECT_EQ(in, out);
}
TEST(FormatParse, RoundTripDistantPast) {
const time_zone utc = utc_time_zone();
const time_point<sys_seconds> in = time_point<sys_seconds>::min();
const std::string s = format(RFC3339_full, in, utc);
time_point<sys_seconds> out;
EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
EXPECT_EQ(in, out);
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "time_zone_if.h"
#include "time_zone_info.h"
#include "time_zone_libc.h"
namespace absl {
namespace time_internal {
namespace cctz {
std::unique_ptr<TimeZoneIf> TimeZoneIf::Load(const std::string& name) {
// Support "libc:localtime" and "libc:*" to access the legacy
// localtime and UTC support respectively from the C library.
if (name.compare(0, 5, "libc:") == 0) {
return std::unique_ptr<TimeZoneIf>(new TimeZoneLibC(name.substr(5)));
}
// Otherwise use the "zoneinfo" implementation by default.
std::unique_ptr<TimeZoneInfo> tz(new TimeZoneInfo);
if (!tz->Load(name)) tz.reset();
return std::unique_ptr<TimeZoneIf>(tz.release());
}
// Defined out-of-line to avoid emitting a weak vtable in all TUs.
TimeZoneIf::~TimeZoneIf() {}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
#include <chrono>
#include <cstdint>
#include <memory>
#include <string>
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
namespace time_internal {
namespace cctz {
// A simple interface used to hide time-zone complexities from time_zone::Impl.
// Subclasses implement the functions for civil-time conversions in the zone.
class TimeZoneIf {
public:
// A factory function for TimeZoneIf implementations.
static std::unique_ptr<TimeZoneIf> Load(const std::string& name);
virtual ~TimeZoneIf();
virtual time_zone::absolute_lookup BreakTime(
const time_point<sys_seconds>& tp) const = 0;
virtual time_zone::civil_lookup MakeTime(
const civil_second& cs) const = 0;
virtual std::string Description() const = 0;
virtual bool NextTransition(time_point<sys_seconds>* tp) const = 0;
virtual bool PrevTransition(time_point<sys_seconds>* tp) const = 0;
protected:
TimeZoneIf() {}
};
// Convert between time_point<sys_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.
inline std::int_fast64_t ToUnixSeconds(const time_point<sys_seconds>& tp) {
return (tp - std::chrono::time_point_cast<sys_seconds>(
std::chrono::system_clock::from_time_t(0)))
.count();
}
inline time_point<sys_seconds> FromUnixSeconds(std::int_fast64_t t) {
return std::chrono::time_point_cast<sys_seconds>(
std::chrono::system_clock::from_time_t(0)) +
sys_seconds(t);
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "time_zone_impl.h"
#include <mutex>
#include <string>
#include <unordered_map>
#include <utility>
#include "time_zone_fixed.h"
namespace absl {
namespace time_internal {
namespace cctz {
namespace {
// time_zone::Impls are linked into a map to support fast lookup by name.
using TimeZoneImplByName =
std::unordered_map<std::string, const time_zone::Impl*>;
TimeZoneImplByName* time_zone_map = nullptr;
// Mutual exclusion for time_zone_map.
std::mutex time_zone_mutex;
} // namespace
time_zone time_zone::Impl::UTC() {
return time_zone(UTCImpl());
}
bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
const time_zone::Impl* const utc_impl = UTCImpl();
// First check for UTC (which is never a key in time_zone_map).
auto offset = sys_seconds::zero();
if (FixedOffsetFromName(name, &offset) && offset == sys_seconds::zero()) {
*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.
{
std::lock_guard<std::mutex> lock(time_zone_mutex);
if (time_zone_map != nullptr) {
TimeZoneImplByName::const_iterator itr = time_zone_map->find(name);
if (itr != time_zone_map->end()) {
*tz = time_zone(itr->second);
return itr->second != utc_impl;
}
}
}
// Now check again, under an exclusive lock.
std::lock_guard<std::mutex> lock(time_zone_mutex);
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
}
}
*tz = time_zone(impl);
return impl != utc_impl;
}
const time_zone::Impl& time_zone::Impl::get(const time_zone& tz) {
if (tz.impl_ == nullptr) {
// Dereferencing an implicit-UTC time_zone is expected to be
// rare, so we don't mind paying a small synchronization cost.
return *UTCImpl();
}
return *tz.impl_;
}
void time_zone::Impl::ClearTimeZoneMapTestOnly() {
std::lock_guard<std::mutex> lock(time_zone_mutex);
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.
time_zone_map->clear();
}
}
time_zone::Impl::Impl(const std::string& name) : name_(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;
}();
return utc_impl;
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
#include <memory>
#include <string>
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#include "time_zone_if.h"
#include "time_zone_info.h"
namespace absl {
namespace time_internal {
namespace cctz {
// time_zone::Impl is the internal object referenced by a cctz::time_zone.
class time_zone::Impl {
public:
// The UTC time zone. Also used for other time zones that fail to load.
static time_zone UTC();
// Load a named time zone. Returns false if the name is invalid, or if
// some other kind of error occurs. Note that loading "UTC" never fails.
static bool LoadTimeZone(const std::string& name, time_zone* tz);
// Dereferences the time_zone to obtain its Impl.
static const time_zone::Impl& get(const time_zone& tz);
// Clears the map of cached time zones. Primarily for use in benchmarks
// that gauge the performance of loading/parsing the time-zone data.
static void ClearTimeZoneMapTestOnly();
// The primary key is the time-zone ID (e.g., "America/New_York").
const std::string& name() const { return name_; }
// Breaks a time_point down to civil-time components in this time zone.
time_zone::absolute_lookup BreakTime(
const time_point<sys_seconds>& tp) const {
return zone_->BreakTime(tp);
}
// Converts the civil-time components in this time zone into a time_point.
// That is, the opposite of BreakTime(). The requested civil time may be
// ambiguous or illegal due to a change of UTC offset.
time_zone::civil_lookup MakeTime(const civil_second& cs) const {
return zone_->MakeTime(cs);
}
// Returns an implementation-specific description of this time zone.
std::string Description() const { return zone_->Description(); }
// Finds the time of the next/previous offset change in this time zone.
//
// By definition, NextTransition(&tp) returns false when tp has its
// maximum value, and PrevTransition(&tp) returns false when tp has its
// mimimum value. If the zone has no transitions, the result will also
// be false no matter what the argument.
//
// Otherwise, when tp has its mimimum value, NextTransition(&tp) returns
// true and sets tp to the first recorded transition. Chains of calls
// to NextTransition()/PrevTransition() will eventually return false,
// but it is unspecified exactly when NextTransition(&tp) jumps to false,
// or what time is set by PrevTransition(&tp) for a very distant tp.
bool NextTransition(time_point<sys_seconds>* tp) const {
return zone_->NextTransition(tp);
}
bool PrevTransition(time_point<sys_seconds>* tp) const {
return zone_->PrevTransition(tp);
}
private:
explicit Impl(const std::string& name);
static const Impl* UTCImpl();
const std::string name_;
std::unique_ptr<TimeZoneIf> zone_;
};
} // namespace cctz
} // namespace time_internal
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file implements the TimeZoneIf interface using the "zoneinfo"
// data provided by the IANA Time Zone Database (i.e., the only real game
// in town).
//
// TimeZoneInfo represents the history of UTC-offset changes within a time
// zone. Most changes are due to daylight-saving rules, but occasionally
// shifts are made to the time-zone's base offset. The database only attempts
// to be definitive for times since 1970, so be wary of local-time conversions
// before that. Also, rule and zone-boundary changes are made at the whim
// of governments, so the conversion of future times needs to be taken with
// a grain of salt.
//
// For more information see tzfile(5), http://www.iana.org/time-zones, or
// http://en.wikipedia.org/wiki/Zoneinfo.
//
// Note that we assume the proleptic Gregorian calendar and 60-second
// minutes throughout.
#include "time_zone_info.h"
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "time_zone_fixed.h"
#include "time_zone_posix.h"
namespace absl {
namespace time_internal {
namespace cctz {
namespace {
inline bool IsLeap(year_t year) {
return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
}
// The number of days in non-leap and leap years respectively.
const std::int_least32_t kDaysPerYear[2] = {365, 366};
// The day offsets of the beginning of each (1-based) month in non-leap and
// leap years respectively (e.g., 335 days before December in a leap year).
const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = {
{-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
{-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
};
// We reject leap-second encoded zoneinfo and so assume 60-second minutes.
const std::int_least32_t kSecsPerDay = 24 * 60 * 60;
// 400-year chunks always have 146097 days (20871 weeks).
const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay;
// Like kDaysPerYear[] but scaled up by a factor of kSecsPerDay.
const std::int_least32_t kSecsPerYear[2] = {
365 * kSecsPerDay,
366 * kSecsPerDay,
};
// 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;
}
// Multi-byte, numeric values are encoded using a MSB first,
// twos-complement representation. These helpers decode, from
// the given address, 4-byte and 8-byte values respectively.
// Note: If int_fastXX_t == intXX_t and this machine is not
// twos complement, then there will be at least one input value
// we cannot represent.
std::int_fast32_t Decode32(const char* cp) {
std::uint_fast32_t v = 0;
for (int i = 0; i != (32 / 8); ++i) v = (v << 8) | Decode8(cp++);
const std::int_fast32_t s32max = 0x7fffffff;
const auto s32maxU = static_cast<std::uint_fast32_t>(s32max);
if (v <= s32maxU) return static_cast<std::int_fast32_t>(v);
return static_cast<std::int_fast32_t>(v - s32maxU - 1) - s32max - 1;
}
std::int_fast64_t Decode64(const char* cp) {
std::uint_fast64_t v = 0;
for (int i = 0; i != (64 / 8); ++i) v = (v << 8) | Decode8(cp++);
const std::int_fast64_t s64max = 0x7fffffffffffffff;
const auto s64maxU = static_cast<std::uint_fast64_t>(s64max);
if (v <= s64maxU) return static_cast<std::int_fast64_t>(v);
return static_cast<std::int_fast64_t>(v - s64maxU - 1) - s64max - 1;
}
// Generate a year-relative offset for a PosixTransition.
std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday,
const PosixTransition& pt) {
std::int_fast64_t days = 0;
switch (pt.date.fmt) {
case PosixTransition::J: {
days = pt.date.j.day;
if (!leap_year || days < kMonthOffsets[1][3]) days -= 1;
break;
}
case PosixTransition::N: {
days = pt.date.n.day;
break;
}
case PosixTransition::M: {
const bool last_week = (pt.date.m.week == 5);
days = kMonthOffsets[leap_year][pt.date.m.month + last_week];
const std::int_fast64_t weekday = (jan1_weekday + days) % 7;
if (last_week) {
days -= (weekday + 7 - 1 - pt.date.m.weekday) % 7 + 1;
} else {
days += (pt.date.m.weekday + 7 - weekday) % 7;
days += (pt.date.m.week - 1) * 7;
}
break;
}
}
return (days * kSecsPerDay) + pt.time.offset;
}
inline time_zone::civil_lookup MakeUnique(const time_point<sys_seconds>& tp) {
time_zone::civil_lookup cl;
cl.kind = time_zone::civil_lookup::UNIQUE;
cl.pre = cl.trans = cl.post = tp;
return cl;
}
inline time_zone::civil_lookup MakeUnique(std::int_fast64_t unix_time) {
return MakeUnique(FromUnixSeconds(unix_time));
}
inline time_zone::civil_lookup MakeSkipped(const Transition& tr,
const civil_second& cs) {
time_zone::civil_lookup cl;
cl.kind = time_zone::civil_lookup::SKIPPED;
cl.pre = FromUnixSeconds(tr.unix_time - 1 + (cs - tr.prev_civil_sec));
cl.trans = FromUnixSeconds(tr.unix_time);
cl.post = FromUnixSeconds(tr.unix_time - (tr.civil_sec - cs));
return cl;
}
inline time_zone::civil_lookup MakeRepeated(const Transition& tr,
const civil_second& cs) {
time_zone::civil_lookup cl;
cl.kind = time_zone::civil_lookup::REPEATED;
cl.pre = FromUnixSeconds(tr.unix_time - 1 - (tr.prev_civil_sec - cs));
cl.trans = FromUnixSeconds(tr.unix_time);
cl.post = FromUnixSeconds(tr.unix_time + (cs - tr.civil_sec));
return cl;
}
inline civil_second YearShift(const civil_second& cs, year_t shift) {
return civil_second(cs.year() + shift, cs.month(), cs.day(),
cs.hour(), cs.minute(), cs.second());
}
} // namespace
// What (no leap-seconds) UTC+seconds zoneinfo would look like.
bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) {
transition_types_.resize(1);
TransitionType& tt(transition_types_.back());
tt.utc_offset = static_cast<std::int_least32_t>(offset.count());
tt.is_dst = false;
tt.abbr_index = 0;
// We temporarily add some redundant, contemporary (2012 through 2021)
// transitions for performance reasons. See TimeZoneInfo::LocalTime().
// TODO: Fix the performance issue and remove the extra transitions.
transitions_.clear();
transitions_.reserve(12);
for (const std::int_fast64_t unix_time : {
-(1LL << 59), // BIG_BANG
1325376000LL, // 2012-01-01T00:00:00+00:00
1356998400LL, // 2013-01-01T00:00:00+00:00
1388534400LL, // 2014-01-01T00:00:00+00:00
1420070400LL, // 2015-01-01T00:00:00+00:00
1451606400LL, // 2016-01-01T00:00:00+00:00
1483228800LL, // 2017-01-01T00:00:00+00:00
1514764800LL, // 2018-01-01T00:00:00+00:00
1546300800LL, // 2019-01-01T00:00:00+00:00
1577836800LL, // 2020-01-01T00:00:00+00:00
1609459200LL, // 2021-01-01T00:00:00+00:00
2147483647LL, // 2^31 - 1
}) {
Transition& tr(*transitions_.emplace(transitions_.end()));
tr.unix_time = unix_time;
tr.type_index = 0;
tr.civil_sec = LocalTime(tr.unix_time, tt).cs;
tr.prev_civil_sec = tr.civil_sec - 1;
}
default_transition_type_ = 0;
abbreviations_ = FixedOffsetToAbbr(offset);
abbreviations_.append(1, '\0'); // add NUL
future_spec_.clear(); // never needed for a fixed-offset zone
extended_ = false;
tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs;
tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs;
transitions_.shrink_to_fit();
return true;
}
// Builds the in-memory header using the raw bytes from the file.
bool TimeZoneInfo::Header::Build(const tzhead& tzh) {
std::int_fast32_t v;
if ((v = Decode32(tzh.tzh_timecnt)) < 0) return false;
timecnt = static_cast<std::size_t>(v);
if ((v = Decode32(tzh.tzh_typecnt)) < 0) return false;
typecnt = static_cast<std::size_t>(v);
if ((v = Decode32(tzh.tzh_charcnt)) < 0) return false;
charcnt = static_cast<std::size_t>(v);
if ((v = Decode32(tzh.tzh_leapcnt)) < 0) return false;
leapcnt = static_cast<std::size_t>(v);
if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false;
ttisstdcnt = static_cast<std::size_t>(v);
if ((v = Decode32(tzh.tzh_ttisgmtcnt)) < 0) return false;
ttisgmtcnt = static_cast<std::size_t>(v);
return true;
}
// How many bytes of data are associated with this header. The result
// depends upon whether this is a section with 4-byte or 8-byte times.
std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const {
std::size_t len = 0;
len += (time_len + 1) * timecnt; // unix_time + type_index
len += (4 + 1 + 1) * typecnt; // utc_offset + is_dst + abbr_index
len += 1 * charcnt; // abbreviations
len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC
len += 1 * ttisstdcnt; // UTC/local indicators
len += 1 * ttisgmtcnt; // 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).
bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index,
std::uint_fast8_t tt2_index) const {
if (tt1_index == tt2_index) return true;
const TransitionType& tt1(transition_types_[tt1_index]);
const TransitionType& tt2(transition_types_[tt2_index]);
if (tt1.is_dst != tt2.is_dst) return false;
if (tt1.utc_offset != tt2.utc_offset) return false;
if (tt1.abbr_index != tt2.abbr_index) return false;
return true;
}
// Use the POSIX-TZ-environment-variable-style std::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) {
extended_ = false;
bool extending = !future_spec_.empty();
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
}
// 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);
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();
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
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;
}
assert(tr == &transitions_[0] + transitions_.size());
}
bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
// Read and validate the header.
tzhead tzh;
if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh))
return false;
if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
return false;
Header hdr;
if (!hdr.Build(tzh))
return false;
std::size_t time_len = 4;
if (tzh.tzh_version[0] != '\0') {
// Skip the 4-byte data.
if (zip->Skip(hdr.DataLength(time_len)) != 0)
return false;
// Read and validate the header for the 8-byte data.
if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh))
return false;
if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
return false;
if (tzh.tzh_version[0] == '\0')
return false;
if (!hdr.Build(tzh))
return false;
time_len = 8;
}
if (hdr.typecnt == 0)
return false;
if (hdr.leapcnt != 0) {
// This code assumes 60-second minutes so we do not want
// the leap-second encoded zoneinfo. We could reverse the
// compensation, but the "right" encoding is rarely used
// so currently we simply reject such data.
return false;
}
if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt)
return false;
if (hdr.ttisgmtcnt != 0 && hdr.ttisgmtcnt != hdr.typecnt)
return false;
// Read the data into a local buffer.
std::size_t len = hdr.DataLength(time_len);
std::vector<char> tbuf(len);
if (zip->Read(tbuf.data(), len) != len)
return false;
const char* bp = tbuf.data();
// Decode and validate the transitions.
transitions_.reserve(hdr.timecnt + 2); // We might add a couple.
transitions_.resize(hdr.timecnt);
for (std::size_t i = 0; i != hdr.timecnt; ++i) {
transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp);
bp += time_len;
if (i != 0) {
// Check that the transitions are ordered by time (as zic guarantees).
if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i]))
return false; // out of order
}
}
bool seen_type_0 = false;
for (std::size_t i = 0; i != hdr.timecnt; ++i) {
transitions_[i].type_index = Decode8(bp++);
if (transitions_[i].type_index >= hdr.typecnt)
return false;
if (transitions_[i].type_index == 0)
seen_type_0 = true;
}
// Decode and validate the transition types.
transition_types_.resize(hdr.typecnt);
for (std::size_t i = 0; i != hdr.typecnt; ++i) {
transition_types_[i].utc_offset =
static_cast<std::int_least32_t>(Decode32(bp));
if (transition_types_[i].utc_offset >= kSecsPerDay ||
transition_types_[i].utc_offset <= -kSecsPerDay)
return false;
bp += 4;
transition_types_[i].is_dst = (Decode8(bp++) != 0);
transition_types_[i].abbr_index = Decode8(bp++);
if (transition_types_[i].abbr_index >= hdr.charcnt)
return false;
}
// Determine the before-first-transition type.
default_transition_type_ = 0;
if (seen_type_0 && hdr.timecnt != 0) {
std::uint_fast8_t index = 0;
if (transition_types_[0].is_dst) {
index = transitions_[0].type_index;
while (index != 0 && transition_types_[index].is_dst)
--index;
}
while (index != hdr.typecnt && transition_types_[index].is_dst)
++index;
if (index != hdr.typecnt)
default_transition_type_ = index;
}
// Copy all the abbreviations.
abbreviations_.assign(bp, hdr.charcnt);
bp += hdr.charcnt;
// Skip the unused portions. We've already dispensed with leap-second
// encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when
// interpreting a POSIX spec that does not include start/end rules, and
// that isn't the case here (see "zic -p").
bp += (8 + 4) * hdr.leapcnt; // leap-time + TAI-UTC
bp += 1 * hdr.ttisstdcnt; // UTC/local indicators
bp += 1 * hdr.ttisgmtcnt; // 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 {
unsigned char ch; // all non-EOF results are positive
return (zip->Read(&ch, 1) == 1) ? ch : EOF;
};
if (get_char(zip) != '\n')
return false;
for (int c = get_char(zip); c != '\n'; c = get_char(zip)) {
if (c == EOF)
return false;
future_spec_.push_back(static_cast<char>(c));
}
}
// We don't check for EOF so that we're forwards compatible.
// Trim redundant transitions. zic may have added these to work around
// differences between the glibc and reference implementations (see
// zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071).
// For us, they just get in the way when we do future_spec_ extension.
while (hdr.timecnt > 1) {
if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index,
transitions_[hdr.timecnt - 2].type_index)) {
break;
}
hdr.timecnt -= 1;
}
transitions_.resize(hdr.timecnt);
// Ensure that there is always a transition in the first half of the
// time line (the second half is handled 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.
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.type_index = default_transition_type_;
hdr.timecnt += 1;
}
// Extend the transitions using the future specification.
ExtendTransitions(name, hdr);
// Compute the local civil time for each transition and the preceding
// second. These will be used for reverse conversions in MakeTime().
const TransitionType* ttp = &transition_types_[default_transition_type_];
for (std::size_t i = 0; i != transitions_.size(); ++i) {
Transition& tr(transitions_[i]);
tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1;
ttp = &transition_types_[tr.type_index];
tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs;
if (i != 0) {
// Check that the transitions are ordered by civil time. Essentially
// this means that an offset change cannot cross another such change.
// No one does this in practice, and we depend on it in MakeTime().
if (!Transition::ByCivilTime()(transitions_[i - 1], tr))
return false; // out of order
}
}
// Compute the maximum/minimum civil times that can be converted to a
// time_point<sys_seconds> for each of the zone's transition types.
for (auto& tt : transition_types_) {
tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs;
tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs;
}
transitions_.shrink_to_fit();
return true;
}
namespace {
// fopen(3) adaptor.
inline FILE* FOpen(const char* path, const char* mode) {
#if defined(_MSC_VER)
FILE* fp;
if (fopen_s(&fp, path, mode) != 0) fp = nullptr;
return fp;
#else
return fopen(path, mode); // TODO: Enable the close-on-exec flag.
#endif
}
// A stdio(3)-backed implementation of ZoneInfoSource.
class FileZoneInfoSource : public ZoneInfoSource {
public:
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
std::size_t Read(void* ptr, std::size_t size) override {
size = std::min(size, len_);
std::size_t nread = fread(ptr, 1, size, fp_.get());
len_ -= nread;
return nread;
}
int Skip(std::size_t offset) override {
offset = std::min(offset, len_);
int rc = fseek(fp_.get(), static_cast<long>(offset), SEEK_CUR);
if (rc == 0) len_ -= offset;
return rc;
}
protected:
explicit FileZoneInfoSource(
FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max())
: fp_(fp, fclose), len_(len) {}
private:
std::unique_ptr<FILE, int(*)(FILE*)> 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));
// Map the time-zone name to a path name.
std::string path;
if (name.empty() || name[0] != '/') {
const char* tzdir = "/usr/share/zoneinfo";
char* tzdir_env = nullptr;
#if defined(_MSC_VER)
_dupenv_s(&tzdir_env, nullptr, "TZDIR");
#else
tzdir_env = std::getenv("TZDIR");
#endif
if (tzdir_env && *tzdir_env) tzdir = tzdir_env;
path += tzdir;
path += '/';
#if defined(_MSC_VER)
free(tzdir_env);
#endif
}
path += name;
// Open the zoneinfo file.
FILE* 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));
}
#if defined(__ANDROID__)
class AndroidZoneInfoSource : public FileZoneInfoSource {
public:
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
private:
explicit AndroidZoneInfoSource(FILE* fp, std::size_t len)
: FileZoneInfoSource(fp, len) {}
};
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));
// 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;
char hbuf[24]; // covers header.zonetab_offset too
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
if (strncmp(hbuf, "tzdata", 6) != 0) continue;
const std::int_fast32_t index_offset = Decode32(hbuf + 12);
const std::int_fast32_t data_offset = Decode32(hbuf + 16);
if (index_offset < 0 || data_offset < index_offset) continue;
if (fseek(fp.get(), static_cast<long>(index_offset), SEEK_SET) != 0)
continue;
char ebuf[52]; // covers entry.unused too
const std::size_t index_size =
static_cast<std::size_t>(data_offset - index_offset);
const std::size_t zonecnt = index_size / sizeof(ebuf);
if (zonecnt * sizeof(ebuf) != index_size) continue;
for (std::size_t i = 0; i != zonecnt; ++i) {
if (fread(ebuf, 1, sizeof(ebuf), fp.get()) != sizeof(ebuf)) break;
const std::int_fast32_t start = data_offset + Decode32(ebuf + 40);
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 (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)));
}
}
}
return nullptr;
}
#endif
} // namespace
bool TimeZoneInfo::Load(const std::string& name) {
// We can ensure that the loading of UTC or any other fixed-offset
// zone never fails because the simple, fixed-offset state can be
// internally generated. Note that this depends on our choice to not
// accept leap-second encoded ("right") zoneinfo.
auto offset = sys_seconds::zero();
if (FixedOffsetFromName(name, &offset)) {
return ResetToBuiltinUTC(offset);
}
// 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 defined(__ANDROID__)
if (auto zip = AndroidZoneInfoSource::Open(name)) return zip;
#endif
return nullptr;
});
return zip != nullptr && Load(name, zip.get());
}
// BreakTime() translation for a particular transition type.
time_zone::absolute_lookup TimeZoneInfo::LocalTime(
std::int_fast64_t unix_time, const TransitionType& tt) const {
// A civil time in "+offset" looks like (time+offset) in UTC.
// Note: We perform two additions in the civil_second domain to
// sidestep the chance of overflow in (unix_time + tt.utc_offset).
return {(civil_second() + unix_time) + tt.utc_offset,
tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]};
}
// BreakTime() translation for a particular transition.
time_zone::absolute_lookup TimeZoneInfo::LocalTime(
std::int_fast64_t unix_time, const Transition& tr) const {
const TransitionType& tt = transition_types_[tr.type_index];
// Note: (unix_time - tr.unix_time) will never overflow as we
// have ensured that there is always a "nearby" transition.
return {tr.civil_sec + (unix_time - tr.unix_time), // TODO: Optimize.
tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]};
}
// MakeTime() translation with a conversion-preserving +N * 400-year shift.
time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
year_t c4_shift) const {
assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_);
time_zone::civil_lookup cl = MakeTime(cs);
if (c4_shift > sys_seconds::max().count() / kSecsPer400Years) {
cl.pre = cl.trans = cl.post = time_point<sys_seconds>::max();
} else {
const auto offset = sys_seconds(c4_shift * kSecsPer400Years);
const auto limit = time_point<sys_seconds>::max() - offset;
for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) {
if (*tp > limit) {
*tp = time_point<sys_seconds>::max();
} else {
*tp += offset;
}
}
}
return cl;
}
time_zone::absolute_lookup TimeZoneInfo::BreakTime(
const time_point<sys_seconds>& tp) const {
std::int_fast64_t unix_time = ToUnixSeconds(tp);
const std::size_t timecnt = transitions_.size();
assert(timecnt != 0); // We always add a transition.
if (unix_time < transitions_[0].unix_time) {
return LocalTime(unix_time, transition_types_[default_transition_type_]);
}
if (unix_time >= transitions_[timecnt - 1].unix_time) {
// After the last transition. If we extended the transitions using
// future_spec_, shift back to a supported year using the 400-year
// cycle of calendaric equivalence and then compensate accordingly.
if (extended_) {
const std::int_fast64_t diff =
unix_time - transitions_[timecnt - 1].unix_time;
const year_t shift = diff / kSecsPer400Years + 1;
const auto d = sys_seconds(shift * kSecsPer400Years);
time_zone::absolute_lookup al = BreakTime(tp - d);
al.cs = YearShift(al.cs, shift * 400);
return al;
}
return LocalTime(unix_time, transitions_[timecnt - 1]);
}
const std::size_t hint = local_time_hint_.load(std::memory_order_relaxed);
if (0 < hint && hint < timecnt) {
if (transitions_[hint - 1].unix_time <= unix_time) {
if (unix_time < transitions_[hint].unix_time) {
return LocalTime(unix_time, transitions_[hint - 1]);
}
}
}
const Transition target = {unix_time, 0, civil_second(), civil_second()};
const Transition* begin = &transitions_[0];
const Transition* tr = std::upper_bound(begin, begin + timecnt, target,
Transition::ByUnixTime());
local_time_hint_.store(static_cast<std::size_t>(tr - begin),
std::memory_order_relaxed);
return LocalTime(unix_time, *--tr);
}
time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
const std::size_t timecnt = transitions_.size();
assert(timecnt != 0); // We always add a transition.
// Find the first transition after our target civil time.
const Transition* tr = nullptr;
const Transition* begin = &transitions_[0];
const Transition* end = begin + timecnt;
if (cs < begin->civil_sec) {
tr = begin;
} else if (cs >= transitions_[timecnt - 1].civil_sec) {
tr = end;
} else {
const std::size_t hint = time_local_hint_.load(std::memory_order_relaxed);
if (0 < hint && hint < timecnt) {
if (transitions_[hint - 1].civil_sec <= cs) {
if (cs < transitions_[hint].civil_sec) {
tr = begin + hint;
}
}
}
if (tr == nullptr) {
const Transition target = {0, 0, cs, civil_second()};
tr = std::upper_bound(begin, end, target, Transition::ByCivilTime());
time_local_hint_.store(static_cast<std::size_t>(tr - begin),
std::memory_order_relaxed);
}
}
if (tr == begin) {
if (tr->prev_civil_sec >= cs) {
// Before first transition, so use the default offset.
const TransitionType& tt(transition_types_[default_transition_type_]);
if (cs < tt.civil_min) return MakeUnique(time_point<sys_seconds>::min());
return MakeUnique(cs - (civil_second() + tt.utc_offset));
}
// tr->prev_civil_sec < cs < tr->civil_sec
return MakeSkipped(*tr, cs);
}
if (tr == end) {
if (cs > (--tr)->prev_civil_sec) {
// After the last transition. If we extended the transitions using
// future_spec_, shift back to a supported year using the 400-year
// cycle of calendaric equivalence and then compensate accordingly.
if (extended_ && cs.year() > last_year_) {
const year_t shift = (cs.year() - last_year_ - 1) / 400 + 1;
return TimeLocal(YearShift(cs, shift * -400), shift);
}
const TransitionType& tt(transition_types_[tr->type_index]);
if (cs > tt.civil_max) return MakeUnique(time_point<sys_seconds>::max());
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
}
// tr->civil_sec <= cs <= tr->prev_civil_sec
return MakeRepeated(*tr, cs);
}
if (tr->prev_civil_sec < cs) {
// tr->prev_civil_sec < cs < tr->civil_sec
return MakeSkipped(*tr, cs);
}
if (cs <= (--tr)->prev_civil_sec) {
// tr->civil_sec <= cs <= tr->prev_civil_sec
return MakeRepeated(*tr, cs);
}
// In between transitions.
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
}
std::string TimeZoneInfo::Description() const {
std::ostringstream oss;
// TODO: It would nice if the zoneinfo data included the zone name.
// TODO: It would nice if the zoneinfo data included the tzdb version.
oss << "#trans=" << transitions_.size();
oss << " #types=" << transition_types_.size();
oss << " spec='" << future_spec_ << "'";
return oss.str();
}
bool TimeZoneInfo::NextTransition(time_point<sys_seconds>* tp) const {
if (transitions_.empty()) return false;
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.
++begin;
}
std::int_fast64_t unix_time = ToUnixSeconds(*tp);
const Transition target = { unix_time };
const Transition* tr = std::upper_bound(begin, end, target,
Transition::ByUnixTime());
if (tr != begin) { // skip no-op transitions
for (; tr != end; ++tr) {
if (!EquivTransitions(tr[-1].type_index, tr[0].type_index)) break;
}
}
// When tr == end we return false, ignoring future_spec_.
if (tr == end) return false;
*tp = FromUnixSeconds(tr->unix_time);
return true;
}
bool TimeZoneInfo::PrevTransition(time_point<sys_seconds>* tp) const {
if (transitions_.empty()) return false;
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.
++begin;
}
std::int_fast64_t unix_time = ToUnixSeconds(*tp);
if (FromUnixSeconds(unix_time) != *tp) {
if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) {
if (end == begin) return false; // Ignore future_spec_.
*tp = FromUnixSeconds((--end)->unix_time);
return true;
}
unix_time += 1; // ceils
}
const Transition target = { unix_time };
const Transition* tr = std::lower_bound(begin, end, target,
Transition::ByUnixTime());
if (tr != begin) { // skip no-op transitions
for (; tr - 1 != begin; --tr) {
if (!EquivTransitions(tr[-2].type_index, tr[-1].type_index)) break;
}
}
// When tr == end we return the "last" transition, ignoring future_spec_.
if (tr == begin) return false;
*tp = FromUnixSeconds((--tr)->unix_time);
return true;
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
#include "time_zone_if.h"
#include "tzfile.h"
namespace absl {
namespace time_internal {
namespace cctz {
// A transition to a new UTC offset.
struct Transition {
std::int_least64_t unix_time; // the instant of this transition
std::uint_least8_t type_index; // index of the transition type
civil_second civil_sec; // local civil time of transition
civil_second prev_civil_sec; // local civil time one second earlier
struct ByUnixTime {
inline bool operator()(const Transition& lhs, const Transition& rhs) const {
return lhs.unix_time < rhs.unix_time;
}
};
struct ByCivilTime {
inline bool operator()(const Transition& lhs, const Transition& rhs) const {
return lhs.civil_sec < rhs.civil_sec;
}
};
};
// The characteristics of a particular transition.
struct TransitionType {
std::int_least32_t utc_offset; // the new prevailing UTC offset
civil_second civil_max; // max convertible civil time for offset
civil_second civil_min; // min convertible civil time for offset
bool is_dst; // did we move into daylight-saving time
std::uint_least8_t abbr_index; // index of the new abbreviation
};
// A time zone backed by the IANA Time Zone Database (zoneinfo).
class TimeZoneInfo : public TimeZoneIf {
public:
TimeZoneInfo() = default;
TimeZoneInfo(const TimeZoneInfo&) = delete;
TimeZoneInfo& operator=(const TimeZoneInfo&) = delete;
// Loads the zoneinfo for the given name, returning true if successful.
bool Load(const std::string& name);
// TimeZoneIf implementations.
time_zone::absolute_lookup BreakTime(
const time_point<sys_seconds>& tp) const override;
time_zone::civil_lookup MakeTime(
const civil_second& cs) const override;
std::string Description() const override;
bool NextTransition(time_point<sys_seconds>* tp) const override;
bool PrevTransition(time_point<sys_seconds>* tp) const override;
private:
struct Header { // counts of:
std::size_t timecnt; // transition times
std::size_t typecnt; // transition types
std::size_t charcnt; // zone abbreviation characters
std::size_t leapcnt; // leap seconds (we expect none)
std::size_t ttisstdcnt; // UTC/local indicators (unused)
std::size_t ttisgmtcnt; // 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 EquivTransitions(std::uint_fast8_t tt1_index,
std::uint_fast8_t tt2_index) const;
void ExtendTransitions(const std::string& name, const Header& hdr);
bool ResetToBuiltinUTC(const sys_seconds& offset);
bool Load(const std::string& name, ZoneInfoSource* zip);
// Helpers for BreakTime() and MakeTime().
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
const TransitionType& tt) const;
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
const Transition& tr) const;
time_zone::civil_lookup TimeLocal(const civil_second& cs,
year_t c4_shift) const;
std::vector<Transition> transitions_; // ordered by unix_time and civil_sec
std::vector<TransitionType> transition_types_; // distinct transition types
std::uint_fast8_t default_transition_type_; // for before first transition
std::string abbreviations_; // all the NUL-terminated abbreviations
std::string future_spec_; // for after the last zic transition
bool extended_; // future_spec_ was used to generate transitions
year_t last_year_; // the final year of the generated transitions
// We remember the transitions found during the last BreakTime() and
// MakeTime() calls. If the next request is for the same transition we
// will avoid re-searching.
mutable std::atomic<std::size_t> local_time_hint_ = {}; // BreakTime() hint
mutable std::atomic<std::size_t> time_local_hint_ = {}; // MakeTime() hint
};
} // namespace cctz
} // namespace time_internal
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(_WIN32) || defined(_WIN64)
#define _CRT_SECURE_NO_WARNINGS 1
#endif
#include "time_zone_libc.h"
#include <chrono>
#include <ctime>
#include <tuple>
#include <utility>
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
namespace time_internal {
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) {
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};
}
#elif defined(__sun)
// Uses the globals: 'timezone', 'altzone' and 'tzname'.
OffsetAbbr get_offset_abbr(const std::tm& tm) {
const bool is_dst = tm.tm_isdst > 0;
const int off = is_dst ? altzone : timezone;
const char* abbr = tzname[is_dst];
return {off, abbr};
}
#elif defined(__native_client__) || defined(__myriad2__) || \
defined(__EMSCRIPTEN__)
// Uses the globals: 'timezone' and 'tzname'.
OffsetAbbr get_offset_abbr(const std::tm& tm) {
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};
}
#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};
}
#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};
}
#endif // !defined(__tm_gmtoff) && !defined(__tm_zone)
#endif
} // namespace
TimeZoneLibC::TimeZoneLibC(const std::string& name)
: local_(name == "localtime") {}
time_zone::absolute_lookup TimeZoneLibC::BreakTime(
const time_point<sys_seconds>& tp) const {
time_zone::absolute_lookup al;
std::time_t t = ToUnixSeconds(tp);
std::tm tm;
if (local_) {
#if defined(_WIN32) || defined(_WIN64)
localtime_s(&tm, &t);
#else
localtime_r(&t, &tm);
#endif
std::tie(al.offset, al.abbr) = get_offset_abbr(tm);
} else {
#if defined(_WIN32) || defined(_WIN64)
gmtime_s(&tm, &t);
#else
gmtime_r(&t, &tm);
#endif
al.offset = 0;
al.abbr = "UTC";
}
al.cs = civil_second(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
al.is_dst = tm.tm_isdst > 0;
return al;
}
time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
time_zone::civil_lookup cl;
std::time_t t;
if (local_) {
// Does not handle SKIPPED/AMBIGUOUS or huge years.
std::tm tm;
tm.tm_year = static_cast<int>(cs.year() - 1900);
tm.tm_mon = cs.month() - 1;
tm.tm_mday = cs.day();
tm.tm_hour = cs.hour();
tm.tm_min = cs.minute();
tm.tm_sec = cs.second();
tm.tm_isdst = -1;
t = std::mktime(&tm);
} else {
t = cs - civil_second();
}
cl.kind = time_zone::civil_lookup::UNIQUE;
cl.pre = cl.trans = cl.post = FromUnixSeconds(t);
return cl;
}
std::string TimeZoneLibC::Description() const {
return local_ ? "localtime" : "UTC";
}
bool TimeZoneLibC::NextTransition(time_point<sys_seconds>* tp) const {
return false;
}
bool TimeZoneLibC::PrevTransition(time_point<sys_seconds>* tp) const {
return false;
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
#include <string>
#include "time_zone_if.h"
namespace absl {
namespace time_internal {
namespace cctz {
// A time zone backed by gmtime_r(3), localtime_r(3), and mktime(3),
// and which therefore only supports UTC and the local time zone.
// TODO: Add support for fixed offsets from UTC.
class TimeZoneLibC : public TimeZoneIf {
public:
explicit TimeZoneLibC(const std::string& name);
// TimeZoneIf implementations.
time_zone::absolute_lookup BreakTime(
const time_point<sys_seconds>& tp) const override;
time_zone::civil_lookup MakeTime(
const civil_second& cs) const override;
std::string Description() const override;
bool NextTransition(time_point<sys_seconds>* tp) const override;
bool PrevTransition(time_point<sys_seconds>* tp) const override;
private:
const bool local_; // localtime or UTC
};
} // namespace cctz
} // namespace time_internal
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#if __ANDROID_API__ >= 21
#include <dlfcn.h>
#endif
#endif
#include <cstdlib>
#include <cstring>
#include <string>
#include "time_zone_fixed.h"
#include "time_zone_impl.h"
namespace absl {
namespace time_internal {
namespace cctz {
#if defined(__ANDROID__) && __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.
// See Chromium's base/sys_info_android.cc for a similar example.
using property_get_func = int (*)(const char*, char*);
property_get_func LoadSystemPropertyGet() {
int flag = RTLD_LAZY | RTLD_GLOBAL;
#if defined(RTLD_NOLOAD)
flag |= RTLD_NOLOAD; // libc.so should already be resident
#endif
if (void* handle = dlopen("libc.so", flag)) {
void* sym = dlsym(handle, "__system_property_get");
dlclose(handle);
return reinterpret_cast<property_get_func>(sym);
}
return nullptr;
}
int __system_property_get(const char* name, char* value) {
static property_get_func system_property_get = LoadSystemPropertyGet();
return system_property_get ? system_property_get(name, value) : -1;
}
} // namespace
#endif
std::string time_zone::name() const {
return time_zone::Impl::get(*this).name();
}
time_zone::absolute_lookup time_zone::lookup(
const time_point<sys_seconds>& tp) const {
return time_zone::Impl::get(*this).BreakTime(tp);
}
time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
return time_zone::Impl::get(*this).MakeTime(cs);
}
bool operator==(time_zone lhs, time_zone rhs) {
return &time_zone::Impl::get(lhs) == &time_zone::Impl::get(rhs);
}
bool load_time_zone(const std::string& name, time_zone* tz) {
return time_zone::Impl::LoadTimeZone(name, tz);
}
time_zone utc_time_zone() {
return time_zone::Impl::UTC(); // avoid name lookup
}
time_zone fixed_time_zone(const sys_seconds& offset) {
time_zone tz;
load_time_zone(FixedOffsetToName(offset), &tz);
return tz;
}
time_zone local_time_zone() {
const char* zone = ":localtime";
// Allow ${TZ} to override to default zone.
char* tz_env = nullptr;
#if defined(_MSC_VER)
_dupenv_s(&tz_env, nullptr, "TZ");
#else
tz_env = std::getenv("TZ");
#endif
#if defined(__ANDROID__)
char sysprop[PROP_VALUE_MAX];
if (tz_env == nullptr)
if (__system_property_get("persist.sys.timezone", sysprop) > 0)
tz_env = sysprop;
#endif
if (tz_env) zone = tz_env;
// We only support the "[:]<zone-name>" form.
if (*zone == ':') ++zone;
// Map "localtime" to a system-specific name, but
// allow ${LOCALTIME} to override the default name.
char* localtime_env = nullptr;
if (strcmp(zone, "localtime") == 0) {
#if defined(_MSC_VER)
// System-specific default is just "localtime".
_dupenv_s(&localtime_env, nullptr, "LOCALTIME");
#else
zone = "/etc/localtime"; // System-specific default.
localtime_env = std::getenv("LOCALTIME");
#endif
if (localtime_env) zone = localtime_env;
}
const std::string name = zone;
#if defined(_MSC_VER)
free(localtime_env);
free(tz_env);
#endif
time_zone tz;
load_time_zone(name, &tz); // Falls back to UTC.
return tz;
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#include <chrono>
#include <cstddef>
#include <future>
#include <string>
#include <thread>
#include <vector>
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "gtest/gtest.h"
using std::chrono::time_point_cast;
using std::chrono::system_clock;
using std::chrono::nanoseconds;
using std::chrono::microseconds;
using std::chrono::milliseconds;
using std::chrono::seconds;
using std::chrono::minutes;
using std::chrono::hours;
namespace absl {
namespace time_internal {
namespace cctz {
namespace {
// A list of known time-zone names.
const char* const kTimeZoneNames[] = {
"Africa/Abidjan",
"Africa/Accra",
"Africa/Addis_Ababa",
"Africa/Algiers",
"Africa/Asmara",
"Africa/Asmera",
"Africa/Bamako",
"Africa/Bangui",
"Africa/Banjul",
"Africa/Bissau",
"Africa/Blantyre",
"Africa/Brazzaville",
"Africa/Bujumbura",
"Africa/Cairo",
"Africa/Casablanca",
"Africa/Ceuta",
"Africa/Conakry",
"Africa/Dakar",
"Africa/Dar_es_Salaam",
"Africa/Djibouti",
"Africa/Douala",
"Africa/El_Aaiun",
"Africa/Freetown",
"Africa/Gaborone",
"Africa/Harare",
"Africa/Johannesburg",
"Africa/Juba",
"Africa/Kampala",
"Africa/Khartoum",
"Africa/Kigali",
"Africa/Kinshasa",
"Africa/Lagos",
"Africa/Libreville",
"Africa/Lome",
"Africa/Luanda",
"Africa/Lubumbashi",
"Africa/Lusaka",
"Africa/Malabo",
"Africa/Maputo",
"Africa/Maseru",
"Africa/Mbabane",
"Africa/Mogadishu",
"Africa/Monrovia",
"Africa/Nairobi",
"Africa/Ndjamena",
"Africa/Niamey",
"Africa/Nouakchott",
"Africa/Ouagadougou",
"Africa/Porto-Novo",
"Africa/Sao_Tome",
"Africa/Timbuktu",
"Africa/Tripoli",
"Africa/Tunis",
"Africa/Windhoek",
"America/Adak",
"America/Anchorage",
"America/Anguilla",
"America/Antigua",
"America/Araguaina",
"America/Argentina/Buenos_Aires",
"America/Argentina/Catamarca",
"America/Argentina/ComodRivadavia",
"America/Argentina/Cordoba",
"America/Argentina/Jujuy",
"America/Argentina/La_Rioja",
"America/Argentina/Mendoza",
"America/Argentina/Rio_Gallegos",
"America/Argentina/Salta",
"America/Argentina/San_Juan",
"America/Argentina/San_Luis",
"America/Argentina/Tucuman",
"America/Argentina/Ushuaia",
"America/Aruba",
"America/Asuncion",
"America/Atikokan",
"America/Atka",
"America/Bahia",
"America/Bahia_Banderas",
"America/Barbados",
"America/Belem",
"America/Belize",
"America/Blanc-Sablon",
"America/Boa_Vista",
"America/Bogota",
"America/Boise",
"America/Buenos_Aires",
"America/Cambridge_Bay",
"America/Campo_Grande",
"America/Cancun",
"America/Caracas",
"America/Catamarca",
"America/Cayenne",
"America/Cayman",
"America/Chicago",
"America/Chihuahua",
"America/Coral_Harbour",
"America/Cordoba",
"America/Costa_Rica",
"America/Creston",
"America/Cuiaba",
"America/Curacao",
"America/Danmarkshavn",
"America/Dawson",
"America/Dawson_Creek",
"America/Denver",
"America/Detroit",
"America/Dominica",
"America/Edmonton",
"America/Eirunepe",
"America/El_Salvador",
"America/Ensenada",
"America/Fort_Nelson",
"America/Fort_Wayne",
"America/Fortaleza",
"America/Glace_Bay",
"America/Godthab",
"America/Goose_Bay",
"America/Grand_Turk",
"America/Grenada",
"America/Guadeloupe",
"America/Guatemala",
"America/Guayaquil",
"America/Guyana",
"America/Halifax",
"America/Havana",
"America/Hermosillo",
"America/Indiana/Indianapolis",
"America/Indiana/Knox",
"America/Indiana/Marengo",
"America/Indiana/Petersburg",
"America/Indiana/Tell_City",
"America/Indiana/Vevay",
"America/Indiana/Vincennes",
"America/Indiana/Winamac",
"America/Indianapolis",
"America/Inuvik",
"America/Iqaluit",
"America/Jamaica",
"America/Jujuy",
"America/Juneau",
"America/Kentucky/Louisville",
"America/Kentucky/Monticello",
"America/Knox_IN",
"America/Kralendijk",
"America/La_Paz",
"America/Lima",
"America/Los_Angeles",
"America/Louisville",
"America/Lower_Princes",
"America/Maceio",
"America/Managua",
"America/Manaus",
"America/Marigot",
"America/Martinique",
"America/Matamoros",
"America/Mazatlan",
"America/Mendoza",
"America/Menominee",
"America/Merida",
"America/Metlakatla",
"America/Mexico_City",
"America/Miquelon",
"America/Moncton",
"America/Monterrey",
"America/Montevideo",
"America/Montreal",
"America/Montserrat",
"America/Nassau",
"America/New_York",
"America/Nipigon",
"America/Nome",
"America/Noronha",
"America/North_Dakota/Beulah",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
"America/Paramaribo",
"America/Phoenix",
"America/Port-au-Prince",
"America/Port_of_Spain",
"America/Porto_Acre",
"America/Porto_Velho",
"America/Puerto_Rico",
"America/Punta_Arenas",
"America/Rainy_River",
"America/Rankin_Inlet",
"America/Recife",
"America/Regina",
"America/Resolute",
"America/Rio_Branco",
"America/Rosario",
"America/Santa_Isabel",
"America/Santarem",
"America/Santiago",
"America/Santo_Domingo",
"America/Sao_Paulo",
"America/Scoresbysund",
"America/Shiprock",
"America/Sitka",
"America/St_Barthelemy",
"America/St_Johns",
"America/St_Kitts",
"America/St_Lucia",
"America/St_Thomas",
"America/St_Vincent",
"America/Swift_Current",
"America/Tegucigalpa",
"America/Thule",
"America/Thunder_Bay",
"America/Tijuana",
"America/Toronto",
"America/Tortola",
"America/Vancouver",
"America/Virgin",
"America/Whitehorse",
"America/Winnipeg",
"America/Yakutat",
"America/Yellowknife",
"Antarctica/Casey",
"Antarctica/Davis",
"Antarctica/DumontDUrville",
"Antarctica/Macquarie",
"Antarctica/Mawson",
"Antarctica/McMurdo",
"Antarctica/Palmer",
"Antarctica/Rothera",
"Antarctica/South_Pole",
"Antarctica/Syowa",
"Antarctica/Troll",
"Antarctica/Vostok",
"Arctic/Longyearbyen",
"Asia/Aden",
"Asia/Almaty",
"Asia/Amman",
"Asia/Anadyr",
"Asia/Aqtau",
"Asia/Aqtobe",
"Asia/Ashgabat",
"Asia/Ashkhabad",
"Asia/Atyrau",
"Asia/Baghdad",
"Asia/Bahrain",
"Asia/Baku",
"Asia/Bangkok",
"Asia/Barnaul",
"Asia/Beirut",
"Asia/Bishkek",
"Asia/Brunei",
"Asia/Calcutta",
"Asia/Chita",
"Asia/Choibalsan",
"Asia/Chongqing",
"Asia/Chungking",
"Asia/Colombo",
"Asia/Dacca",
"Asia/Damascus",
"Asia/Dhaka",
"Asia/Dili",
"Asia/Dubai",
"Asia/Dushanbe",
"Asia/Famagusta",
"Asia/Gaza",
"Asia/Harbin",
"Asia/Hebron",
"Asia/Ho_Chi_Minh",
"Asia/Hong_Kong",
"Asia/Hovd",
"Asia/Irkutsk",
"Asia/Istanbul",
"Asia/Jakarta",
"Asia/Jayapura",
"Asia/Jerusalem",
"Asia/Kabul",
"Asia/Kamchatka",
"Asia/Karachi",
"Asia/Kashgar",
"Asia/Kathmandu",
"Asia/Katmandu",
"Asia/Khandyga",
"Asia/Kolkata",
"Asia/Krasnoyarsk",
"Asia/Kuala_Lumpur",
"Asia/Kuching",
"Asia/Kuwait",
"Asia/Macao",
"Asia/Macau",
"Asia/Magadan",
"Asia/Makassar",
"Asia/Manila",
"Asia/Muscat",
"Asia/Nicosia",
"Asia/Novokuznetsk",
"Asia/Novosibirsk",
"Asia/Omsk",
"Asia/Oral",
"Asia/Phnom_Penh",
"Asia/Pontianak",
"Asia/Pyongyang",
"Asia/Qatar",
"Asia/Qyzylorda",
"Asia/Rangoon",
"Asia/Riyadh",
"Asia/Saigon",
"Asia/Sakhalin",
"Asia/Samarkand",
"Asia/Seoul",
"Asia/Shanghai",
"Asia/Singapore",
"Asia/Srednekolymsk",
"Asia/Taipei",
"Asia/Tashkent",
"Asia/Tbilisi",
"Asia/Tehran",
"Asia/Tel_Aviv",
"Asia/Thimbu",
"Asia/Thimphu",
"Asia/Tokyo",
"Asia/Tomsk",
"Asia/Ujung_Pandang",
"Asia/Ulaanbaatar",
"Asia/Ulan_Bator",
"Asia/Urumqi",
"Asia/Ust-Nera",
"Asia/Vientiane",
"Asia/Vladivostok",
"Asia/Yakutsk",
"Asia/Yangon",
"Asia/Yekaterinburg",
"Asia/Yerevan",
"Atlantic/Azores",
"Atlantic/Bermuda",
"Atlantic/Canary",
"Atlantic/Cape_Verde",
"Atlantic/Faeroe",
"Atlantic/Faroe",
"Atlantic/Jan_Mayen",
"Atlantic/Madeira",
"Atlantic/Reykjavik",
"Atlantic/South_Georgia",
"Atlantic/St_Helena",
"Atlantic/Stanley",
"Australia/ACT",
"Australia/Adelaide",
"Australia/Brisbane",
"Australia/Broken_Hill",
"Australia/Canberra",
"Australia/Currie",
"Australia/Darwin",
"Australia/Eucla",
"Australia/Hobart",
"Australia/LHI",
"Australia/Lindeman",
"Australia/Lord_Howe",
"Australia/Melbourne",
"Australia/NSW",
"Australia/North",
"Australia/Perth",
"Australia/Queensland",
"Australia/South",
"Australia/Sydney",
"Australia/Tasmania",
"Australia/Victoria",
"Australia/West",
"Australia/Yancowinna",
"Brazil/Acre",
"Brazil/DeNoronha",
"Brazil/East",
"Brazil/West",
"CET",
"CST6CDT",
"Canada/Atlantic",
"Canada/Central",
"Canada/East-Saskatchewan",
"Canada/Eastern",
"Canada/Mountain",
"Canada/Newfoundland",
"Canada/Pacific",
"Canada/Saskatchewan",
"Canada/Yukon",
"Chile/Continental",
"Chile/EasterIsland",
"Cuba",
"EET",
"EST",
"EST5EDT",
"Egypt",
"Eire",
"Etc/GMT",
"Etc/GMT+0",
"Etc/GMT+1",
"Etc/GMT+10",
"Etc/GMT+11",
"Etc/GMT+12",
"Etc/GMT+2",
"Etc/GMT+3",
"Etc/GMT+4",
"Etc/GMT+5",
"Etc/GMT+6",
"Etc/GMT+7",
"Etc/GMT+8",
"Etc/GMT+9",
"Etc/GMT-0",
"Etc/GMT-1",
"Etc/GMT-10",
"Etc/GMT-11",
"Etc/GMT-12",
"Etc/GMT-13",
"Etc/GMT-14",
"Etc/GMT-2",
"Etc/GMT-3",
"Etc/GMT-4",
"Etc/GMT-5",
"Etc/GMT-6",
"Etc/GMT-7",
"Etc/GMT-8",
"Etc/GMT-9",
"Etc/GMT0",
"Etc/Greenwich",
"Etc/UCT",
"Etc/UTC",
"Etc/Universal",
"Etc/Zulu",
"Europe/Amsterdam",
"Europe/Andorra",
"Europe/Astrakhan",
"Europe/Athens",
"Europe/Belfast",
"Europe/Belgrade",
"Europe/Berlin",
"Europe/Bratislava",
"Europe/Brussels",
"Europe/Bucharest",
"Europe/Budapest",
"Europe/Busingen",
"Europe/Chisinau",
"Europe/Copenhagen",
"Europe/Dublin",
"Europe/Gibraltar",
"Europe/Guernsey",
"Europe/Helsinki",
"Europe/Isle_of_Man",
"Europe/Istanbul",
"Europe/Jersey",
"Europe/Kaliningrad",
"Europe/Kiev",
"Europe/Kirov",
"Europe/Lisbon",
"Europe/Ljubljana",
"Europe/London",
"Europe/Luxembourg",
"Europe/Madrid",
"Europe/Malta",
"Europe/Mariehamn",
"Europe/Minsk",
"Europe/Monaco",
"Europe/Moscow",
"Europe/Nicosia",
"Europe/Oslo",
"Europe/Paris",
"Europe/Podgorica",
"Europe/Prague",
"Europe/Riga",
"Europe/Rome",
"Europe/Samara",
"Europe/San_Marino",
"Europe/Sarajevo",
"Europe/Saratov",
"Europe/Simferopol",
"Europe/Skopje",
"Europe/Sofia",
"Europe/Stockholm",
"Europe/Tallinn",
"Europe/Tirane",
"Europe/Tiraspol",
"Europe/Ulyanovsk",
"Europe/Uzhgorod",
"Europe/Vaduz",
"Europe/Vatican",
"Europe/Vienna",
"Europe/Vilnius",
"Europe/Volgograd",
"Europe/Warsaw",
"Europe/Zagreb",
"Europe/Zaporozhye",
"Europe/Zurich",
"GB",
"GB-Eire",
"GMT",
"GMT+0",
"GMT-0",
"GMT0",
"Greenwich",
"HST",
"Hongkong",
"Iceland",
"Indian/Antananarivo",
"Indian/Chagos",
"Indian/Christmas",
"Indian/Cocos",
"Indian/Comoro",
"Indian/Kerguelen",
"Indian/Mahe",
"Indian/Maldives",
"Indian/Mauritius",
"Indian/Mayotte",
"Indian/Reunion",
"Iran",
"Israel",
"Jamaica",
"Japan",
"Kwajalein",
"Libya",
"MET",
"MST",
"MST7MDT",
"Mexico/BajaNorte",
"Mexico/BajaSur",
"Mexico/General",
"NZ",
"NZ-CHAT",
"Navajo",
"PRC",
"PST8PDT",
"Pacific/Apia",
"Pacific/Auckland",
"Pacific/Bougainville",
"Pacific/Chatham",
"Pacific/Chuuk",
"Pacific/Easter",
"Pacific/Efate",
"Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Fiji",
"Pacific/Funafuti",
"Pacific/Galapagos",
"Pacific/Gambier",
"Pacific/Guadalcanal",
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Johnston",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
"Pacific/Majuro",
"Pacific/Marquesas",
"Pacific/Midway",
"Pacific/Nauru",
"Pacific/Niue",
"Pacific/Norfolk",
"Pacific/Noumea",
"Pacific/Pago_Pago",
"Pacific/Palau",
"Pacific/Pitcairn",
"Pacific/Pohnpei",
"Pacific/Ponape",
"Pacific/Port_Moresby",
"Pacific/Rarotonga",
"Pacific/Saipan",
"Pacific/Samoa",
"Pacific/Tahiti",
"Pacific/Tarawa",
"Pacific/Tongatapu",
"Pacific/Truk",
"Pacific/Wake",
"Pacific/Wallis",
"Pacific/Yap",
"Poland",
"Portugal",
"ROC",
"ROK",
"Singapore",
"Turkey",
"UCT",
"US/Alaska",
"US/Aleutian",
"US/Arizona",
"US/Central",
"US/East-Indiana",
"US/Eastern",
"US/Hawaii",
"US/Indiana-Starke",
"US/Michigan",
"US/Mountain",
"US/Pacific",
"US/Samoa",
"UTC",
"Universal",
"W-SU",
"WET",
"Zulu",
nullptr
};
// Helper to return a loaded time zone by value (UTC on error).
time_zone LoadZone(const std::string& name) {
time_zone tz;
load_time_zone(name, &tz);
return tz;
}
// This helper is a macro so that failed expectations show up with the
// correct line numbers.
#define ExpectTime(tp, tz, y, m, d, hh, mm, ss, off, isdst, zone) \
do { \
time_zone::absolute_lookup al = tz.lookup(tp); \
EXPECT_EQ(y, al.cs.year()); \
EXPECT_EQ(m, al.cs.month()); \
EXPECT_EQ(d, al.cs.day()); \
EXPECT_EQ(hh, al.cs.hour()); \
EXPECT_EQ(mm, al.cs.minute()); \
EXPECT_EQ(ss, al.cs.second()); \
EXPECT_EQ(off, al.offset); \
EXPECT_TRUE(isdst == al.is_dst); \
/* EXPECT_STREQ(zone, al.abbr); */ \
} while (0)
} // namespace
TEST(TimeZones, LoadZonesConcurrently) {
std::promise<void> ready_promise;
std::shared_future<void> ready_future(ready_promise.get_future());
auto load_zones = [ready_future](std::promise<void>* started,
std::set<std::string>* failures) {
started->set_value();
ready_future.wait();
for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) {
std::string zone = *np;
time_zone tz;
if (load_time_zone(zone, &tz)) {
EXPECT_EQ(zone, tz.name());
} else {
failures->insert(zone);
}
}
};
const std::size_t n_threads = 128;
std::vector<std::thread> threads;
std::vector<std::set<std::string>> thread_failures(n_threads);
for (std::size_t i = 0; i != n_threads; ++i) {
std::promise<void> started;
threads.emplace_back(load_zones, &started, &thread_failures[i]);
started.get_future().wait();
}
ready_promise.set_value();
for (auto& thread : threads) {
thread.join();
}
// Allow a small number of failures to account for skew between
// the contents of kTimeZoneNames and the zoneinfo data source.
const std::size_t max_failures = 3;
std::set<std::string> failures;
for (const auto& thread_failure : thread_failures) {
failures.insert(thread_failure.begin(), thread_failure.end());
}
EXPECT_LE(failures.size(), max_failures) << testing::PrintToString(failures);
}
TEST(TimeZone, NamedTimeZones) {
const time_zone utc = utc_time_zone();
EXPECT_EQ("UTC", utc.name());
const time_zone nyc = LoadZone("America/New_York");
EXPECT_EQ("America/New_York", nyc.name());
const time_zone syd = LoadZone("Australia/Sydney");
EXPECT_EQ("Australia/Sydney", syd.name());
const time_zone fixed0 = fixed_time_zone(sys_seconds::zero());
EXPECT_EQ("UTC", fixed0.name());
const time_zone fixed_pos =
fixed_time_zone(hours(3) + minutes(25) + seconds(45));
EXPECT_EQ("Fixed/UTC+03:25:45", fixed_pos.name());
const time_zone fixed_neg =
fixed_time_zone(-(hours(12) + minutes(34) + seconds(56)));
EXPECT_EQ("Fixed/UTC-12:34:56", fixed_neg.name());
}
TEST(TimeZone, Failures) {
time_zone tz;
EXPECT_FALSE(load_time_zone(":America/Los_Angeles", &tz));
tz = LoadZone("America/Los_Angeles");
EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz));
EXPECT_EQ(system_clock::from_time_t(0),
convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC
// Ensures that the load still fails on a subsequent attempt.
tz = LoadZone("America/Los_Angeles");
EXPECT_FALSE(load_time_zone("Invalid/TimeZone", &tz));
EXPECT_EQ(system_clock::from_time_t(0),
convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC
// Loading an empty std::string timezone should fail.
tz = LoadZone("America/Los_Angeles");
EXPECT_FALSE(load_time_zone("", &tz));
EXPECT_EQ(system_clock::from_time_t(0),
convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC
}
TEST(TimeZone, Equality) {
const time_zone a;
const time_zone b;
EXPECT_EQ(a, b);
EXPECT_EQ(a.name(), b.name());
const time_zone implicit_utc;
const time_zone explicit_utc = utc_time_zone();
EXPECT_EQ(implicit_utc, explicit_utc);
EXPECT_EQ(implicit_utc.name(), explicit_utc.name());
const time_zone fixed_zero = fixed_time_zone(sys_seconds::zero());
EXPECT_EQ(fixed_zero, LoadZone(fixed_zero.name()));
EXPECT_EQ(fixed_zero, explicit_utc);
const time_zone fixed_utc = LoadZone("Fixed/UTC+00:00:00");
EXPECT_EQ(fixed_utc, LoadZone(fixed_utc.name()));
EXPECT_EQ(fixed_utc, explicit_utc);
const time_zone fixed_pos =
fixed_time_zone(hours(3) + minutes(25) + seconds(45));
EXPECT_EQ(fixed_pos, LoadZone(fixed_pos.name()));
EXPECT_NE(fixed_pos, explicit_utc);
const time_zone fixed_neg =
fixed_time_zone(-(hours(12) + minutes(34) + seconds(56)));
EXPECT_EQ(fixed_neg, LoadZone(fixed_neg.name()));
EXPECT_NE(fixed_neg, explicit_utc);
const time_zone fixed_lim = fixed_time_zone(hours(24));
EXPECT_EQ(fixed_lim, LoadZone(fixed_lim.name()));
EXPECT_NE(fixed_lim, explicit_utc);
const time_zone fixed_ovfl = fixed_time_zone(hours(24) + seconds(1));
EXPECT_EQ(fixed_ovfl, LoadZone(fixed_ovfl.name()));
EXPECT_EQ(fixed_ovfl, explicit_utc);
EXPECT_EQ(fixed_time_zone(seconds(1)), fixed_time_zone(seconds(1)));
const time_zone local = local_time_zone();
EXPECT_EQ(local, LoadZone(local.name()));
time_zone la = LoadZone("America/Los_Angeles");
time_zone nyc = LoadZone("America/New_York");
EXPECT_NE(la, nyc);
}
TEST(StdChronoTimePoint, TimeTAlignment) {
// Ensures that the Unix epoch and the system clock epoch are an integral
// number of seconds apart. This simplifies conversions to/from time_t.
auto diff = system_clock::time_point() - system_clock::from_time_t(0);
EXPECT_EQ(system_clock::time_point::duration::zero(), diff % seconds(1));
}
TEST(BreakTime, TimePointResolution) {
const time_zone utc = utc_time_zone();
const auto t0 = system_clock::from_time_t(0);
ExpectTime(time_point_cast<nanoseconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
ExpectTime(time_point_cast<microseconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
ExpectTime(time_point_cast<milliseconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
ExpectTime(time_point_cast<seconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
ExpectTime(time_point_cast<sys_seconds>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
ExpectTime(time_point_cast<minutes>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
ExpectTime(time_point_cast<hours>(t0), utc,
1970, 1, 1, 0, 0, 0, 0, false, "UTC");
}
TEST(BreakTime, LocalTimeInUTC) {
const time_zone tz = utc_time_zone();
const auto tp = system_clock::from_time_t(0);
ExpectTime(tp, tz, 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(BreakTime, LocalTimeInUTCUnaligned) {
const time_zone tz = utc_time_zone();
const auto tp = system_clock::from_time_t(0) - milliseconds(500);
ExpectTime(tp, tz, 1969, 12, 31, 23, 59, 59, 0, false, "UTC");
EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(BreakTime, LocalTimePosix) {
// See IEEE Std 1003.1-1988 B.2.3 General Terms, Epoch.
const time_zone tz = utc_time_zone();
const auto tp = system_clock::from_time_t(536457599);
ExpectTime(tp, tz, 1986, 12, 31, 23, 59, 59, 0, false, "UTC");
EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(TimeZoneImpl, LocalTimeInFixed) {
const sys_seconds offset = -(hours(8) + minutes(33) + seconds(47));
const time_zone tz = fixed_time_zone(offset);
const auto tp = system_clock::from_time_t(0);
ExpectTime(tp, tz, 1969, 12, 31, 15, 26, 13, offset.count(), false,
"UTC-083347");
EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(BreakTime, LocalTimeInNewYork) {
const time_zone tz = LoadZone("America/New_York");
const auto tp = system_clock::from_time_t(45);
ExpectTime(tp, tz, 1969, 12, 31, 19, 0, 45, -5 * 60 * 60, false, "EST");
EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(BreakTime, LocalTimeInMTV) {
const time_zone tz = LoadZone("America/Los_Angeles");
const auto tp = system_clock::from_time_t(1380855729);
ExpectTime(tp, tz, 2013, 10, 3, 20, 2, 9, -7 * 60 * 60, true, "PDT");
EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(BreakTime, LocalTimeInSydney) {
const time_zone tz = LoadZone("Australia/Sydney");
const auto tp = system_clock::from_time_t(90);
ExpectTime(tp, tz, 1970, 1, 1, 10, 1, 30, 10 * 60 * 60, false, "AEST");
EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz))));
}
TEST(MakeTime, TimePointResolution) {
const time_zone utc = utc_time_zone();
const time_point<nanoseconds> tp_ns =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_ns, utc));
const time_point<microseconds> tp_us =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_us, utc));
const time_point<milliseconds> tp_ms =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_ms, utc));
const time_point<seconds> tp_s =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_s, utc));
const time_point<sys_seconds> tp_s64 =
convert(civil_second(2015, 1, 2, 3, 4, 5), utc);
EXPECT_EQ("04:05", format("%M:%E*S", tp_s64, utc));
// These next two require time_point_cast because the conversion from a
// resolution of seconds (the return value of convert()) to a coarser
// resolution requires an explicit cast.
const time_point<minutes> tp_m =
time_point_cast<minutes>(
convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
EXPECT_EQ("04:00", format("%M:%E*S", tp_m, utc));
const time_point<hours> tp_h =
time_point_cast<hours>(
convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
EXPECT_EQ("00:00", format("%M:%E*S", tp_h, utc));
}
TEST(MakeTime, Normalization) {
const time_zone tz = LoadZone("America/New_York");
const auto tp = convert(civil_second(2009, 2, 13, 18, 31, 30), tz);
EXPECT_EQ(system_clock::from_time_t(1234567890), tp);
// Now requests for the same time_point but with out-of-range fields.
EXPECT_EQ(tp, convert(civil_second(2008, 14, 13, 18, 31, 30), tz)); // month
EXPECT_EQ(tp, convert(civil_second(2009, 1, 44, 18, 31, 30), tz)); // day
EXPECT_EQ(tp, convert(civil_second(2009, 2, 12, 42, 31, 30), tz)); // hour
EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 17, 91, 30), tz)); // minute
EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 18, 30, 90), tz)); // second
}
// NOTE: Run this with --copt=-ftrapv to detect overflow problems.
TEST(MakeTime, SysSecondsLimits) {
const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez";
const time_zone utc = utc_time_zone();
const time_zone east = fixed_time_zone(hours(14));
const time_zone west = fixed_time_zone(-hours(14));
time_point<sys_seconds> tp;
// Approach the maximal time_point<sys_seconds> value from below.
tp = convert(civil_second(292277026596, 12, 4, 15, 30, 6), utc);
EXPECT_EQ("292277026596-12-04T15:30:06+00:00", format(RFC3339, tp, utc));
tp = convert(civil_second(292277026596, 12, 4, 15, 30, 7), utc);
EXPECT_EQ("292277026596-12-04T15:30:07+00:00", format(RFC3339, tp, utc));
EXPECT_EQ(time_point<sys_seconds>::max(), tp);
tp = convert(civil_second(292277026596, 12, 4, 15, 30, 8), utc);
EXPECT_EQ(time_point<sys_seconds>::max(), tp);
tp = convert(civil_second::max(), utc);
EXPECT_EQ(time_point<sys_seconds>::max(), tp);
// Checks that we can also get the maximal value for a far-east zone.
tp = convert(civil_second(292277026596, 12, 5, 5, 30, 7), east);
EXPECT_EQ("292277026596-12-05T05:30:07+14:00", format(RFC3339, tp, east));
EXPECT_EQ(time_point<sys_seconds>::max(), tp);
tp = convert(civil_second(292277026596, 12, 5, 5, 30, 8), east);
EXPECT_EQ(time_point<sys_seconds>::max(), tp);
tp = convert(civil_second::max(), east);
EXPECT_EQ(time_point<sys_seconds>::max(), tp);
// Checks that we can also get the maximal value for a far-west zone.
tp = convert(civil_second(292277026596, 12, 4, 1, 30, 7), west);
EXPECT_EQ("292277026596-12-04T01:30:07-14:00", format(RFC3339, tp, west));
EXPECT_EQ(time_point<sys_seconds>::max(), tp);
tp = convert(civil_second(292277026596, 12, 4, 7, 30, 8), west);
EXPECT_EQ(time_point<sys_seconds>::max(), tp);
tp = convert(civil_second::max(), west);
EXPECT_EQ(time_point<sys_seconds>::max(), tp);
// Approach the minimal time_point<sys_seconds> value from above.
tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 53), utc);
EXPECT_EQ("-292277022657-01-27T08:29:53+00:00", format(RFC3339, tp, utc));
tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 52), utc);
EXPECT_EQ("-292277022657-01-27T08:29:52+00:00", format(RFC3339, tp, utc));
EXPECT_EQ(time_point<sys_seconds>::min(), tp);
tp = convert(civil_second(-292277022657, 1, 27, 8, 29, 51), utc);
EXPECT_EQ(time_point<sys_seconds>::min(), tp);
tp = convert(civil_second::min(), utc);
EXPECT_EQ(time_point<sys_seconds>::min(), tp);
// Checks that we can also get the minimal value for a far-east zone.
tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 52), east);
EXPECT_EQ("-292277022657-01-27T22:29:52+14:00", format(RFC3339, tp, east));
EXPECT_EQ(time_point<sys_seconds>::min(), tp);
tp = convert(civil_second(-292277022657, 1, 27, 22, 29, 51), east);
EXPECT_EQ(time_point<sys_seconds>::min(), tp);
tp = convert(civil_second::min(), east);
EXPECT_EQ(time_point<sys_seconds>::min(), tp);
// Checks that we can also get the minimal value for a far-west zone.
tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 52), west);
EXPECT_EQ("-292277022657-01-26T18:29:52-14:00", format(RFC3339, tp, west));
EXPECT_EQ(time_point<sys_seconds>::min(), tp);
tp = convert(civil_second(-292277022657, 1, 26, 18, 29, 51), west);
EXPECT_EQ(time_point<sys_seconds>::min(), tp);
tp = convert(civil_second::min(), west);
EXPECT_EQ(time_point<sys_seconds>::min(), tp);
}
TEST(TimeZoneEdgeCase, AmericaNewYork) {
const time_zone tz = LoadZone("America/New_York");
// Spring 1:59:59 -> 3:00:00
auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -5 * 3600, false, "EST");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -4 * 3600, true, "EDT");
// Fall 1:59:59 -> 1:00:00
tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -4 * 3600, true, "EDT");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -5 * 3600, false, "EST");
}
TEST(TimeZoneEdgeCase, AmericaLosAngeles) {
const time_zone tz = LoadZone("America/Los_Angeles");
// Spring 1:59:59 -> 3:00:00
auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -8 * 3600, false, "PST");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 3, 10, 3, 0, 0, -7 * 3600, true, "PDT");
// Fall 1:59:59 -> 1:00:00
tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, true, "PDT");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 11, 3, 1, 0, 0, -8 * 3600, false, "PST");
}
TEST(TimeZoneEdgeCase, ArizonaNoTransition) {
const time_zone tz = LoadZone("America/Phoenix");
// No transition in Spring.
auto tp = convert(civil_second(2013, 3, 10, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 3, 10, 1, 59, 59, -7 * 3600, false, "MST");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 3, 10, 2, 0, 0, -7 * 3600, false, "MST");
// No transition in Fall.
tp = convert(civil_second(2013, 11, 3, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 11, 3, 1, 59, 59, -7 * 3600, false, "MST");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 11, 3, 2, 0, 0, -7 * 3600, false, "MST");
}
TEST(TimeZoneEdgeCase, AsiaKathmandu) {
const time_zone tz = LoadZone("Asia/Kathmandu");
// A non-DST offset change from +0530 to +0545
//
// 504901799 == Tue, 31 Dec 1985 23:59:59 +0530 (+0530)
// 504901800 == Wed, 1 Jan 1986 00:15:00 +0545 (+0545)
auto tp = convert(civil_second(1985, 12, 31, 23, 59, 59), tz);
ExpectTime(tp, tz, 1985, 12, 31, 23, 59, 59, 5.5 * 3600, false, "+0530");
tp += seconds(1);
ExpectTime(tp, tz, 1986, 1, 1, 0, 15, 0, 5.75 * 3600, false, "+0545");
}
TEST(TimeZoneEdgeCase, PacificChatham) {
const time_zone tz = LoadZone("Pacific/Chatham");
// One-hour DST offset changes, but at atypical values
//
// 1365256799 == Sun, 7 Apr 2013 03:44:59 +1345 (+1345)
// 1365256800 == Sun, 7 Apr 2013 02:45:00 +1245 (+1245)
auto tp = convert(civil_second(2013, 4, 7, 3, 44, 59), tz);
ExpectTime(tp, tz, 2013, 4, 7, 3, 44, 59, 13.75 * 3600, true, "+1345");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 4, 7, 2, 45, 0, 12.75 * 3600, false, "+1245");
// 1380376799 == Sun, 29 Sep 2013 02:44:59 +1245 (+1245)
// 1380376800 == Sun, 29 Sep 2013 03:45:00 +1345 (+1345)
tp = convert(civil_second(2013, 9, 29, 2, 44, 59), tz);
ExpectTime(tp, tz, 2013, 9, 29, 2, 44, 59, 12.75 * 3600, false, "+1245");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 9, 29, 3, 45, 0, 13.75 * 3600, true, "+1345");
}
TEST(TimeZoneEdgeCase, AustraliaLordHowe) {
const time_zone tz = LoadZone("Australia/Lord_Howe");
// Half-hour DST offset changes
//
// 1365260399 == Sun, 7 Apr 2013 01:59:59 +1100 (+11)
// 1365260400 == Sun, 7 Apr 2013 01:30:00 +1030 (+1030)
auto tp = convert(civil_second(2013, 4, 7, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 4, 7, 1, 59, 59, 11 * 3600, true, "+11");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 4, 7, 1, 30, 0, 10.5 * 3600, false, "+1030");
// 1380986999 == Sun, 6 Oct 2013 01:59:59 +1030 (+1030)
// 1380987000 == Sun, 6 Oct 2013 02:30:00 +1100 (+11)
tp = convert(civil_second(2013, 10, 6, 1, 59, 59), tz);
ExpectTime(tp, tz, 2013, 10, 6, 1, 59, 59, 10.5 * 3600, false, "+1030");
tp += seconds(1);
ExpectTime(tp, tz, 2013, 10, 6, 2, 30, 0, 11 * 3600, true, "+11");
}
TEST(TimeZoneEdgeCase, PacificApia) {
const time_zone tz = LoadZone("Pacific/Apia");
// At the end of December 2011, Samoa jumped forward by one day,
// skipping 30 December from the local calendar, when the nation
// moved to the west of the International Date Line.
//
// A one-day, non-DST offset change
//
// 1325239199 == Thu, 29 Dec 2011 23:59:59 -1000 (-10)
// 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))));
tp += 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))));
}
TEST(TimeZoneEdgeCase, AfricaCairo) {
const time_zone tz = LoadZone("Africa/Cairo");
// An interesting case of midnight not existing.
//
// 1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
// 1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz);
ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
tp += seconds(1);
ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
}
TEST(TimeZoneEdgeCase, AfricaMonrovia) {
const time_zone tz = LoadZone("Africa/Monrovia");
// Strange offset change -00:44:30 -> +00:00:00 (non-DST)
//
// 63593069 == Thu, 6 Jan 1972 23:59:59 -0044 (MMT)
// 63593070 == Fri, 7 Jan 1972 00:44:30 +0000 (GMT)
auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
tp += seconds(1);
#ifndef TZDATA_2017B_IS_UBIQUITOUS
// The 2017b tzdata release moved the shift from -004430 to +00
// from 1972-05-01 to 1972-01-07, so we temporarily accept both
// outcomes until 2017b is ubiquitous.
if (tz.lookup(tp).offset == -44.5 * 60) {
tp = convert(civil_second(1972, 4, 30, 23, 59, 59), tz);
ExpectTime(tp, tz, 1972, 4, 30, 23, 59, 59, -44.5 * 60, false, "LRT");
tp += seconds(1);
ExpectTime(tp, tz, 1972, 5, 1, 0, 44, 30, 0 * 60, false, "GMT");
return;
}
#endif
ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
}
TEST(TimeZoneEdgeCase, AmericaJamaica) {
// Jamaica discontinued DST transitions in 1983, and is now at a
// constant -0500. This makes it an interesting edge-case target.
// Note that the 32-bit times used in a (tzh_version == 0) zoneinfo
// file cannot represent the abbreviation-only transition of 1890,
// so we ignore the abbreviation by expecting what we received.
const time_zone tz = LoadZone("America/Jamaica");
// Before the first transition.
auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz);
#if AMERICA_JAMAICA_PRE_1913_OFFSET_FIX
// Commit 907241e: Fix off-by-1 error for Jamaica and T&C before 1913.
// Until that commit has made its way into a full release we avoid the
// expectations on the -18430 offset below. TODO: Uncomment these.
ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false,
tz.lookup(tp).abbr);
// Over the first (abbreviation-change only) transition.
// -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
// -2524503169 == Wed, 1 Jan 1890 00:00:00 -0507 (KMT)
tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz);
ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false,
tz.lookup(tp).abbr);
tp += seconds(1);
ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT");
#endif
// Over the last (DST) transition.
// 436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT)
// 436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST)
tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz);
ExpectTime(tp, tz, 1983, 10, 30, 1, 59, 59, -4 * 3600, true, "EDT");
tp += seconds(1);
ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST");
// After the last transition.
tp = convert(civil_second(1983, 12, 31, 23, 59, 59), tz);
ExpectTime(tp, tz, 1983, 12, 31, 23, 59, 59, -5 * 3600, false, "EST");
}
TEST(TimeZoneEdgeCase, WET) {
// Cover some non-existent times within forward transitions.
const time_zone tz = LoadZone("WET");
// Before the first transition.
auto tp = convert(civil_second(1977, 1, 1, 0, 0, 0), tz);
ExpectTime(tp, tz, 1977, 1, 1, 0, 0, 0, 0, false, "WET");
// Over the first transition.
// 228877199 == Sun, 3 Apr 1977 00:59:59 +0000 (WET)
// 228877200 == Sun, 3 Apr 1977 02:00:00 +0100 (WEST)
tp = convert(civil_second(1977, 4, 3, 0, 59, 59), tz);
ExpectTime(tp, tz, 1977, 4, 3, 0, 59, 59, 0, false, "WET");
tp += seconds(1);
ExpectTime(tp, tz, 1977, 4, 3, 2, 0, 0, 1 * 3600, true, "WEST");
// A non-existent time within the first transition.
time_zone::civil_lookup cl1 = tz.lookup(civil_second(1977, 4, 3, 1, 15, 0));
EXPECT_EQ(time_zone::civil_lookup::SKIPPED, cl1.kind);
ExpectTime(cl1.pre, tz, 1977, 4, 3, 2, 15, 0, 1 * 3600, true, "WEST");
ExpectTime(cl1.trans, tz, 1977, 4, 3, 2, 0, 0, 1 * 3600, true, "WEST");
ExpectTime(cl1.post, tz, 1977, 4, 3, 0, 15, 0, 0 * 3600, false, "WET");
// A non-existent time within the second forward transition.
time_zone::civil_lookup cl2 = tz.lookup(civil_second(1978, 4, 2, 1, 15, 0));
EXPECT_EQ(time_zone::civil_lookup::SKIPPED, cl2.kind);
ExpectTime(cl2.pre, tz, 1978, 4, 2, 2, 15, 0, 1 * 3600, true, "WEST");
ExpectTime(cl2.trans, tz, 1978, 4, 2, 2, 0, 0, 1 * 3600, true, "WEST");
ExpectTime(cl2.post, tz, 1978, 4, 2, 0, 15, 0, 0 * 3600, false, "WET");
}
TEST(TimeZoneEdgeCase, FixedOffsets) {
const time_zone gmtm5 = LoadZone("Etc/GMT+5"); // -0500
auto tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtm5);
ExpectTime(tp, gmtm5, 1970, 1, 1, 0, 0, 0, -5 * 3600, false, "-05");
EXPECT_EQ(system_clock::from_time_t(5 * 3600), tp);
const time_zone gmtp5 = LoadZone("Etc/GMT-5"); // +0500
tp = convert(civil_second(1970, 1, 1, 0, 0, 0), gmtp5);
ExpectTime(tp, gmtp5, 1970, 1, 1, 0, 0, 0, 5 * 3600, false, "+05");
EXPECT_EQ(system_clock::from_time_t(-5 * 3600), tp);
}
TEST(TimeZoneEdgeCase, NegativeYear) {
// Tests transition from year 0 (aka 1BCE) to year -1.
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))));
tp -= 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))));
}
TEST(TimeZoneEdgeCase, UTC32bitLimit) {
const time_zone tz = utc_time_zone();
// Limits of signed 32-bit time_t
//
// 2147483647 == Tue, 19 Jan 2038 03:14:07 +0000 (UTC)
// 2147483648 == Tue, 19 Jan 2038 03:14:08 +0000 (UTC)
auto tp = convert(civil_second(2038, 1, 19, 3, 14, 7), tz);
ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 7, 0 * 3600, false, "UTC");
tp += seconds(1);
ExpectTime(tp, tz, 2038, 1, 19, 3, 14, 8, 0 * 3600, false, "UTC");
}
TEST(TimeZoneEdgeCase, UTC5DigitYear) {
const time_zone tz = utc_time_zone();
// Rollover to 5-digit year
//
// 253402300799 == Fri, 31 Dec 9999 23:59:59 +0000 (UTC)
// 253402300800 == Sat, 1 Jan 1000 00:00:00 +0000 (UTC)
auto tp = convert(civil_second(9999, 12, 31, 23, 59, 59), tz);
ExpectTime(tp, tz, 9999, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC");
tp += seconds(1);
ExpectTime(tp, tz, 10000, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC");
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "time_zone_posix.h"
#include <cstddef>
#include <cstring>
#include <limits>
#include <string>
namespace absl {
namespace time_internal {
namespace cctz {
namespace {
const char kDigits[] = "0123456789";
const char* ParseInt(const char* p, int min, int max, int* vp) {
int value = 0;
const char* op = p;
const int kMaxInt = std::numeric_limits<int>::max();
for (; const char* dp = strchr(kDigits, *p); ++p) {
int d = static_cast<int>(dp - kDigits);
if (d >= 10) break; // '\0'
if (value > kMaxInt / 10) return nullptr;
value *= 10;
if (value > kMaxInt - d) return nullptr;
value += d;
}
if (p == op || value < min || value > max) return nullptr;
*vp = value;
return p;
}
// abbr = <.*?> | [^-+,\d]{3,}
const char* ParseAbbr(const char* p, std::string* abbr) {
const char* op = p;
if (*p == '<') { // special zoneinfo <...> form
while (*++p != '>') {
if (*p == '\0') return nullptr;
}
abbr->assign(op + 1, static_cast<std::size_t>(p - op) - 1);
return ++p;
}
while (*p != '\0') {
if (strchr("-+,", *p)) break;
if (strchr(kDigits, *p)) break;
++p;
}
if (p - op < 3) return nullptr;
abbr->assign(op, static_cast<std::size_t>(p - op));
return p;
}
// offset = [+|-]hh[:mm[:ss]] (aggregated into single seconds value)
const char* ParseOffset(const char* p, int min_hour, int max_hour, int sign,
std::int_fast32_t* offset) {
if (p == nullptr) return nullptr;
if (*p == '+' || *p == '-') {
if (*p++ == '-') sign = -sign;
}
int hours = 0;
int minutes = 0;
int seconds = 0;
p = ParseInt(p, min_hour, max_hour, &hours);
if (p == nullptr) return nullptr;
if (*p == ':') {
p = ParseInt(p + 1, 0, 59, &minutes);
if (p == nullptr) return nullptr;
if (*p == ':') {
p = ParseInt(p + 1, 0, 59, &seconds);
if (p == nullptr) return nullptr;
}
}
*offset = sign * ((((hours * 60) + minutes) * 60) + seconds);
return p;
}
// datetime = ( Jn | n | Mm.w.d ) [ / offset ]
const char* ParseDateTime(const char* p, PosixTransition* res) {
if (p != nullptr && *p == ',') {
if (*++p == 'M') {
int month = 0;
if ((p = ParseInt(p + 1, 1, 12, &month)) != nullptr && *p == '.') {
int week = 0;
if ((p = ParseInt(p + 1, 1, 5, &week)) != nullptr && *p == '.') {
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);
}
}
}
} else if (*p == 'J') {
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);
}
} 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);
}
}
}
if (p != nullptr) {
res->time.offset = 2 * 60 * 60; // default offset is 02:00:00
if (*p == '/') p = ParseOffset(p + 1, -167, 167, 1, &res->time.offset);
}
return p;
}
} // namespace
// spec = std offset [ dst [ offset ] , datetime , datetime ]
bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) {
const char* p = spec.c_str();
if (*p == ':') return false;
p = ParseAbbr(p, &res->std_abbr);
p = ParseOffset(p, 0, 24, -1, &res->std_offset);
if (p == nullptr) return false;
if (*p == '\0') return true;
p = ParseAbbr(p, &res->dst_abbr);
if (p == nullptr) return false;
res->dst_offset = res->std_offset + (60 * 60); // default
if (*p != ',') p = ParseOffset(p, 0, 24, -1, &res->dst_offset);
p = ParseDateTime(p, &res->dst_start);
p = ParseDateTime(p, &res->dst_end);
return p != nullptr && *p == '\0';
}
} // namespace cctz
} // namespace time_internal
} // namespace absl
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Parsing of a POSIX zone spec as described in the TZ part of section 8.3 in
// http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html.
//
// The current POSIX spec for America/Los_Angeles is "PST8PDT,M3.2.0,M11.1.0",
// which would be broken down as ...
//
// PosixTimeZone {
// std_abbr = "PST"
// std_offset = -28800
// dst_abbr = "PDT"
// dst_offset = -25200
// dst_start = PosixTransition {
// date {
// m {
// month = 3
// week = 2
// weekday = 0
// }
// }
// time {
// offset = 7200
// }
// }
// dst_end = PosixTransition {
// date {
// m {
// month = 11
// week = 1
// weekday = 0
// }
// }
// time {
// offset = 7200
// }
// }
// }
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
#include <cstdint>
#include <string>
namespace absl {
namespace time_internal {
namespace cctz {
// The date/time of the transition. The date is specified as either:
// (J) the Nth day of the year (1 <= N <= 365), excluding leap days, or
// (N) the Nth day of the year (0 <= N <= 365), including leap days, or
// (M) the Nth weekday of a month (e.g., the 2nd Sunday in March).
// The time, specified as a day offset, identifies the particular moment
// of the transition, and may be negative or >= 24h, and in which case
// it would take us to another day, and perhaps week, or even month.
struct PosixTransition {
enum DateFormat { J, N, M };
struct {
DateFormat fmt;
union {
struct {
std::int_fast16_t day; // day of non-leap year [1:365]
} j;
struct {
std::int_fast16_t day; // day of year [0:365]
} n;
struct {
std::int_fast8_t month; // month of year [1:12]
std::int_fast8_t week; // week of month [1:5] (5==last)
std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat
} m;
};
} date;
struct {
std::int_fast32_t offset; // seconds before/after 00:00:00
} time;
};
// The entirety of a POSIX-std::string specified time-zone rule. The standard
// abbreviation and offset are always given. If the time zone includes
// daylight saving, then the daylight abbrevation is non-empty and the
// remaining fields are also valid. Note that the start/end transitions
// are not ordered---in the southern hemisphere the transition to end
// daylight time occurs first in any particular year.
struct PosixTimeZone {
std::string std_abbr;
std::int_fast32_t std_offset;
std::string dst_abbr;
std::int_fast32_t dst_offset;
PosixTransition dst_start;
PosixTransition dst_end;
};
// Breaks down a POSIX time-zone specification into its constituent pieces,
// filling in any missing values (DST offset, or start/end transition times)
// with the standard-defined defaults. Returns false if the specification
// could not be parsed (although some fields of *res may have been altered).
bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res);
} // namespace cctz
} // namespace time_internal
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
#ifndef TZFILE_H
#define TZFILE_H
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
/*
** This header is for use ONLY with the time conversion code.
** There is no guarantee that it will remain unchanged,
** or that it will remain at all.
** Do NOT copy it to any system include directory.
** Thank you!
*/
/*
** Information about time zone files.
*/
#ifndef TZDIR
#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
#endif /* !defined TZDIR */
#ifndef TZDEFAULT
#define TZDEFAULT "/etc/localtime"
#endif /* !defined TZDEFAULT */
#ifndef TZDEFRULES
#define TZDEFRULES "posixrules"
#endif /* !defined TZDEFRULES */
/*
** Each file begins with. . .
*/
#define TZ_MAGIC "TZif"
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
char tzh_reserved[15]; /* reserved; must be zero */
char tzh_ttisgmtcnt[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 */
char tzh_typecnt[4]; /* coded number of local time types */
char tzh_charcnt[4]; /* coded number of abbr. chars */
};
/*
** . . .followed by. . .
**
** tzh_timecnt (char [4])s coded transition times a la time(2)
** tzh_timecnt (unsigned char)s types of local time starting at above
** tzh_typecnt repetitions of
** one (char [4]) coded UT offset in seconds
** one (unsigned char) used to set tm_isdst
** one (unsigned char) that's an abbreviation list index
** tzh_charcnt (char)s '\0'-terminated zone abbreviations
** tzh_leapcnt repetitions of
** one (char [4]) coded leap second transition times
** 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
** assumed to be local time
*/
/*
** If tzh_version is '2' or greater, the above is followed by a second instance
** of tzhead and a second instance of the data in which each coded transition
** time uses 8 rather than 4 chars,
** then a POSIX-TZ-environment-variable-style std::string for use in handling
** instants after the last transition time stored in the file
** (with nothing between the newlines if there is no POSIX representation for
** such instants).
**
** If tz_version is '3' or greater, the above is extended as follows.
** First, the POSIX TZ std::string's hour offset may range from -167
** through 167 as compared to the POSIX-required 0 through 24.
** Second, its DST start time may be January 1 at 00:00 and its stop
** time December 31 at 24:00 plus the difference between DST and
** standard time, indicating DST all year.
*/
/*
** In the current implementation, "tzset()" refuses to deal with files that
** exceed any of the limits below.
*/
#ifndef TZ_MAX_TIMES
#define TZ_MAX_TIMES 2000
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
/* (limited by what unsigned chars can hold) */
#endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
#endif /* !defined TZ_MAX_LEAPS */
#endif /* !defined TZFILE_H */
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
namespace absl {
namespace time_internal {
namespace cctz {
// Defined out-of-line to avoid emitting a weak vtable in all TUs.
ZoneInfoSource::~ZoneInfoSource() {}
} // namespace cctz
} // namespace time_internal
} // namespace absl
namespace absl {
namespace time_internal {
namespace cctz_extension {
namespace {
// A default for cctz_extension::zone_info_source_factory, which simply
// defers to the fallback factory.
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory(
const std::string& name,
const std::function<std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>(
const std::string& name)>& fallback_factory) {
return fallback_factory(name);
}
} // namespace
// 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)
extern ZoneInfoSourceFactory zone_info_source_factory;
extern ZoneInfoSourceFactory default_factory;
ZoneInfoSourceFactory default_factory = DefaultFactory;
#if defined(_M_IX86)
#pragma comment( \
linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZA=?default_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZA")
#elif defined(_M_IA_64) || defined(_M_AMD64)
#pragma comment( \
linker, \
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZEA=?default_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZEA")
#else
#error Unsupported MSVC platform
#endif
#else
ZoneInfoSourceFactory zone_info_source_factory
__attribute__((weak)) = DefaultFactory;
#endif // _MSC_VER
} // namespace cctz_extension
} // namespace time_internal
} // namespace absl
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}
git clone https://github.com/eggert/tz.git
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 ...
# 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 N905 (2016-11-15). See: Updates on ISO 3166-1
# http://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 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 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
# tz zone descriptions
#
# This file is in the public domain.
#
# From Paul Eggert (2017-10-01):
# This file contains a table where each row stands for a zone where
# civil time stamps 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 zone, as a comma-separated list
# of ISO 3166 2-character country codes. See the file 'iso3166.tab'.
# 2. Latitude and longitude of the zone'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. Zone name used in value of TZ environment variable.
# Please see the theory.html file for how zone names are chosen.
# If multiple zones 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 zones.
#
# If a zone 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 zones first, where that does not contradict (1).
#
# This table is intended as an aid for users, to help them select time
# zone data entries 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 -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville
AQ -6736+06253 Antarctica/Mawson Mawson
AQ -6448-06406 Antarctica/Palmer Palmer
AQ -6734-06808 Antarctica/Rothera Rothera
AQ -690022+0393524 Antarctica/Syowa Syowa
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 (most areas)
AU -3956+14352 Australia/Currie Tasmania (King Island)
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
BS +2505-07721 America/Nassau
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 +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore)
CA +4339-07923 America/Toronto Eastern - ON, QC (most areas)
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 +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H)
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 +4906-11631 America/Creston MST - BC (Creston)
CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John)
CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson)
CA +4916-12307 America/Vancouver Pacific - BC (most areas)
CA +6043-13503 America/Whitehorse Pacific - Yukon (south)
CA +6404-13925 America/Dawson Pacific - Yukon (north)
CC -1210+09655 Indian/Cocos
CH,DE,LI +4723+00832 Europe/Zurich Swiss time
CI,BF,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
CW,AW,BQ,SX +1211-06900 America/Curacao
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
GH +0533-00013 Africa/Accra
GI +3608-00521 Europe/Gibraltar
GL +6411-05144 America/Godthab 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 -0308-17105 Pacific/Enderbury 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 +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 +2214+11335 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,KY +0858-07932 America/Panama
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 -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas)
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 +182806-0660622 America/Puerto_Rico
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
RU +4457+03406 Europe/Simferopol MSK+00 - Crimea
RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd
RU +5836+04939 Europe/Kirov MSK+00 - Kirov
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,KW,YE +2438+04643 Asia/Riyadh
SB -0932+16012 Pacific/Guadalcanal
SC -0440+05528 Indian/Mahe
SD +1536+03232 Africa/Khartoum
SE +5920+01803 Europe/Stockholm
SG +0117+10351 Asia/Singapore
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 -2110-17510 Pacific/Tongatapu
TR +4101+02858 Europe/Istanbul
TT,AG,AI,BL,DM,GD,GP,KN,LC,MF,MS,VC,VG,VI +1039-06131 America/Port_of_Spain
TV -0831+17913 Pacific/Funafuti
TW +2503+12130 Asia/Taipei
UA +5026+03031 Europe/Kiev Ukraine (most areas)
UA +4837+02218 Europe/Uzhgorod Ruthenia
UA +4750+03510 Europe/Zaporozhye Zaporozh'ye/Zaporizhia; Lugansk/Luhansk (east)
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 +332654-1120424 America/Phoenix MST - Arizona (except Navajo)
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
......@@ -19,7 +19,9 @@
#include <cstring>
#include "absl/base/internal/raw_logging.h"
#include "cctz/zone_info_source.h"
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
namespace cctz = absl::time_internal::cctz;
namespace absl {
namespace time_internal {
......@@ -33,6 +35,8 @@ TimeZone LoadTimeZone(const std::string& name) {
} // namespace time_internal
} // namespace absl
namespace absl {
namespace time_internal {
namespace cctz_extension {
namespace {
......@@ -115,3 +119,5 @@ std::unique_ptr<cctz::ZoneInfoSource> TestFactory(
ZoneInfoSourceFactory zone_info_source_factory = TestFactory;
} // namespace cctz_extension
} // namespace time_internal
} // namespace absl
......@@ -36,8 +36,10 @@
#include <ctime>
#include <limits>
#include "cctz/civil_time.h"
#include "cctz/time_zone.h"
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace cctz = absl::time_internal::cctz;
namespace absl {
namespace {
......
......@@ -64,7 +64,7 @@
#include <utility>
#include "absl/base/port.h" // Needed for string vs std::string
#include "cctz/time_zone.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
......@@ -1032,12 +1032,12 @@ std::string UnparseFlag(Time t);
// - http://en.wikipedia.org/wiki/Zoneinfo
class TimeZone {
public:
explicit TimeZone(cctz::time_zone tz) : cz_(tz) {}
explicit TimeZone(time_internal::cctz::time_zone tz) : cz_(tz) {}
TimeZone() = default; // UTC, but prefer UTCTimeZone() to be explicit.
TimeZone(const TimeZone&) = default;
TimeZone& operator=(const TimeZone&) = default;
explicit operator cctz::time_zone() const { return cz_; }
explicit operator time_internal::cctz::time_zone() const { return cz_; }
std::string name() const { return cz_.name(); }
......@@ -1048,7 +1048,7 @@ class TimeZone {
return os << tz.name();
}
cctz::time_zone cz_;
time_internal::cctz::time_zone cz_;
};
// LoadTimeZone()
......@@ -1058,11 +1058,11 @@ class TimeZone {
// `false` and `*tz` is set to the UTC time zone.
inline bool LoadTimeZone(const std::string& name, TimeZone* tz) {
if (name == "localtime") {
*tz = TimeZone(cctz::local_time_zone());
*tz = TimeZone(time_internal::cctz::local_time_zone());
return true;
}
cctz::time_zone cz;
const bool b = cctz::load_time_zone(name, &cz);
time_internal::cctz::time_zone cz;
const bool b = time_internal::cctz::load_time_zone(name, &cz);
*tz = TimeZone(cz);
return b;
}
......@@ -1074,14 +1074,14 @@ inline bool LoadTimeZone(const std::string& name, TimeZone* tz) {
// you'll get UTC (i.e., no offset) instead.
inline TimeZone FixedTimeZone(int seconds) {
return TimeZone(
cctz::fixed_time_zone(std::chrono::seconds(seconds)));
time_internal::cctz::fixed_time_zone(std::chrono::seconds(seconds)));
}
// UTCTimeZone()
//
// Convenience method returning the UTC time zone.
inline TimeZone UTCTimeZone() {
return TimeZone(cctz::utc_time_zone());
return TimeZone(time_internal::cctz::utc_time_zone());
}
// LocalTimeZone()
......@@ -1091,7 +1091,7 @@ inline TimeZone UTCTimeZone() {
// and particularly so in a server process, as the zone configured for the
// local machine should be irrelevant. Prefer an explicit zone name.
inline TimeZone LocalTimeZone() {
return TimeZone(cctz::local_time_zone());
return TimeZone(time_internal::cctz::local_time_zone());
}
// ============================================================================
......
......@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cctz/time_zone.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#include "gtest/gtest.h"
#include "absl/time/internal/test_util.h"
#include "absl/time/time.h"
namespace cctz = absl::time_internal::cctz;
namespace {
TEST(TimeZone, ValueSemantics) {
......
......@@ -774,7 +774,9 @@ class optional : private optional_internal::optional_data<T>,
//
// Accesses the underlying `T` value's member `m` of an `optional`. If the
// `optional` is empty, behavior is undefined.
constexpr const T* operator->() const { return this->pointer(); }
//
// If you need myOpt->foo in constexpr, use (*myOpt).foo instead.
const T* operator->() const { return this->pointer(); }
T* operator->() {
assert(this->engaged_);
return this->pointer();
......@@ -870,12 +872,12 @@ class optional : private optional_internal::optional_data<T>,
private:
// Private accessors for internal storage viewed as pointer to T.
constexpr const T* pointer() const { return &this->data_; }
T* pointer() { return &this->data_; }
const T* pointer() const { return std::addressof(this->data_); }
T* pointer() { return std::addressof(this->data_); }
// Private accessors for internal storage viewed as reference to T.
constexpr const T& reference() const { return *this->pointer(); }
T& reference() { return *(this->pointer()); }
constexpr const T& reference() const { return this->data_; }
T& reference() { return this->data_; }
// T constraint checks. You can't have an optional of nullopt_t, in_place_t
// or a reference.
......
......@@ -263,7 +263,7 @@ TEST(optionalTest, CopyConstructor) {
constexpr absl::optional<TrivialCopyable> o1(42);
constexpr absl::optional<TrivialCopyable> o2 = o1;
static_assert(o2, "");
static_assert(o2->x == 42, "");
static_assert((*o2).x == 42, "");
#ifndef ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG
EXPECT_TRUE(absl::is_trivially_copy_constructible<
absl::optional<TrivialCopyable>>::value);
......@@ -327,14 +327,14 @@ TEST(optionalTest, Destructor) {
TEST(optionalTest, InPlaceConstructor) {
constexpr absl::optional<ConstexprType> opt0{absl::in_place_t()};
static_assert(opt0, "");
static_assert(opt0->x == ConstexprType::kCtorDefault, "");
static_assert((*opt0).x == ConstexprType::kCtorDefault, "");
constexpr absl::optional<ConstexprType> opt1{absl::in_place_t(), 1};
static_assert(opt1, "");
static_assert(opt1->x == ConstexprType::kCtorInt, "");
static_assert((*opt1).x == ConstexprType::kCtorInt, "");
#ifndef ABSL_HAVE_NO_CONSTEXPR_INITIALIZER_LIST
constexpr absl::optional<ConstexprType> opt2{absl::in_place_t(), {1, 2}};
static_assert(opt2, "");
static_assert(opt2->x == ConstexprType::kCtorInitializerList, "");
static_assert((*opt2).x == ConstexprType::kCtorInitializerList, "");
#endif
// TODO(absl-team): uncomment these when std::is_constructible<T, Args&&...>
......@@ -362,13 +362,13 @@ TEST(optionalTest, ValueConstructor) {
// optional via ConstexprType::ConstexprType(const char*).
constexpr absl::optional<ConstexprType> opt1 = {"abc"};
static_assert(opt1, "");
static_assert(ConstexprType::kCtorConstChar == opt1->x, "");
static_assert(ConstexprType::kCtorConstChar == (*opt1).x, "");
EXPECT_TRUE(
(std::is_convertible<const char*, absl::optional<ConstexprType>>::value));
// direct initialization
constexpr absl::optional<ConstexprType> opt2{2};
static_assert(opt2, "");
static_assert(ConstexprType::kCtorInt == opt2->x, "");
static_assert(ConstexprType::kCtorInt == (*opt2).x, "");
EXPECT_FALSE(
(std::is_convertible<int, absl::optional<ConstexprType>>::value));
......@@ -934,6 +934,33 @@ TEST(optionalTest, Swap) {
EXPECT_TRUE(noexcept(swap(opt1, opt2)));
}
template <int v>
struct DeletedOpAddr {
constexpr static const int value = v;
constexpr DeletedOpAddr() = default;
constexpr const DeletedOpAddr<v>* operator&() const = delete; // NOLINT
DeletedOpAddr<v>* operator&() = delete; // NOLINT
};
// The static_assert featuring a constexpr call to operator->() is commented out
// to document the fact that the current implementation of absl::optional<T>
// expects such usecases to be malformed and not compile.
TEST(optionalTest, OperatorAddr) {
constexpr const int v = -1;
{ // constexpr
constexpr const absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{});
static_assert(opt.has_value(), "");
// static_assert(opt->value == v, "");
static_assert((*opt).value == v, "");
}
{ // non-constexpr
const absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{});
EXPECT_TRUE(opt.has_value());
EXPECT_TRUE(opt->value == v);
EXPECT_TRUE((*opt).value == v);
}
}
TEST(optionalTest, PointerStuff) {
absl::optional<std::string> opt(absl::in_place, "foo");
EXPECT_EQ("foo", *opt);
......@@ -943,7 +970,7 @@ TEST(optionalTest, PointerStuff) {
EXPECT_EQ(opt_const->size(), 3);
constexpr absl::optional<ConstexprType> opt1(1);
static_assert(opt1->x == ConstexprType::kCtorInt, "");
static_assert((*opt1).x == ConstexprType::kCtorInt, "");
}
// gcc has a bug pre 4.9.1 where it doesn't do correct overload resolution
......@@ -1123,13 +1150,13 @@ TEST(optionalTest, make_optional) {
constexpr TrivialCopyable v;
constexpr absl::optional<TrivialCopyable> c_opt0 = absl::make_optional(v);
static_assert(c_opt0->x == 0, "");
static_assert((*c_opt0).x == 0, "");
constexpr absl::optional<TrivialCopyable> c_opt1 =
absl::make_optional<TrivialCopyable>();
static_assert(c_opt1->x == 0, "");
static_assert((*c_opt1).x == 0, "");
constexpr absl::optional<TrivialCopyable> c_opt2 =
absl::make_optional<TrivialCopyable>(42);
static_assert(c_opt2->x == 42, "");
static_assert((*c_opt2).x == 42, "");
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment