- 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
This commit is contained in:
		
							parent
							
								
									94ce52d46c
								
							
						
					
					
						commit
						af7882601a
					
				
					 638 changed files with 9262 additions and 58 deletions
				
			
		|  | @ -34,7 +34,7 @@ function(absl_library) | ||||||
|   cmake_parse_arguments(ABSL_LIB |   cmake_parse_arguments(ABSL_LIB | ||||||
|     "DISABLE_INSTALL" # keep that in case we want to support installation one day |     "DISABLE_INSTALL" # keep that in case we want to support installation one day | ||||||
|     "TARGET;EXPORT_NAME" |     "TARGET;EXPORT_NAME" | ||||||
|     "SOURCES;PUBLIC_LIBRARIES;PRIVATE_COMPILE_FLAGS;PUBLIC_INCLUDE_DIRS;PRIVATE_INCLUDE_DIRS" |     "SOURCES;PUBLIC_LIBRARIES;PRIVATE_COMPILE_FLAGS" | ||||||
|     ${ARGN} |     ${ARGN} | ||||||
|   ) |   ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,10 +19,8 @@ googletest framework | ||||||
| 
 | 
 | ||||||
| ### Step-by-Step Instructions | ### Step-by-Step Instructions | ||||||
| 
 | 
 | ||||||
| 1. If you haven't done so already, integrate the Abseil dependency | 1. If you want to build the Abseil tests, integrate the Abseil dependency | ||||||
| [CCTZ](https://github.com/google/cctz) into your CMake project. Consequently, the | [Google Test](https://github.com/google/googletest) into your CMake project. To disable Abseil tests, you have to pass | ||||||
| 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 |  | ||||||
| `-DBUILD_TESTING=OFF` when configuring your project with CMake. | `-DBUILD_TESTING=OFF` when configuring your project with CMake. | ||||||
| 
 | 
 | ||||||
| 2. Download Abseil and copy it into a subdirectory in your CMake project or add | 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 | 3. You can then use the CMake command | ||||||
| [`add_subdirectory()`](https://cmake.org/cmake/help/latest/command/add_subdirectory.html) | [`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 | to include Abseil directly in your CMake project. | ||||||
| customize the name of the `cctz` target with the `-DABSL_CCTZ_TARGET=*my_cctz*` option. |  | ||||||
| 
 | 
 | ||||||
| 4. Add the **absl::** target you wish to use to the | 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) | [`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 | ## pthread | ||||||
| find_package(Threads REQUIRED) | find_package(Threads REQUIRED) | ||||||
| 
 | 
 | ||||||
| if(NOT ABSL_CCTZ_TARGET) |  | ||||||
|   set(ABSL_CCTZ_TARGET cctz) |  | ||||||
| endif() |  | ||||||
| 
 |  | ||||||
| # commented: used only for standalone test | # commented: used only for standalone test | ||||||
| # Don't remove these or else CMake CI will break | # Don't remove these or else CMake CI will break | ||||||
| #add_subdirectory(cctz) |  | ||||||
| #add_subdirectory(googletest) | #add_subdirectory(googletest) | ||||||
| check_target(${ABSL_CCTZ_TARGET}) |  | ||||||
| 
 | 
 | ||||||
| ## check targets | ## check targets | ||||||
| if(BUILD_TESTING) | if(BUILD_TESTING) | ||||||
|  |  | ||||||
|  | @ -17,13 +17,6 @@ http_archive( | ||||||
|      strip_prefix = "googletest-master", |      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. | # RE2 regular-expression framework. Used by some unit-tests. | ||||||
| http_archive( | http_archive( | ||||||
|     name = "com_googlesource_code_re2", |     name = "com_googlesource_code_re2", | ||||||
|  |  | ||||||
|  | @ -384,7 +384,7 @@ | ||||||
| 
 | 
 | ||||||
| // ABSL_HAVE_STD_VARIANT
 | // 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 | #ifdef ABSL_HAVE_STD_VARIANT | ||||||
| #error "ABSL_HAVE_STD_VARIANT cannot be directly set." | #error "ABSL_HAVE_STD_VARIANT cannot be directly set." | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -206,6 +206,8 @@ struct Mallocator { | ||||||
|     typedef Mallocator<U> other; |     typedef Mallocator<U> other; | ||||||
|   }; |   }; | ||||||
|   Mallocator() = default; |   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))); } |   T* allocate(size_t n) { return static_cast<T*>(std::malloc(n * sizeof(T))); } | ||||||
|   void deallocate(T* p, size_t) { std::free(p); } |   void deallocate(T* p, size_t) { std::free(p); } | ||||||
|  |  | ||||||
|  | @ -50,6 +50,8 @@ struct Mallocator { | ||||||
|     typedef Mallocator<U> other; |     typedef Mallocator<U> other; | ||||||
|   }; |   }; | ||||||
|   Mallocator() = default; |   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))); } |   T* allocate(size_t n) { return static_cast<T*>(std::malloc(n * sizeof(T))); } | ||||||
|   void deallocate(T* p, size_t) { std::free(p); } |   void deallocate(T* p, size_t) { std::free(p); } | ||||||
|  |  | ||||||
|  | @ -44,8 +44,8 @@ cc_library( | ||||||
|         "//absl/base", |         "//absl/base", | ||||||
|         "//absl/base:core_headers", |         "//absl/base:core_headers", | ||||||
|         "//absl/numeric:int128", |         "//absl/numeric:int128", | ||||||
|         "@com_googlesource_code_cctz//:civil_time", |         "//absl/time/internal/cctz:civil_time", | ||||||
|         "@com_googlesource_code_cctz//:time_zone", |         "//absl/time/internal/cctz:time_zone", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -63,7 +63,7 @@ cc_library( | ||||||
|     deps = [ |     deps = [ | ||||||
|         ":time", |         ":time", | ||||||
|         "//absl/base", |         "//absl/base", | ||||||
|         "@com_googlesource_code_cctz//:time_zone", |         "//absl/time/internal/cctz:time_zone", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -91,7 +91,7 @@ cc_test( | ||||||
|         "//absl/base", |         "//absl/base", | ||||||
|         "//absl/base:config", |         "//absl/base:config", | ||||||
|         "//absl/base:core_headers", |         "//absl/base:core_headers", | ||||||
|  |         "//absl/time/internal/cctz:time_zone", | ||||||
|         "@com_google_googletest//:gtest_main", |         "@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 | list(APPEND TIME_INTERNAL_HEADERS | ||||||
|   "internal/test_util.h" |   "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 | list(APPEND TIME_SRC | ||||||
|  | @ -29,10 +33,27 @@ list(APPEND TIME_SRC | ||||||
|   "clock.cc" |   "clock.cc" | ||||||
|   "duration.cc" |   "duration.cc" | ||||||
|   "format.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_PUBLIC_HEADERS} | ||||||
|   ${TIME_INTERNAL_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( | absl_library( | ||||||
|   TARGET |   TARGET | ||||||
|  | @ -41,8 +62,6 @@ absl_library( | ||||||
|     ${TIME_SRC} |     ${TIME_SRC} | ||||||
|   PUBLIC_LIBRARIES |   PUBLIC_LIBRARIES | ||||||
|     ${TIME_PUBLIC_LIBRARIES} |     ${TIME_PUBLIC_LIBRARIES} | ||||||
|   PUBLIC_INCLUDE_DIRS |  | ||||||
|     ${CCTZ_INCLUDE_DIRS} |  | ||||||
|   EXPORT_NAME |   EXPORT_NAME | ||||||
|     time |     time | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | @ -16,8 +16,10 @@ | ||||||
| #include <cctype> | #include <cctype> | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
| 
 | 
 | ||||||
|  | #include "absl/time/internal/cctz/include/cctz/time_zone.h" | ||||||
| #include "absl/time/time.h" | #include "absl/time/time.h" | ||||||
| #include "cctz/time_zone.h" | 
 | ||||||
|  | namespace cctz = absl::time_internal::cctz; | ||||||
| 
 | 
 | ||||||
| namespace absl { | namespace absl { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										105
									
								
								absl/time/internal/cctz/BUILD.bazel
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								absl/time/internal/cctz/BUILD.bazel
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
							
								
								
									
										329
									
								
								absl/time/internal/cctz/include/cctz/civil_time.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										329
									
								
								absl/time/internal/cctz/include/cctz/civil_time.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										564
									
								
								absl/time/internal/cctz/include/cctz/civil_time_detail.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										564
									
								
								absl/time/internal/cctz/include/cctz/civil_time_detail.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										316
									
								
								absl/time/internal/cctz/include/cctz/time_zone.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								absl/time/internal/cctz/include/cctz/time_zone.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										91
									
								
								absl/time/internal/cctz/include/cctz/zone_info_source.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								absl/time/internal/cctz/include/cctz/zone_info_source.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										90
									
								
								absl/time/internal/cctz/src/civil_time_detail.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								absl/time/internal/cctz/src/civil_time_detail.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										1049
									
								
								absl/time/internal/cctz/src/civil_time_test.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1049
									
								
								absl/time/internal/cctz/src/civil_time_test.cc
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										133
									
								
								absl/time/internal/cctz/src/time_zone_fixed.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								absl/time/internal/cctz/src/time_zone_fixed.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										49
									
								
								absl/time/internal/cctz/src/time_zone_fixed.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								absl/time/internal/cctz/src/time_zone_fixed.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										848
									
								
								absl/time/internal/cctz/src/time_zone_format.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										848
									
								
								absl/time/internal/cctz/src/time_zone_format.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										1408
									
								
								absl/time/internal/cctz/src/time_zone_format_test.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1408
									
								
								absl/time/internal/cctz/src/time_zone_format_test.cc
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										41
									
								
								absl/time/internal/cctz/src/time_zone_if.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								absl/time/internal/cctz/src/time_zone_if.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										70
									
								
								absl/time/internal/cctz/src/time_zone_if.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								absl/time/internal/cctz/src/time_zone_if.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										117
									
								
								absl/time/internal/cctz/src/time_zone_impl.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								absl/time/internal/cctz/src/time_zone_impl.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										97
									
								
								absl/time/internal/cctz/src/time_zone_impl.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								absl/time/internal/cctz/src/time_zone_impl.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										956
									
								
								absl/time/internal/cctz/src/time_zone_info.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										956
									
								
								absl/time/internal/cctz/src/time_zone_info.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										132
									
								
								absl/time/internal/cctz/src/time_zone_info.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								absl/time/internal/cctz/src/time_zone_info.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										156
									
								
								absl/time/internal/cctz/src/time_zone_libc.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								absl/time/internal/cctz/src/time_zone_libc.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										50
									
								
								absl/time/internal/cctz/src/time_zone_libc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								absl/time/internal/cctz/src/time_zone_libc.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										142
									
								
								absl/time/internal/cctz/src/time_zone_lookup.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								absl/time/internal/cctz/src/time_zone_lookup.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										1259
									
								
								absl/time/internal/cctz/src/time_zone_lookup_test.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1259
									
								
								absl/time/internal/cctz/src/time_zone_lookup_test.cc
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										155
									
								
								absl/time/internal/cctz/src/time_zone_posix.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								absl/time/internal/cctz/src/time_zone_posix.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										118
									
								
								absl/time/internal/cctz/src/time_zone_posix.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								absl/time/internal/cctz/src/time_zone_posix.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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_
 | ||||||
							
								
								
									
										117
									
								
								absl/time/internal/cctz/src/tzfile.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								absl/time/internal/cctz/src/tzfile.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 */ | ||||||
							
								
								
									
										70
									
								
								absl/time/internal/cctz/src/zone_info_source.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								absl/time/internal/cctz/src/zone_info_source.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										37
									
								
								absl/time/internal/cctz/testdata/README.zoneinfo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								absl/time/internal/cctz/testdata/README.zoneinfo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 ... | ||||||
							
								
								
									
										1
									
								
								absl/time/internal/cctz/testdata/version
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								absl/time/internal/cctz/testdata/version
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | 2018d-2-g8d1dac0 | ||||||
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Adak
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Adak
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Antigua
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue