- 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
pull/117/head
Abseil Team 7 years ago committed by Derek Mauro
parent 94ce52d46c
commit af7882601a
  1. 2
      CMake/AbseilHelpers.cmake
  2. 9
      CMake/README.md
  3. 6
      CMakeLists.txt
  4. 7
      WORKSPACE
  5. 2
      absl/base/config.h
  6. 2
      absl/strings/str_cat_test.cc
  7. 2
      absl/strings/string_view_test.cc
  8. 8
      absl/time/BUILD.bazel
  9. 25
      absl/time/CMakeLists.txt
  10. 4
      absl/time/format.cc
  11. 105
      absl/time/internal/cctz/BUILD.bazel
  12. 329
      absl/time/internal/cctz/include/cctz/civil_time.h
  13. 564
      absl/time/internal/cctz/include/cctz/civil_time_detail.h
  14. 316
      absl/time/internal/cctz/include/cctz/time_zone.h
  15. 91
      absl/time/internal/cctz/include/cctz/zone_info_source.h
  16. 90
      absl/time/internal/cctz/src/civil_time_detail.cc
  17. 1049
      absl/time/internal/cctz/src/civil_time_test.cc
  18. 133
      absl/time/internal/cctz/src/time_zone_fixed.cc
  19. 49
      absl/time/internal/cctz/src/time_zone_fixed.h
  20. 848
      absl/time/internal/cctz/src/time_zone_format.cc
  21. 1408
      absl/time/internal/cctz/src/time_zone_format_test.cc
  22. 41
      absl/time/internal/cctz/src/time_zone_if.cc
  23. 70
      absl/time/internal/cctz/src/time_zone_if.h
  24. 117
      absl/time/internal/cctz/src/time_zone_impl.cc
  25. 97
      absl/time/internal/cctz/src/time_zone_impl.h
  26. 956
      absl/time/internal/cctz/src/time_zone_info.cc
  27. 132
      absl/time/internal/cctz/src/time_zone_info.h
  28. 156
      absl/time/internal/cctz/src/time_zone_libc.cc
  29. 50
      absl/time/internal/cctz/src/time_zone_libc.h
  30. 142
      absl/time/internal/cctz/src/time_zone_lookup.cc
  31. 1259
      absl/time/internal/cctz/src/time_zone_lookup_test.cc
  32. 155
      absl/time/internal/cctz/src/time_zone_posix.cc
  33. 118
      absl/time/internal/cctz/src/time_zone_posix.h
  34. 117
      absl/time/internal/cctz/src/tzfile.h
  35. 70
      absl/time/internal/cctz/src/zone_info_source.cc
  36. 37
      absl/time/internal/cctz/testdata/README.zoneinfo
  37. 1
      absl/time/internal/cctz/testdata/version
  38. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
  39. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
  40. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
  41. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
  42. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
  43. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
  44. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
  45. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
  46. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
  47. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
  48. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
  49. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
  50. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
  51. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
  52. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
  53. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
  54. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
  55. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
  56. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
  57. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
  58. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
  59. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
  60. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
  61. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
  62. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
  63. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
  64. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
  65. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
  66. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
  67. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
  68. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
  69. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
  70. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
  71. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
  72. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
  73. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
  74. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
  75. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
  76. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
  77. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
  78. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
  79. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
  80. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
  81. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
  82. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
  83. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
  84. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
  85. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
  86. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
  87. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
  88. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
  89. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
  90. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
  91. BIN
      absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
  92. BIN
      absl/time/internal/cctz/testdata/zoneinfo/America/Adak
  93. BIN
      absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
  94. BIN
      absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
  95. BIN
      absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
  96. BIN
      absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina
  97. BIN
      absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires
  98. BIN
      absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca
  99. BIN
      absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia
  100. BIN
      absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba
  101. Some files were not shown because too many files have changed in this diff Show More

@ -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 {

@ -0,0 +1,105 @@
# 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

@ -0,0 +1,329 @@
// 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_

@ -0,0 +1,564 @@
// 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_

@ -0,0 +1,316 @@
// 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_

@ -0,0 +1,91 @@
// 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_

@ -0,0 +1,90 @@
// 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

File diff suppressed because it is too large Load Diff

@ -0,0 +1,133 @@
// 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

@ -0,0 +1,49 @@
// 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_

@ -0,0 +1,848 @@
// 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

File diff suppressed because it is too large Load Diff

@ -0,0 +1,41 @@
// 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

@ -0,0 +1,70 @@
// 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_

@ -0,0 +1,117 @@
// 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

@ -0,0 +1,97 @@
// 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_

@ -0,0 +1,956 @@
// 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

@ -0,0 +1,132 @@
// 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_

@ -0,0 +1,156 @@
// 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

@ -0,0 +1,50 @@
// 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_

@ -0,0 +1,142 @@
// 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

File diff suppressed because it is too large Load Diff

@ -0,0 +1,155 @@
// 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

@ -0,0 +1,118 @@
// 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_

@ -0,0 +1,117 @@
#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 */

@ -0,0 +1,70 @@
// 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

@ -0,0 +1,37 @@
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 ...

@ -0,0 +1 @@
2018d-2-g8d1dac0

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save