Export of internal Abseil changes.
-- aa9e2bff92652605b8244677058be787c872f99c by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 202702969 -- d26c857c203589892a84bc44d789f2a15a60f234 by Abseil Team <absl-team@google.com>: Cleans up the FixedArray code (formatting, renames, etc) without changing the functionality PiperOrigin-RevId: 202538159 GitOrigin-RevId: aa9e2bff92652605b8244677058be787c872f99c Change-Id: I6561257232c6cc8e1cbf51d7e26bae5f8760551e
This commit is contained in:
		
							parent
							
								
									ba8d6cf077
								
							
						
					
					
						commit
						134496a31d
					
				
					 16 changed files with 452 additions and 262 deletions
				
			
		|  | @ -1,4 +1,4 @@ | ||||||
| // Copyright 2017 The Abseil Authors.
 | // Copyright 2018 The Abseil Authors.
 | ||||||
| //
 | //
 | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||||
| // you may not use this file except in compliance with the License.
 | // you may not use this file except in compliance with the License.
 | ||||||
|  | @ -57,13 +57,13 @@ constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1); | ||||||
| // FixedArray
 | // FixedArray
 | ||||||
| // -----------------------------------------------------------------------------
 | // -----------------------------------------------------------------------------
 | ||||||
| //
 | //
 | ||||||
| // A `FixedArray` provides a run-time fixed-size array, allocating small arrays
 | // A `FixedArray` provides a run-time fixed-size array, allocating a small array
 | ||||||
| // inline for efficiency and correctness.
 | // inline for efficiency.
 | ||||||
| //
 | //
 | ||||||
| // Most users should not specify an `inline_elements` argument and let
 | // Most users should not specify an `inline_elements` argument and let
 | ||||||
| // `FixedArray<>` automatically determine the number of elements
 | // `FixedArray` automatically determine the number of elements
 | ||||||
| // to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
 | // to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
 | ||||||
| // `FixedArray<>` implementation will inline arrays of
 | // `FixedArray` implementation will use inline storage for arrays with a
 | ||||||
| // length <= `inline_elements`.
 | // length <= `inline_elements`.
 | ||||||
| //
 | //
 | ||||||
| // Note that a `FixedArray` constructed with a `size_type` argument will
 | // Note that a `FixedArray` constructed with a `size_type` argument will
 | ||||||
|  | @ -84,15 +84,12 @@ class FixedArray { | ||||||
| 
 | 
 | ||||||
|   // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
 |   // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
 | ||||||
|   // but this seems to be mostly pedantic.
 |   // but this seems to be mostly pedantic.
 | ||||||
|   template <typename Iter> |   template <typename Iterator> | ||||||
|   using EnableIfForwardIterator = typename std::enable_if< |   using EnableIfForwardIterator = absl::enable_if_t<std::is_convertible< | ||||||
|       std::is_convertible< |       typename std::iterator_traits<Iterator>::iterator_category, | ||||||
|           typename std::iterator_traits<Iter>::iterator_category, |       std::forward_iterator_tag>::value>; | ||||||
|           std::forward_iterator_tag>::value, |  | ||||||
|       int>::type; |  | ||||||
| 
 | 
 | ||||||
|  public: |  public: | ||||||
|   // For playing nicely with stl:
 |  | ||||||
|   using value_type = T; |   using value_type = T; | ||||||
|   using iterator = T*; |   using iterator = T*; | ||||||
|   using const_iterator = const T*; |   using const_iterator = const T*; | ||||||
|  | @ -114,40 +111,38 @@ class FixedArray { | ||||||
|       : FixedArray(other.begin(), other.end()) {} |       : FixedArray(other.begin(), other.end()) {} | ||||||
| 
 | 
 | ||||||
|   FixedArray(FixedArray&& other) noexcept( |   FixedArray(FixedArray&& other) noexcept( | ||||||
|   // clang-format off
 |       absl::conjunction<absl::allocator_is_nothrow<std::allocator<value_type>>, | ||||||
|       absl::allocator_is_nothrow<std::allocator<value_type>>::value && |                         std::is_nothrow_move_constructible<value_type>>::value) | ||||||
|   // clang-format on
 |  | ||||||
|           std::is_nothrow_move_constructible<value_type>::value) |  | ||||||
|       : FixedArray(std::make_move_iterator(other.begin()), |       : FixedArray(std::make_move_iterator(other.begin()), | ||||||
|                    std::make_move_iterator(other.end())) {} |                    std::make_move_iterator(other.end())) {} | ||||||
| 
 | 
 | ||||||
|   // Creates an array object that can store `n` elements.
 |   // Creates an array object that can store `n` elements.
 | ||||||
|   // Note that trivially constructible elements will be uninitialized.
 |   // Note that trivially constructible elements will be uninitialized.
 | ||||||
|   explicit FixedArray(size_type n) : rep_(n) { |   explicit FixedArray(size_type n) : storage_(n) { | ||||||
|     absl::memory_internal::uninitialized_default_construct_n(rep_.begin(), |     absl::memory_internal::uninitialized_default_construct_n(storage_.begin(), | ||||||
|                                                              size()); |                                                              size()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Creates an array initialized with `n` copies of `val`.
 |   // Creates an array initialized with `n` copies of `val`.
 | ||||||
|   FixedArray(size_type n, const value_type& val) : rep_(n) { |   FixedArray(size_type n, const value_type& val) : storage_(n) { | ||||||
|     std::uninitialized_fill_n(data(), size(), val); |     std::uninitialized_fill_n(data(), size(), val); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Creates an array initialized with the elements from the input
 |   // Creates an array initialized with the elements from the input
 | ||||||
|   // range. The array's size will always be `std::distance(first, last)`.
 |   // range. The array's size will always be `std::distance(first, last)`.
 | ||||||
|   // REQUIRES: Iter must be a forward_iterator or better.
 |   // REQUIRES: Iterator must be a forward_iterator or better.
 | ||||||
|   template <typename Iter, EnableIfForwardIterator<Iter> = 0> |   template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr> | ||||||
|   FixedArray(Iter first, Iter last) : rep_(std::distance(first, last)) { |   FixedArray(Iterator first, Iterator last) | ||||||
|  |       : storage_(std::distance(first, last)) { | ||||||
|     std::uninitialized_copy(first, last, data()); |     std::uninitialized_copy(first, last, data()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Creates the array from an initializer_list.
 |   FixedArray(std::initializer_list<value_type> init_list) | ||||||
|   FixedArray(std::initializer_list<T> init_list) |  | ||||||
|       : FixedArray(init_list.begin(), init_list.end()) {} |       : FixedArray(init_list.begin(), init_list.end()) {} | ||||||
| 
 | 
 | ||||||
|   ~FixedArray() noexcept { |   ~FixedArray() noexcept { | ||||||
|     for (Holder* cur = rep_.begin(); cur != rep_.end(); ++cur) { |     for (const StorageElement& cur : storage_) { | ||||||
|       cur->~Holder(); |       cur.~StorageElement(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -159,7 +154,7 @@ class FixedArray { | ||||||
|   // FixedArray::size()
 |   // FixedArray::size()
 | ||||||
|   //
 |   //
 | ||||||
|   // Returns the length of the fixed array.
 |   // Returns the length of the fixed array.
 | ||||||
|   size_type size() const { return rep_.size(); } |   size_type size() const { return storage_.size(); } | ||||||
| 
 | 
 | ||||||
|   // FixedArray::max_size()
 |   // FixedArray::max_size()
 | ||||||
|   //
 |   //
 | ||||||
|  | @ -184,12 +179,12 @@ class FixedArray { | ||||||
|   //
 |   //
 | ||||||
|   // Returns a const T* pointer to elements of the `FixedArray`. This pointer
 |   // Returns a const T* pointer to elements of the `FixedArray`. This pointer
 | ||||||
|   // can be used to access (but not modify) the contained elements.
 |   // can be used to access (but not modify) the contained elements.
 | ||||||
|   const_pointer data() const { return AsValue(rep_.begin()); } |   const_pointer data() const { return AsValueType(storage_.begin()); } | ||||||
| 
 | 
 | ||||||
|   // Overload of FixedArray::data() to return a T* pointer to elements of the
 |   // Overload of FixedArray::data() to return a T* pointer to elements of the
 | ||||||
|   // fixed array. This pointer can be used to access and modify the contained
 |   // fixed array. This pointer can be used to access and modify the contained
 | ||||||
|   // elements.
 |   // elements.
 | ||||||
|   pointer data() { return AsValue(rep_.begin()); } |   pointer data() { return AsValueType(storage_.begin()); } | ||||||
| 
 | 
 | ||||||
|   // FixedArray::operator[]
 |   // FixedArray::operator[]
 | ||||||
|   //
 |   //
 | ||||||
|  | @ -309,7 +304,7 @@ class FixedArray { | ||||||
|   // FixedArray::fill()
 |   // FixedArray::fill()
 | ||||||
|   //
 |   //
 | ||||||
|   // Assigns the given `value` to all elements in the fixed array.
 |   // Assigns the given `value` to all elements in the fixed array.
 | ||||||
|   void fill(const T& value) { std::fill(begin(), end(), value); } |   void fill(const value_type& val) { std::fill(begin(), end(), val); } | ||||||
| 
 | 
 | ||||||
|   // Relational operators. Equality operators are elementwise using
 |   // Relational operators. Equality operators are elementwise using
 | ||||||
|   // `operator==`, while order operators order FixedArrays lexicographically.
 |   // `operator==`, while order operators order FixedArrays lexicographically.
 | ||||||
|  | @ -339,18 +334,18 @@ class FixedArray { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   // Holder
 |   // StorageElement
 | ||||||
|   //
 |   //
 | ||||||
|   // Wrapper for holding elements of type T for both the case where T is a
 |   // For FixedArrays with a C-style-array value_type, StorageElement is a POD
 | ||||||
|   // C-style array type and the general case where it is not. This is needed for
 |   // wrapper struct called StorageElementWrapper that holds the value_type
 | ||||||
|   // construction and destruction of the entire array regardless of how many
 |   // instance inside. This is needed for construction and destruction of the
 | ||||||
|   // dimensions it has.
 |   // entire array regardless of how many dimensions it has. For all other cases,
 | ||||||
|  |   // StorageElement is just an alias of value_type.
 | ||||||
|   //
 |   //
 | ||||||
|   // Maintainer's Note: The simpler solution would be to simply wrap T in a
 |   // Maintainer's Note: The simpler solution would be to simply wrap value_type
 | ||||||
|   // struct whether it's an array or not: 'struct Holder { T v; };', but
 |   // in a struct whether it's an array or not. That causes some paranoid
 | ||||||
|   // that causes some paranoid diagnostics to misfire about uses of data(),
 |   // diagnostics to misfire, believing that 'data()' returns a pointer to a
 | ||||||
|   // believing that 'data()' (aka '&rep_.begin().v') is a pointer to a single
 |   // single element, rather than the packed array that it really is.
 | ||||||
|   // element, rather than the packed array that it really is.
 |  | ||||||
|   // e.g.:
 |   // e.g.:
 | ||||||
|   //
 |   //
 | ||||||
|   //     FixedArray<char> buf(1);
 |   //     FixedArray<char> buf(1);
 | ||||||
|  | @ -362,115 +357,95 @@ class FixedArray { | ||||||
|   template <typename OuterT = value_type, |   template <typename OuterT = value_type, | ||||||
|             typename InnerT = absl::remove_extent_t<OuterT>, |             typename InnerT = absl::remove_extent_t<OuterT>, | ||||||
|             size_t InnerN = std::extent<OuterT>::value> |             size_t InnerN = std::extent<OuterT>::value> | ||||||
|   struct ArrayHolder { |   struct StorageElementWrapper { | ||||||
|     InnerT array[InnerN]; |     InnerT array[InnerN]; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   using Holder = absl::conditional_t<std::is_array<value_type>::value, |   using StorageElement = | ||||||
|                                      ArrayHolder<value_type>, value_type>; |       absl::conditional_t<std::is_array<value_type>::value, | ||||||
|  |                           StorageElementWrapper<value_type>, value_type>; | ||||||
| 
 | 
 | ||||||
|   static_assert(sizeof(Holder) == sizeof(value_type), ""); |   static pointer AsValueType(pointer ptr) { return ptr; } | ||||||
|   static_assert(alignof(Holder) == alignof(value_type), ""); |   static pointer AsValueType(StorageElementWrapper<value_type>* ptr) { | ||||||
| 
 |  | ||||||
|   static pointer AsValue(pointer ptr) { return ptr; } |  | ||||||
|   static pointer AsValue(ArrayHolder<value_type>* ptr) { |  | ||||||
|     return std::addressof(ptr->array); |     return std::addressof(ptr->array); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // InlineSpace
 |   static_assert(sizeof(StorageElement) == sizeof(value_type), ""); | ||||||
|   //
 |   static_assert(alignof(StorageElement) == alignof(value_type), ""); | ||||||
|   // Allocate some space, not an array of elements of type T, so that we can
 |  | ||||||
|   // skip calling the T constructors and destructors for space we never use.
 |  | ||||||
|   // How many elements should we store inline?
 |  | ||||||
|   //   a. If not specified, use a default of kInlineBytesDefault bytes (This is
 |  | ||||||
|   //   currently 256 bytes, which seems small enough to not cause stack overflow
 |  | ||||||
|   //   or unnecessary stack pollution, while still allowing stack allocation for
 |  | ||||||
|   //   reasonably long character arrays).
 |  | ||||||
|   //   b. Never use 0 length arrays (not ISO C++)
 |  | ||||||
|   //
 |  | ||||||
|   template <size_type N, typename = void> |  | ||||||
|   class InlineSpace { |  | ||||||
|    public: |  | ||||||
|     Holder* data() { return reinterpret_cast<Holder*>(space_.data()); } |  | ||||||
|     void AnnotateConstruct(size_t n) const { Annotate(n, true); } |  | ||||||
|     void AnnotateDestruct(size_t n) const { Annotate(n, false); } |  | ||||||
| 
 | 
 | ||||||
|    private: |   struct NonEmptyInlinedStorage { | ||||||
| #ifndef ADDRESS_SANITIZER |     using StorageElementBuffer = | ||||||
|     void Annotate(size_t, bool) const { } |         absl::aligned_storage_t<sizeof(StorageElement), | ||||||
| #else |                                 alignof(StorageElement)>; | ||||||
|     void Annotate(size_t n, bool creating) const { |     StorageElement* data() { | ||||||
|       if (!n) return; |       return reinterpret_cast<StorageElement*>(inlined_storage_.data()); | ||||||
|       const void* bot = &left_redzone_; |  | ||||||
|       const void* beg = space_.data(); |  | ||||||
|       const void* end = space_.data() + n; |  | ||||||
|       const void* top = &right_redzone_ + 1; |  | ||||||
|       // args: (beg, end, old_mid, new_mid)
 |  | ||||||
|       if (creating) { |  | ||||||
|         ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, top, end); |  | ||||||
|         ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, beg, bot); |  | ||||||
|       } else { |  | ||||||
|         ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, end, top); |  | ||||||
|         ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, bot, beg); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  | #ifdef ADDRESS_SANITIZER | ||||||
|  |     void* RedzoneBegin() { return &redzone_begin_; } | ||||||
|  |     void* RedzoneEnd() { return &redzone_end_ + 1; } | ||||||
| #endif  // ADDRESS_SANITIZER
 | #endif  // ADDRESS_SANITIZER
 | ||||||
| 
 | 
 | ||||||
|     using Buffer = |     void AnnotateConstruct(size_t); | ||||||
|         typename std::aligned_storage<sizeof(Holder), alignof(Holder)>::type; |     void AnnotateDestruct(size_t); | ||||||
| 
 | 
 | ||||||
|     ADDRESS_SANITIZER_REDZONE(left_redzone_); |     ADDRESS_SANITIZER_REDZONE(redzone_begin_); | ||||||
|     std::array<Buffer, N> space_; |     std::array<StorageElementBuffer, inline_elements> inlined_storage_; | ||||||
|     ADDRESS_SANITIZER_REDZONE(right_redzone_); |     ADDRESS_SANITIZER_REDZONE(redzone_end_); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   // specialization when N = 0.
 |   struct EmptyInlinedStorage { | ||||||
|   template <typename U> |     StorageElement* data() { return nullptr; } | ||||||
|   class InlineSpace<0, U> { |     void AnnotateConstruct(size_t) {} | ||||||
|    public: |     void AnnotateDestruct(size_t) {} | ||||||
|     Holder* data() { return nullptr; } |  | ||||||
|     void AnnotateConstruct(size_t) const {} |  | ||||||
|     void AnnotateDestruct(size_t) const {} |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   // Rep
 |   using InlinedStorage = | ||||||
|   //
 |       absl::conditional_t<inline_elements == 0, EmptyInlinedStorage, | ||||||
|   // An instance of Rep manages the inline and out-of-line memory for FixedArray
 |                           NonEmptyInlinedStorage>; | ||||||
|   //
 |  | ||||||
|   class Rep : public InlineSpace<inline_elements> { |  | ||||||
|    public: |  | ||||||
|     explicit Rep(size_type n) : n_(n), p_(MakeHolder(n)) {} |  | ||||||
| 
 | 
 | ||||||
|     ~Rep() noexcept { |   // Storage
 | ||||||
|       if (IsAllocated(size())) { |   //
 | ||||||
|         std::allocator<Holder>().deallocate(p_, n_); |   // An instance of Storage manages the inline and out-of-line memory for
 | ||||||
|       } else { |   // instances of FixedArray. This guarantees that even when construction of
 | ||||||
|  |   // individual elements fails in the FixedArray constructor body, the
 | ||||||
|  |   // destructor for Storage will still be called and out-of-line memory will be
 | ||||||
|  |   // properly deallocated.
 | ||||||
|  |   //
 | ||||||
|  |   class Storage : public InlinedStorage { | ||||||
|  |    public: | ||||||
|  |     explicit Storage(size_type n) : data_(CreateStorage(n)), size_(n) {} | ||||||
|  |     ~Storage() noexcept { | ||||||
|  |       if (UsingInlinedStorage(size())) { | ||||||
|         this->AnnotateDestruct(size()); |         this->AnnotateDestruct(size()); | ||||||
|  |       } else { | ||||||
|  |         std::allocator<StorageElement>().deallocate(begin(), size()); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     Holder* begin() const { return p_; } | 
 | ||||||
|     Holder* end() const { return p_ + n_; } |     size_type size() const { return size_; } | ||||||
|     size_type size() const { return n_; } |     StorageElement* begin() const { return data_; } | ||||||
|  |     StorageElement* end() const { return begin() + size(); } | ||||||
| 
 | 
 | ||||||
|    private: |    private: | ||||||
|     Holder* MakeHolder(size_type n) { |     static bool UsingInlinedStorage(size_type n) { | ||||||
|       if (IsAllocated(n)) { |       return n <= inline_elements; | ||||||
|         return std::allocator<Holder>().allocate(n); |     } | ||||||
|       } else { | 
 | ||||||
|  |     StorageElement* CreateStorage(size_type n) { | ||||||
|  |       if (UsingInlinedStorage(n)) { | ||||||
|         this->AnnotateConstruct(n); |         this->AnnotateConstruct(n); | ||||||
|         return this->data(); |         return InlinedStorage::data(); | ||||||
|  |       } else { | ||||||
|  |         return std::allocator<StorageElement>().allocate(n); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool IsAllocated(size_type n) const { return n > inline_elements; } |     StorageElement* const data_; | ||||||
| 
 |     const size_type size_; | ||||||
|     const size_type n_; |  | ||||||
|     Holder* const p_; |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| 
 |   const Storage storage_; | ||||||
|   // Data members
 |  | ||||||
|   Rep rep_; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename T, size_t N> | template <typename T, size_t N> | ||||||
|  | @ -479,5 +454,25 @@ constexpr size_t FixedArray<T, N>::inline_elements; | ||||||
| template <typename T, size_t N> | template <typename T, size_t N> | ||||||
| constexpr size_t FixedArray<T, N>::kInlineBytesDefault; | constexpr size_t FixedArray<T, N>::kInlineBytesDefault; | ||||||
| 
 | 
 | ||||||
|  | template <typename T, size_t N> | ||||||
|  | void FixedArray<T, N>::NonEmptyInlinedStorage::AnnotateConstruct(size_t n) { | ||||||
|  | #ifdef ADDRESS_SANITIZER | ||||||
|  |   if (!n) return; | ||||||
|  |   ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n); | ||||||
|  |   ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin()); | ||||||
|  | #endif                   // ADDRESS_SANITIZER
 | ||||||
|  |   static_cast<void>(n);  // Mark used when not in asan mode
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <typename T, size_t N> | ||||||
|  | void FixedArray<T, N>::NonEmptyInlinedStorage::AnnotateDestruct(size_t n) { | ||||||
|  | #ifdef ADDRESS_SANITIZER | ||||||
|  |   if (!n) return; | ||||||
|  |   ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd()); | ||||||
|  |   ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data()); | ||||||
|  | #endif                   // ADDRESS_SANITIZER
 | ||||||
|  |   static_cast<void>(n);  // Mark used when not in asan mode
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| }  // namespace absl
 | }  // namespace absl
 | ||||||
| #endif  // ABSL_CONTAINER_FIXED_ARRAY_H_
 | #endif  // ABSL_CONTAINER_FIXED_ARRAY_H_
 | ||||||
|  |  | ||||||
|  | @ -119,7 +119,7 @@ class time_zone { | ||||||
|   // of the given civil-time argument, and the pre, trans, and post
 |   // of the given civil-time argument, and the pre, trans, and post
 | ||||||
|   // members will give the absolute time answers using the pre-transition
 |   // members will give the absolute time answers using the pre-transition
 | ||||||
|   // offset, the transition point itself, and the post-transition offset,
 |   // offset, the transition point itself, and the post-transition offset,
 | ||||||
|   // respectively (all three times are equal if kind == UNIQUE).  If any
 |   // respectively (all three times are equal if kind == UNIQUE). If any
 | ||||||
|   // of these three absolute times is outside the representable range of a
 |   // of these three absolute times is outside the representable range of a
 | ||||||
|   // time_point<seconds> the field is set to its maximum/minimum value.
 |   // time_point<seconds> the field is set to its maximum/minimum value.
 | ||||||
|   //
 |   //
 | ||||||
|  | @ -159,17 +159,79 @@ class time_zone { | ||||||
|   }; |   }; | ||||||
|   civil_lookup lookup(const civil_second& cs) const; |   civil_lookup lookup(const civil_second& cs) const; | ||||||
| 
 | 
 | ||||||
|  |   // Finds the time of the next/previous offset change in this time zone.
 | ||||||
|  |   //
 | ||||||
|  |   // By definition, next_transition(tp, &trans) returns false when tp has
 | ||||||
|  |   // its maximum value, and prev_transition(tp, &trans) returns false
 | ||||||
|  |   // when tp has its minimum value. If the zone has no transitions, the
 | ||||||
|  |   // result will also be false no matter what the argument.
 | ||||||
|  |   //
 | ||||||
|  |   // Otherwise, when tp has its minimum value, next_transition(tp, &trans)
 | ||||||
|  |   // returns true and sets trans to the first recorded transition. Chains
 | ||||||
|  |   // of calls to next_transition()/prev_transition() will eventually return
 | ||||||
|  |   // false, but it is unspecified exactly when next_transition(tp, &trans)
 | ||||||
|  |   // jumps to false, or what time is set by prev_transition(tp, &trans) for
 | ||||||
|  |   // a very distant tp.
 | ||||||
|  |   //
 | ||||||
|  |   // Note: Enumeration of time-zone transitions is for informational purposes
 | ||||||
|  |   // only. Modern time-related code should not care about when offset changes
 | ||||||
|  |   // occur.
 | ||||||
|  |   //
 | ||||||
|  |   // Example:
 | ||||||
|  |   //   cctz::time_zone nyc;
 | ||||||
|  |   //   if (!cctz::load_time_zone("America/New_York", &nyc)) { ... }
 | ||||||
|  |   //   const auto now = std::chrono::system_clock::now();
 | ||||||
|  |   //   auto tp = cctz::time_point<cctz::seconds>::min();
 | ||||||
|  |   //   cctz::time_zone::civil_transition trans;
 | ||||||
|  |   //   while (tp <= now && nyc.next_transition(tp, &trans)) {
 | ||||||
|  |   //     // transition: trans.from -> trans.to
 | ||||||
|  |   //     tp = nyc.lookup(trans.to).trans;
 | ||||||
|  |   //   }
 | ||||||
|  |   struct civil_transition { | ||||||
|  |     civil_second from;  // the civil time we jump from
 | ||||||
|  |     civil_second to;    // the civil time we jump to
 | ||||||
|  |   }; | ||||||
|  |   bool next_transition(const time_point<seconds>& tp, | ||||||
|  |                        civil_transition* trans) const; | ||||||
|  |   template <typename D> | ||||||
|  |   bool next_transition(const time_point<D>& tp, | ||||||
|  |                        civil_transition* trans) const { | ||||||
|  |     return next_transition(detail::split_seconds(tp).first, trans); | ||||||
|  |   } | ||||||
|  |   bool prev_transition(const time_point<seconds>& tp, | ||||||
|  |                        civil_transition* trans) const; | ||||||
|  |   template <typename D> | ||||||
|  |   bool prev_transition(const time_point<D>& tp, | ||||||
|  |                        civil_transition* trans) const { | ||||||
|  |     return prev_transition(detail::split_seconds(tp).first, trans); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // version() and description() provide additional information about the
 | ||||||
|  |   // time zone. The content of each of the returned strings is unspecified,
 | ||||||
|  |   // however, when the IANA Time Zone Database is the underlying data source
 | ||||||
|  |   // the version() std::string will be in the familar form (e.g, "2018e") or
 | ||||||
|  |   // empty when unavailable.
 | ||||||
|  |   //
 | ||||||
|  |   // Note: These functions are for informational or testing purposes only.
 | ||||||
|  |   std::string version() const;  // empty when unknown
 | ||||||
|  |   std::string description() const; | ||||||
|  | 
 | ||||||
|  |   // Relational operators.
 | ||||||
|  |   friend bool operator==(time_zone lhs, time_zone rhs) { | ||||||
|  |     return &lhs.effective_impl() == &rhs.effective_impl(); | ||||||
|  |   } | ||||||
|  |   friend bool operator!=(time_zone lhs, time_zone rhs) { | ||||||
|  |     return !(lhs == rhs); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   class Impl; |   class Impl; | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   explicit time_zone(const Impl* impl) : impl_(impl) {} |   explicit time_zone(const Impl* impl) : impl_(impl) {} | ||||||
|  |   const Impl& effective_impl() const;  // handles implicit UTC
 | ||||||
|   const 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.
 | // 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
 | // If the name is invalid, or some other kind of error occurs, returns
 | ||||||
| // false and "*tz" is set to the UTC time zone.
 | // false and "*tz" is set to the UTC time zone.
 | ||||||
|  | @ -184,6 +246,7 @@ time_zone utc_time_zone(); | ||||||
| time_zone fixed_time_zone(const seconds& offset); | time_zone fixed_time_zone(const seconds& offset); | ||||||
| 
 | 
 | ||||||
| // Returns a time zone representing the local time zone. Falls back to UTC.
 | // Returns a time zone representing the local time zone. Falls back to UTC.
 | ||||||
|  | // Note: local_time_zone.name() may only be something like "localtime".
 | ||||||
| time_zone local_time_zone(); | time_zone local_time_zone(); | ||||||
| 
 | 
 | ||||||
| // Returns the civil time (cctz::civil_second) within the given time zone at
 | // Returns the civil time (cctz::civil_second) within the given time zone at
 | ||||||
|  | @ -227,7 +290,7 @@ bool parse(const std::string&, const std::string&, const time_zone&, | ||||||
| //   - %E*f - Fractional seconds with full precision (a literal '*')
 | //   - %E*f - Fractional seconds with full precision (a literal '*')
 | ||||||
| //   - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
 | //   - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
 | ||||||
| //
 | //
 | ||||||
| // Note that %E0S behaves like %S, and %E0f produces no characters.  In
 | // 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'.
 | // 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
 | // Note that %Y produces as many characters as it takes to fully render the
 | ||||||
|  | @ -254,7 +317,7 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp, | ||||||
| // Parses an input std::string according to the provided format std::string and
 | // Parses an input std::string according to the provided format std::string and
 | ||||||
| // returns the corresponding time_point. Uses strftime()-like formatting
 | // returns the corresponding time_point. Uses strftime()-like formatting
 | ||||||
| // options, with the same extensions as cctz::format(), but with the
 | // 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
 | // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
 | ||||||
| // and %E*z also accept the same inputs.
 | // and %E*z also accept the same inputs.
 | ||||||
| //
 | //
 | ||||||
| // %Y consumes as many numeric characters as it can, so the matching data
 | // %Y consumes as many numeric characters as it can, so the matching data
 | ||||||
|  |  | ||||||
|  | @ -31,6 +31,11 @@ class ZoneInfoSource { | ||||||
| 
 | 
 | ||||||
|   virtual std::size_t Read(void* ptr, std::size_t size) = 0;  // like fread()
 |   virtual std::size_t Read(void* ptr, std::size_t size) = 0;  // like fread()
 | ||||||
|   virtual int Skip(std::size_t offset) = 0;  // like fseek()
 |   virtual int Skip(std::size_t offset) = 0;  // like fseek()
 | ||||||
|  | 
 | ||||||
|  |   // Until the zoneinfo data supports versioning information, we provide
 | ||||||
|  |   // a way for a ZoneInfoSource to indicate it out-of-band.  The default
 | ||||||
|  |   // implementation returns an empty std::string.
 | ||||||
|  |   virtual std::string Version() const; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| }  // namespace cctz
 | }  // namespace cctz
 | ||||||
|  |  | ||||||
|  | @ -754,23 +754,21 @@ void BM_Zone_LoadAllTimeZonesCached(benchmark::State& state) { | ||||||
| } | } | ||||||
| BENCHMARK(BM_Zone_LoadAllTimeZonesCached); | BENCHMARK(BM_Zone_LoadAllTimeZonesCached); | ||||||
| 
 | 
 | ||||||
| void BM_Zone_TimeZoneImplGetImplicit(benchmark::State& state) { | void BM_Zone_TimeZoneEqualityImplicit(benchmark::State& state) { | ||||||
|   cctz::time_zone tz;  // implicit UTC
 |   cctz::time_zone tz;  // implicit UTC
 | ||||||
|   cctz::time_zone::Impl::get(tz); |  | ||||||
|   while (state.KeepRunning()) { |   while (state.KeepRunning()) { | ||||||
|     cctz::time_zone::Impl::get(tz); |     benchmark::DoNotOptimize(tz == tz); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| BENCHMARK(BM_Zone_TimeZoneImplGetImplicit); | BENCHMARK(BM_Zone_TimeZoneEqualityImplicit); | ||||||
| 
 | 
 | ||||||
| void BM_Zone_TimeZoneImplGetExplicit(benchmark::State& state) { | void BM_Zone_TimeZoneEqualityExplicit(benchmark::State& state) { | ||||||
|   cctz::time_zone tz = cctz::utc_time_zone();  // explicit UTC
 |   cctz::time_zone tz = cctz::utc_time_zone();  // explicit UTC
 | ||||||
|   cctz::time_zone::Impl::get(tz); |  | ||||||
|   while (state.KeepRunning()) { |   while (state.KeepRunning()) { | ||||||
|     cctz::time_zone::Impl::get(tz); |     benchmark::DoNotOptimize(tz == tz); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| BENCHMARK(BM_Zone_TimeZoneImplGetExplicit); | BENCHMARK(BM_Zone_TimeZoneEqualityExplicit); | ||||||
| 
 | 
 | ||||||
| void BM_Zone_UTCTimeZone(benchmark::State& state) { | void BM_Zone_UTCTimeZone(benchmark::State& state) { | ||||||
|   cctz::time_zone tz; |   cctz::time_zone tz; | ||||||
|  |  | ||||||
|  | @ -141,6 +141,9 @@ char* Format02d(char* ep, int v) { | ||||||
| 
 | 
 | ||||||
| // Formats a UTC offset, like +00:00.
 | // Formats a UTC offset, like +00:00.
 | ||||||
| char* FormatOffset(char* ep, int offset, const char* mode) { | char* FormatOffset(char* ep, int offset, const char* mode) { | ||||||
|  |   // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
 | ||||||
|  |   // generate a "negative zero" when we're formatting a zero offset
 | ||||||
|  |   // as the result of a failed load_time_zone().
 | ||||||
|   char sign = '+'; |   char sign = '+'; | ||||||
|   if (offset < 0) { |   if (offset < 0) { | ||||||
|     offset = -offset;  // bounded by 24h so no overflow
 |     offset = -offset;  // bounded by 24h so no overflow
 | ||||||
|  |  | ||||||
|  | @ -64,6 +64,17 @@ void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt, | ||||||
|   EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz)); |   EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // These tests sometimes run on platforms that have zoneinfo data so old
 | ||||||
|  | // that the transition we are attempting to check does not exist, most
 | ||||||
|  | // notably Android emulators.  Fortunately, AndroidZoneInfoSource supports
 | ||||||
|  | // time_zone::version() so, in cases where we've learned that it matters,
 | ||||||
|  | // we can make the check conditionally.
 | ||||||
|  | int VersionCmp(time_zone tz, const std::string& target) { | ||||||
|  |   std::string version = tz.version(); | ||||||
|  |   if (version.empty() && !target.empty()) return 1;  // unknown > known
 | ||||||
|  |   return version.compare(target); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| }  // namespace
 | }  // namespace
 | ||||||
| 
 | 
 | ||||||
| //
 | //
 | ||||||
|  | @ -453,8 +464,8 @@ TEST(Format, ExtendedSecondOffset) { | ||||||
|   EXPECT_TRUE(load_time_zone("America/New_York", &tz)); |   EXPECT_TRUE(load_time_zone("America/New_York", &tz)); | ||||||
|   tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc); |   tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc); | ||||||
|   if (tz.lookup(tp).offset == -5 * 60 * 60) { |   if (tz.lookup(tp).offset == -5 * 60 * 60) { | ||||||
|     // We're likely dealing with zoneinfo that doesn't support really old
 |     // It looks like the tzdata is only 32 bit (probably macOS),
 | ||||||
|     // timestamps, so America/New_York never looks to be on local mean time.
 |     // which bottoms out at 1901-12-13T20:45:52+00:00.
 | ||||||
|   } else { |   } else { | ||||||
|     TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02"); |     TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02"); | ||||||
|     TestFormatSpecifier(tp, tz, "%Ez", "-04:56"); |     TestFormatSpecifier(tp, tz, "%Ez", "-04:56"); | ||||||
|  | @ -464,12 +475,10 @@ TEST(Format, ExtendedSecondOffset) { | ||||||
| 
 | 
 | ||||||
|   EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz)); |   EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz)); | ||||||
|   tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc); |   tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc); | ||||||
| #if defined(__ANDROID__) && __ANDROID_API__ < 25 |   if (VersionCmp(tz, "2016g") >= 0) { | ||||||
|   // Only Android 'N'.1 and beyond have this tz2016g transition.
 |     TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19"); | ||||||
| #else |     TestFormatSpecifier(tp, tz, "%Ez", "+04:31"); | ||||||
|   TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19"); |   } | ||||||
|   TestFormatSpecifier(tp, tz, "%Ez", "+04:31"); |  | ||||||
| #endif |  | ||||||
|   tp += chrono::seconds(1); |   tp += chrono::seconds(1); | ||||||
|   TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00"); |   TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -41,9 +41,13 @@ class TimeZoneIf { | ||||||
|   virtual time_zone::civil_lookup MakeTime( |   virtual time_zone::civil_lookup MakeTime( | ||||||
|       const civil_second& cs) const = 0; |       const civil_second& cs) const = 0; | ||||||
| 
 | 
 | ||||||
|  |   virtual bool NextTransition(const time_point<seconds>& tp, | ||||||
|  |                               time_zone::civil_transition* trans) const = 0; | ||||||
|  |   virtual bool PrevTransition(const time_point<seconds>& tp, | ||||||
|  |                               time_zone::civil_transition* trans) const = 0; | ||||||
|  | 
 | ||||||
|  |   virtual std::string Version() const = 0; | ||||||
|   virtual std::string Description() const = 0; |   virtual std::string Description() const = 0; | ||||||
|   virtual bool NextTransition(time_point<seconds>* tp) const = 0; |  | ||||||
|   virtual bool PrevTransition(time_point<seconds>* tp) const = 0; |  | ||||||
| 
 | 
 | ||||||
|  protected: |  protected: | ||||||
|   TimeZoneIf() {} |   TimeZoneIf() {} | ||||||
|  |  | ||||||
|  | @ -83,15 +83,6 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { | ||||||
|   return impl != utc_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() { | void time_zone::Impl::ClearTimeZoneMapTestOnly() { | ||||||
|   std::lock_guard<std::mutex> lock(time_zone_mutex); |   std::lock_guard<std::mutex> lock(time_zone_mutex); | ||||||
|   if (time_zone_map != nullptr) { |   if (time_zone_map != nullptr) { | ||||||
|  |  | ||||||
|  | @ -37,15 +37,15 @@ class time_zone::Impl { | ||||||
|   // some other kind of error occurs. Note that loading "UTC" never fails.
 |   // some other kind of error occurs. Note that loading "UTC" never fails.
 | ||||||
|   static bool LoadTimeZone(const std::string& name, time_zone* tz); |   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
 |   // Clears the map of cached time zones.  Primarily for use in benchmarks
 | ||||||
|   // that gauge the performance of loading/parsing the time-zone data.
 |   // that gauge the performance of loading/parsing the time-zone data.
 | ||||||
|   static void ClearTimeZoneMapTestOnly(); |   static void ClearTimeZoneMapTestOnly(); | ||||||
| 
 | 
 | ||||||
|   // The primary key is the time-zone ID (e.g., "America/New_York").
 |   // The primary key is the time-zone ID (e.g., "America/New_York").
 | ||||||
|   const std::string& name() const { return name_; } |   const std::string& Name() const { | ||||||
|  |     // TODO: It would nice if the zoneinfo data included the zone name.
 | ||||||
|  |     return name_; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   // Breaks a time_point down to civil-time components in this time zone.
 |   // Breaks a time_point down to civil-time components in this time zone.
 | ||||||
|   time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const { |   time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const { | ||||||
|  | @ -59,28 +59,22 @@ class time_zone::Impl { | ||||||
|     return zone_->MakeTime(cs); |     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.
 |   // Finds the time of the next/previous offset change in this time zone.
 | ||||||
|   //
 |   bool NextTransition(const time_point<seconds>& tp, | ||||||
|   // By definition, NextTransition(&tp) returns false when tp has its
 |                       time_zone::civil_transition* trans) const { | ||||||
|   // maximum value, and PrevTransition(&tp) returns false when tp has its
 |     return zone_->NextTransition(tp, trans); | ||||||
|   // 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<seconds>* tp) const { |  | ||||||
|     return zone_->NextTransition(tp); |  | ||||||
|   } |   } | ||||||
|   bool PrevTransition(time_point<seconds>* tp) const { |   bool PrevTransition(const time_point<seconds>& tp, | ||||||
|     return zone_->PrevTransition(tp); |                       time_zone::civil_transition* trans) const { | ||||||
|  |     return zone_->PrevTransition(tp, trans); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // Returns an implementation-defined version std::string for this time zone.
 | ||||||
|  |   std::string Version() const { return zone_->Version(); } | ||||||
|  | 
 | ||||||
|  |   // Returns an implementation-defined description of this time zone.
 | ||||||
|  |   std::string Description() const { return zone_->Description(); } | ||||||
|  | 
 | ||||||
|  private: |  private: | ||||||
|   explicit Impl(const std::string& name); |   explicit Impl(const std::string& name); | ||||||
|   static const Impl* UTCImpl(); |   static const Impl* UTCImpl(); | ||||||
|  |  | ||||||
|  | @ -186,14 +186,13 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { | ||||||
|   tt.is_dst = false; |   tt.is_dst = false; | ||||||
|   tt.abbr_index = 0; |   tt.abbr_index = 0; | ||||||
| 
 | 
 | ||||||
|   // We temporarily add some redundant, contemporary (2012 through 2021)
 |   // We temporarily add some redundant, contemporary (2013 through 2023)
 | ||||||
|   // transitions for performance reasons.  See TimeZoneInfo::LocalTime().
 |   // transitions for performance reasons.  See TimeZoneInfo::LocalTime().
 | ||||||
|   // TODO: Fix the performance issue and remove the extra transitions.
 |   // TODO: Fix the performance issue and remove the extra transitions.
 | ||||||
|   transitions_.clear(); |   transitions_.clear(); | ||||||
|   transitions_.reserve(12); |   transitions_.reserve(12); | ||||||
|   for (const std::int_fast64_t unix_time : { |   for (const std::int_fast64_t unix_time : { | ||||||
|            -(1LL << 59),  // BIG_BANG
 |            -(1LL << 59),  // BIG_BANG
 | ||||||
|            1325376000LL,  // 2012-01-01T00:00:00+00:00
 |  | ||||||
|            1356998400LL,  // 2013-01-01T00:00:00+00:00
 |            1356998400LL,  // 2013-01-01T00:00:00+00:00
 | ||||||
|            1388534400LL,  // 2014-01-01T00:00:00+00:00
 |            1388534400LL,  // 2014-01-01T00:00:00+00:00
 | ||||||
|            1420070400LL,  // 2015-01-01T00:00:00+00:00
 |            1420070400LL,  // 2015-01-01T00:00:00+00:00
 | ||||||
|  | @ -203,6 +202,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { | ||||||
|            1546300800LL,  // 2019-01-01T00:00:00+00:00
 |            1546300800LL,  // 2019-01-01T00:00:00+00:00
 | ||||||
|            1577836800LL,  // 2020-01-01T00:00:00+00:00
 |            1577836800LL,  // 2020-01-01T00:00:00+00:00
 | ||||||
|            1609459200LL,  // 2021-01-01T00:00:00+00:00
 |            1609459200LL,  // 2021-01-01T00:00:00+00:00
 | ||||||
|  |            1640995200LL,  // 2022-01-01T00:00:00+00:00
 | ||||||
|  |            1672531200LL,  // 2023-01-01T00:00:00+00:00
 | ||||||
|            2147483647LL,  // 2^31 - 1
 |            2147483647LL,  // 2^31 - 1
 | ||||||
|        }) { |        }) { | ||||||
|     Transition& tr(*transitions_.emplace(transitions_.end())); |     Transition& tr(*transitions_.emplace(transitions_.end())); | ||||||
|  | @ -519,6 +520,13 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { | ||||||
| 
 | 
 | ||||||
|   // We don't check for EOF so that we're forwards compatible.
 |   // We don't check for EOF so that we're forwards compatible.
 | ||||||
| 
 | 
 | ||||||
|  |   // If we did not find version information during the standard loading
 | ||||||
|  |   // process (as of tzh_version '3' that is unsupported), then ask the
 | ||||||
|  |   // ZoneInfoSource for any out-of-bound version std::string it may be privy to.
 | ||||||
|  |   if (version_.empty()) { | ||||||
|  |     version_ = zip->Version(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   // Trim redundant transitions. zic may have added these to work around
 |   // Trim redundant transitions. zic may have added these to work around
 | ||||||
|   // differences between the glibc and reference implementations (see
 |   // differences between the glibc and reference implementations (see
 | ||||||
|   // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071).
 |   // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071).
 | ||||||
|  | @ -605,6 +613,10 @@ class FileZoneInfoSource : public ZoneInfoSource { | ||||||
|     if (rc == 0) len_ -= offset; |     if (rc == 0) len_ -= offset; | ||||||
|     return rc; |     return rc; | ||||||
|   } |   } | ||||||
|  |   std::string Version() const override { | ||||||
|  |     // TODO: It would nice if the zoneinfo data included the tzdb version.
 | ||||||
|  |     return std::string(); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|  protected: |  protected: | ||||||
|   explicit FileZoneInfoSource( |   explicit FileZoneInfoSource( | ||||||
|  | @ -654,14 +666,15 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open( | ||||||
|   return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length)); |   return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if defined(__ANDROID__) |  | ||||||
| class AndroidZoneInfoSource : public FileZoneInfoSource { | class AndroidZoneInfoSource : public FileZoneInfoSource { | ||||||
|  public: |  public: | ||||||
|   static std::unique_ptr<ZoneInfoSource> Open(const std::string& name); |   static std::unique_ptr<ZoneInfoSource> Open(const std::string& name); | ||||||
|  |   std::string Version() const override { return version_; } | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   explicit AndroidZoneInfoSource(FILE* fp, std::size_t len) |   explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers) | ||||||
|       : FileZoneInfoSource(fp, len) {} |       : FileZoneInfoSource(fp, len), version_(vers) {} | ||||||
|  |   std::string version_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( | std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( | ||||||
|  | @ -669,6 +682,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( | ||||||
|   // Use of the "file:" prefix is intended for testing purposes only.
 |   // Use of the "file:" prefix is intended for testing purposes only.
 | ||||||
|   if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5)); |   if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5)); | ||||||
| 
 | 
 | ||||||
|  | #if defined(__ANDROID__) | ||||||
|   // See Android's libc/tzcode/bionic.cpp for additional information.
 |   // See Android's libc/tzcode/bionic.cpp for additional information.
 | ||||||
|   for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", |   for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", | ||||||
|                              "/system/usr/share/zoneinfo/tzdata"}) { |                              "/system/usr/share/zoneinfo/tzdata"}) { | ||||||
|  | @ -678,6 +692,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( | ||||||
|     char hbuf[24];  // covers header.zonetab_offset too
 |     char hbuf[24];  // covers header.zonetab_offset too
 | ||||||
|     if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue; |     if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue; | ||||||
|     if (strncmp(hbuf, "tzdata", 6) != 0) continue; |     if (strncmp(hbuf, "tzdata", 6) != 0) continue; | ||||||
|  |     const char* vers = (hbuf[11] == '\0') ? hbuf + 6 : ""; | ||||||
|     const std::int_fast32_t index_offset = Decode32(hbuf + 12); |     const std::int_fast32_t index_offset = Decode32(hbuf + 12); | ||||||
|     const std::int_fast32_t data_offset = Decode32(hbuf + 16); |     const std::int_fast32_t data_offset = Decode32(hbuf + 16); | ||||||
|     if (index_offset < 0 || data_offset < index_offset) continue; |     if (index_offset < 0 || data_offset < index_offset) continue; | ||||||
|  | @ -698,13 +713,13 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( | ||||||
|       if (strcmp(name.c_str(), ebuf) == 0) { |       if (strcmp(name.c_str(), ebuf) == 0) { | ||||||
|         if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break; |         if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break; | ||||||
|         return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource( |         return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource( | ||||||
|             fp.release(), static_cast<std::size_t>(length))); |             fp.release(), static_cast<std::size_t>(length), vers)); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | #endif  // __ANDROID__
 | ||||||
|   return nullptr; |   return nullptr; | ||||||
| } | } | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| }  // namespace
 | }  // namespace
 | ||||||
| 
 | 
 | ||||||
|  | @ -722,9 +737,7 @@ bool TimeZoneInfo::Load(const std::string& name) { | ||||||
|   auto zip = cctz_extension::zone_info_source_factory( |   auto zip = cctz_extension::zone_info_source_factory( | ||||||
|       name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> { |       name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> { | ||||||
|         if (auto zip = FileZoneInfoSource::Open(name)) return zip; |         if (auto zip = FileZoneInfoSource::Open(name)) return zip; | ||||||
| #if defined(__ANDROID__) |  | ||||||
|         if (auto zip = AndroidZoneInfoSource::Open(name)) return zip; |         if (auto zip = AndroidZoneInfoSource::Open(name)) return zip; | ||||||
| #endif |  | ||||||
|         return nullptr; |         return nullptr; | ||||||
|       }); |       }); | ||||||
|   return zip != nullptr && Load(name, zip.get()); |   return zip != nullptr && Load(name, zip.get()); | ||||||
|  | @ -885,17 +898,20 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const { | ||||||
|   return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); |   return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::string TimeZoneInfo::Version() const { | ||||||
|  |   return version_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| std::string TimeZoneInfo::Description() const { | std::string TimeZoneInfo::Description() const { | ||||||
|   std::ostringstream oss; |   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 << "#trans=" << transitions_.size(); | ||||||
|   oss << " #types=" << transition_types_.size(); |   oss << " #types=" << transition_types_.size(); | ||||||
|   oss << " spec='" << future_spec_ << "'"; |   oss << " spec='" << future_spec_ << "'"; | ||||||
|   return oss.str(); |   return oss.str(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool TimeZoneInfo::NextTransition(time_point<seconds>* tp) const { | bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp, | ||||||
|  |                                   time_zone::civil_transition* trans) const { | ||||||
|   if (transitions_.empty()) return false; |   if (transitions_.empty()) return false; | ||||||
|   const Transition* begin = &transitions_[0]; |   const Transition* begin = &transitions_[0]; | ||||||
|   const Transition* end = begin + transitions_.size(); |   const Transition* end = begin + transitions_.size(); | ||||||
|  | @ -904,22 +920,24 @@ bool TimeZoneInfo::NextTransition(time_point<seconds>* tp) const { | ||||||
|     // really a sentinel, not a transition.  See tz/zic.c.
 |     // really a sentinel, not a transition.  See tz/zic.c.
 | ||||||
|     ++begin; |     ++begin; | ||||||
|   } |   } | ||||||
|   std::int_fast64_t unix_time = ToUnixSeconds(*tp); |   std::int_fast64_t unix_time = ToUnixSeconds(tp); | ||||||
|   const Transition target = { unix_time }; |   const Transition target = { unix_time }; | ||||||
|   const Transition* tr = std::upper_bound(begin, end, target, |   const Transition* tr = std::upper_bound(begin, end, target, | ||||||
|                                           Transition::ByUnixTime()); |                                           Transition::ByUnixTime()); | ||||||
|   if (tr != begin) {  // skip no-op transitions
 |   for (; tr != end; ++tr) {  // skip no-op transitions
 | ||||||
|     for (; tr != end; ++tr) { |     std::uint_fast8_t prev_type_index = | ||||||
|       if (!EquivTransitions(tr[-1].type_index, tr[0].type_index)) break; |         (tr == begin) ? default_transition_type_ : tr[-1].type_index; | ||||||
|     } |     if (!EquivTransitions(prev_type_index, tr[0].type_index)) break; | ||||||
|   } |   } | ||||||
|   // When tr == end we return false, ignoring future_spec_.
 |   // When tr == end we return false, ignoring future_spec_.
 | ||||||
|   if (tr == end) return false; |   if (tr == end) return false; | ||||||
|   *tp = FromUnixSeconds(tr->unix_time); |   trans->from = tr->prev_civil_sec + 1; | ||||||
|  |   trans->to = tr->civil_sec; | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const { | bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp, | ||||||
|  |                                   time_zone::civil_transition* trans) const { | ||||||
|   if (transitions_.empty()) return false; |   if (transitions_.empty()) return false; | ||||||
|   const Transition* begin = &transitions_[0]; |   const Transition* begin = &transitions_[0]; | ||||||
|   const Transition* end = begin + transitions_.size(); |   const Transition* end = begin + transitions_.size(); | ||||||
|  | @ -928,11 +946,12 @@ bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const { | ||||||
|     // really a sentinel, not a transition.  See tz/zic.c.
 |     // really a sentinel, not a transition.  See tz/zic.c.
 | ||||||
|     ++begin; |     ++begin; | ||||||
|   } |   } | ||||||
|   std::int_fast64_t unix_time = ToUnixSeconds(*tp); |   std::int_fast64_t unix_time = ToUnixSeconds(tp); | ||||||
|   if (FromUnixSeconds(unix_time) != *tp) { |   if (FromUnixSeconds(unix_time) != tp) { | ||||||
|     if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) { |     if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) { | ||||||
|       if (end == begin) return false;  // Ignore future_spec_.
 |       if (end == begin) return false;  // Ignore future_spec_.
 | ||||||
|       *tp = FromUnixSeconds((--end)->unix_time); |       trans->from = (--end)->prev_civil_sec + 1; | ||||||
|  |       trans->to = end->civil_sec; | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|     unix_time += 1;  // ceils
 |     unix_time += 1;  // ceils
 | ||||||
|  | @ -940,14 +959,15 @@ bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const { | ||||||
|   const Transition target = { unix_time }; |   const Transition target = { unix_time }; | ||||||
|   const Transition* tr = std::lower_bound(begin, end, target, |   const Transition* tr = std::lower_bound(begin, end, target, | ||||||
|                                           Transition::ByUnixTime()); |                                           Transition::ByUnixTime()); | ||||||
|   if (tr != begin) {  // skip no-op transitions
 |   for (; tr != begin; --tr) {  // skip no-op transitions
 | ||||||
|     for (; tr - 1 != begin; --tr) { |     std::uint_fast8_t prev_type_index = | ||||||
|       if (!EquivTransitions(tr[-2].type_index, tr[-1].type_index)) break; |         (tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index; | ||||||
|     } |     if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break; | ||||||
|   } |   } | ||||||
|   // When tr == end we return the "last" transition, ignoring future_spec_.
 |   // When tr == end we return the "last" transition, ignoring future_spec_.
 | ||||||
|   if (tr == begin) return false; |   if (tr == begin) return false; | ||||||
|   *tp = FromUnixSeconds((--tr)->unix_time); |   trans->from = (--tr)->prev_civil_sec + 1; | ||||||
|  |   trans->to = tr->civil_sec; | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -74,9 +74,12 @@ class TimeZoneInfo : public TimeZoneIf { | ||||||
|       const time_point<seconds>& tp) const override; |       const time_point<seconds>& tp) const override; | ||||||
|   time_zone::civil_lookup MakeTime( |   time_zone::civil_lookup MakeTime( | ||||||
|       const civil_second& cs) const override; |       const civil_second& cs) const override; | ||||||
|  |   bool NextTransition(const time_point<seconds>& tp, | ||||||
|  |                       time_zone::civil_transition* trans) const override; | ||||||
|  |   bool PrevTransition(const time_point<seconds>& tp, | ||||||
|  |                       time_zone::civil_transition* trans) const override; | ||||||
|  |   std::string Version() const override; | ||||||
|   std::string Description() const override; |   std::string Description() const override; | ||||||
|   bool NextTransition(time_point<seconds>* tp) const override; |  | ||||||
|   bool PrevTransition(time_point<seconds>* tp) const override; |  | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   struct Header {  // counts of:
 |   struct Header {  // counts of:
 | ||||||
|  | @ -114,6 +117,7 @@ class TimeZoneInfo : public TimeZoneIf { | ||||||
|   std::uint_fast8_t default_transition_type_;  // for before first transition
 |   std::uint_fast8_t default_transition_type_;  // for before first transition
 | ||||||
|   std::string abbreviations_;  // all the NUL-terminated abbreviations
 |   std::string abbreviations_;  // all the NUL-terminated abbreviations
 | ||||||
| 
 | 
 | ||||||
|  |   std::string version_;      // the tzdata version if available
 | ||||||
|   std::string future_spec_;  // for after the last zic transition
 |   std::string future_spec_;  // for after the last zic transition
 | ||||||
|   bool extended_;            // future_spec_ was used to generate transitions
 |   bool extended_;            // future_spec_ was used to generate transitions
 | ||||||
|   year_t last_year_;         // the final year of the generated transitions
 |   year_t last_year_;         // the final year of the generated transitions
 | ||||||
|  |  | ||||||
|  | @ -139,18 +139,24 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { | ||||||
|   return cl; |   return cl; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool TimeZoneLibC::NextTransition(const time_point<seconds>& tp, | ||||||
|  |                                   time_zone::civil_transition* trans) const { | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TimeZoneLibC::PrevTransition(const time_point<seconds>& tp, | ||||||
|  |                                   time_zone::civil_transition* trans) const { | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string TimeZoneLibC::Version() const { | ||||||
|  |   return std::string();  // unknown
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| std::string TimeZoneLibC::Description() const { | std::string TimeZoneLibC::Description() const { | ||||||
|   return local_ ? "localtime" : "UTC"; |   return local_ ? "localtime" : "UTC"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool TimeZoneLibC::NextTransition(time_point<seconds>* tp) const { |  | ||||||
|   return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool TimeZoneLibC::PrevTransition(time_point<seconds>* tp) const { |  | ||||||
|   return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| }  // namespace cctz
 | }  // namespace cctz
 | ||||||
| }  // namespace time_internal
 | }  // namespace time_internal
 | ||||||
| }  // namespace absl
 | }  // namespace absl
 | ||||||
|  |  | ||||||
|  | @ -35,9 +35,12 @@ class TimeZoneLibC : public TimeZoneIf { | ||||||
|       const time_point<seconds>& tp) const override; |       const time_point<seconds>& tp) const override; | ||||||
|   time_zone::civil_lookup MakeTime( |   time_zone::civil_lookup MakeTime( | ||||||
|       const civil_second& cs) const override; |       const civil_second& cs) const override; | ||||||
|  |   bool NextTransition(const time_point<seconds>& tp, | ||||||
|  |                       time_zone::civil_transition* trans) const override; | ||||||
|  |   bool PrevTransition(const time_point<seconds>& tp, | ||||||
|  |                       time_zone::civil_transition* trans) const override; | ||||||
|  |   std::string Version() const override; | ||||||
|   std::string Description() const override; |   std::string Description() const override; | ||||||
|   bool NextTransition(time_point<seconds>* tp) const override; |  | ||||||
|   bool PrevTransition(time_point<seconds>* tp) const override; |  | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   const bool local_;  // localtime or UTC
 |   const bool local_;  // localtime or UTC
 | ||||||
|  |  | ||||||
|  | @ -61,20 +61,43 @@ int __system_property_get(const char* name, char* value) { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| std::string time_zone::name() const { | std::string time_zone::name() const { | ||||||
|   return time_zone::Impl::get(*this).name(); |   return effective_impl().Name(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| time_zone::absolute_lookup time_zone::lookup( | time_zone::absolute_lookup time_zone::lookup( | ||||||
|     const time_point<seconds>& tp) const { |     const time_point<seconds>& tp) const { | ||||||
|   return time_zone::Impl::get(*this).BreakTime(tp); |   return effective_impl().BreakTime(tp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const { | time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const { | ||||||
|   return time_zone::Impl::get(*this).MakeTime(cs); |   return effective_impl().MakeTime(cs); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool operator==(time_zone lhs, time_zone rhs) { | bool time_zone::next_transition(const time_point<seconds>& tp, | ||||||
|   return &time_zone::Impl::get(lhs) == &time_zone::Impl::get(rhs); |                                 civil_transition* trans) const { | ||||||
|  |   return effective_impl().NextTransition(tp, trans); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool time_zone::prev_transition(const time_point<seconds>& tp, | ||||||
|  |                                 civil_transition* trans) const { | ||||||
|  |   return effective_impl().PrevTransition(tp, trans); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string time_zone::version() const { | ||||||
|  |   return effective_impl().Version(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string time_zone::description() const { | ||||||
|  |   return effective_impl().Description(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const time_zone::Impl& time_zone::effective_impl() const { | ||||||
|  |   if (impl_ == nullptr) { | ||||||
|  |     // Dereferencing an implicit-UTC time_zone is expected to be
 | ||||||
|  |     // rare, so we don't mind paying a small synchronization cost.
 | ||||||
|  |     return *time_zone::Impl::UTC().impl_; | ||||||
|  |   } | ||||||
|  |   return *impl_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool load_time_zone(const std::string& name, time_zone* tz) { | bool load_time_zone(const std::string& name, time_zone* tz) { | ||||||
|  |  | ||||||
|  | @ -651,6 +651,17 @@ time_zone LoadZone(const std::string& name) { | ||||||
|     /* EXPECT_STREQ(zone, al.abbr); */                            \ |     /* EXPECT_STREQ(zone, al.abbr); */                            \ | ||||||
|   } while (0) |   } while (0) | ||||||
| 
 | 
 | ||||||
|  | // These tests sometimes run on platforms that have zoneinfo data so old
 | ||||||
|  | // that the transition we are attempting to check does not exist, most
 | ||||||
|  | // notably Android emulators.  Fortunately, AndroidZoneInfoSource supports
 | ||||||
|  | // time_zone::version() so, in cases where we've learned that it matters,
 | ||||||
|  | // we can make the check conditionally.
 | ||||||
|  | int VersionCmp(time_zone tz, const std::string& target) { | ||||||
|  |   std::string version = tz.version(); | ||||||
|  |   if (version.empty() && !target.empty()) return 1;  // unknown > known
 | ||||||
|  |   return version.compare(target); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| }  // namespace
 | }  // namespace
 | ||||||
| 
 | 
 | ||||||
| TEST(TimeZones, LoadZonesConcurrently) { | TEST(TimeZones, LoadZonesConcurrently) { | ||||||
|  | @ -981,6 +992,69 @@ TEST(MakeTime, SysSecondsLimits) { | ||||||
|   EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); |   EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | TEST(NextTransition, UTC) { | ||||||
|  |   const auto tz = utc_time_zone(); | ||||||
|  |   time_zone::civil_transition trans; | ||||||
|  | 
 | ||||||
|  |   auto tp = time_point<absl::time_internal::cctz::seconds>::min(); | ||||||
|  |   EXPECT_FALSE(tz.next_transition(tp, &trans)); | ||||||
|  | 
 | ||||||
|  |   tp = time_point<absl::time_internal::cctz::seconds>::max(); | ||||||
|  |   EXPECT_FALSE(tz.next_transition(tp, &trans)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(PrevTransition, UTC) { | ||||||
|  |   const auto tz = utc_time_zone(); | ||||||
|  |   time_zone::civil_transition trans; | ||||||
|  | 
 | ||||||
|  |   auto tp = time_point<absl::time_internal::cctz::seconds>::max(); | ||||||
|  |   EXPECT_FALSE(tz.prev_transition(tp, &trans)); | ||||||
|  | 
 | ||||||
|  |   tp = time_point<absl::time_internal::cctz::seconds>::min(); | ||||||
|  |   EXPECT_FALSE(tz.prev_transition(tp, &trans)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(NextTransition, AmericaNewYork) { | ||||||
|  |   const auto tz = LoadZone("America/New_York"); | ||||||
|  |   time_zone::civil_transition trans; | ||||||
|  | 
 | ||||||
|  |   auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz); | ||||||
|  |   EXPECT_TRUE(tz.next_transition(tp, &trans)); | ||||||
|  |   EXPECT_EQ(civil_second(2018, 11, 4, 2, 0, 0), trans.from); | ||||||
|  |   EXPECT_EQ(civil_second(2018, 11, 4, 1, 0, 0), trans.to); | ||||||
|  | 
 | ||||||
|  |   tp = time_point<absl::time_internal::cctz::seconds>::max(); | ||||||
|  |   EXPECT_FALSE(tz.next_transition(tp, &trans)); | ||||||
|  | 
 | ||||||
|  |   tp = time_point<absl::time_internal::cctz::seconds>::min(); | ||||||
|  |   EXPECT_TRUE(tz.next_transition(tp, &trans)); | ||||||
|  |   if (trans.from == civil_second(1918, 3, 31, 2, 0, 0)) { | ||||||
|  |     // It looks like the tzdata is only 32 bit (probably macOS),
 | ||||||
|  |     // which bottoms out at 1901-12-13T20:45:52+00:00.
 | ||||||
|  |     EXPECT_EQ(civil_second(1918, 3, 31, 3, 0, 0), trans.to); | ||||||
|  |   } else { | ||||||
|  |     EXPECT_EQ(civil_second(1883, 11, 18, 12, 3, 58), trans.from); | ||||||
|  |     EXPECT_EQ(civil_second(1883, 11, 18, 12, 0, 0), trans.to); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(PrevTransition, AmericaNewYork) { | ||||||
|  |   const auto tz = LoadZone("America/New_York"); | ||||||
|  |   time_zone::civil_transition trans; | ||||||
|  | 
 | ||||||
|  |   auto tp = convert(civil_second(2018, 6, 30, 0, 0, 0), tz); | ||||||
|  |   EXPECT_TRUE(tz.prev_transition(tp, &trans)); | ||||||
|  |   EXPECT_EQ(civil_second(2018, 3, 11, 2, 0, 0), trans.from); | ||||||
|  |   EXPECT_EQ(civil_second(2018, 3, 11, 3, 0, 0), trans.to); | ||||||
|  | 
 | ||||||
|  |   tp = time_point<absl::time_internal::cctz::seconds>::min(); | ||||||
|  |   EXPECT_FALSE(tz.prev_transition(tp, &trans)); | ||||||
|  | 
 | ||||||
|  |   tp = time_point<absl::time_internal::cctz::seconds>::max(); | ||||||
|  |   EXPECT_TRUE(tz.prev_transition(tp, &trans)); | ||||||
|  |   // We have a transition but we don't know which one.
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| TEST(TimeZoneEdgeCase, AmericaNewYork) { | TEST(TimeZoneEdgeCase, AmericaNewYork) { | ||||||
|   const time_zone tz = LoadZone("America/New_York"); |   const time_zone tz = LoadZone("America/New_York"); | ||||||
| 
 | 
 | ||||||
|  | @ -1104,35 +1178,31 @@ TEST(TimeZoneEdgeCase, PacificApia) { | ||||||
| TEST(TimeZoneEdgeCase, AfricaCairo) { | TEST(TimeZoneEdgeCase, AfricaCairo) { | ||||||
|   const time_zone tz = LoadZone("Africa/Cairo"); |   const time_zone tz = LoadZone("Africa/Cairo"); | ||||||
| 
 | 
 | ||||||
| #if defined(__ANDROID__) && __ANDROID_API__ < 21 |   if (VersionCmp(tz, "2014c") >= 0) { | ||||||
|   // Only Android 'L' and beyond have this tz2014c transition.
 |     // An interesting case of midnight not existing.
 | ||||||
| #else |     //
 | ||||||
|   // An interesting case of midnight not existing.
 |     //   1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
 | ||||||
|   //
 |     //   1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
 | ||||||
|   //   1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
 |     auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz); | ||||||
|   //   1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
 |     ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET"); | ||||||
|   auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz); |     tp += absl::time_internal::cctz::seconds(1); | ||||||
|   ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET"); |     ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST"); | ||||||
|   tp += absl::time_internal::cctz::seconds(1); |   } | ||||||
|   ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST"); |  | ||||||
| #endif |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TEST(TimeZoneEdgeCase, AfricaMonrovia) { | TEST(TimeZoneEdgeCase, AfricaMonrovia) { | ||||||
|   const time_zone tz = LoadZone("Africa/Monrovia"); |   const time_zone tz = LoadZone("Africa/Monrovia"); | ||||||
| 
 | 
 | ||||||
| #if defined(__ANDROID__) && __ANDROID_API__ < 26 |   if (VersionCmp(tz, "2017b") >= 0) { | ||||||
|   // Only Android 'O' and beyond have this tz2017b transition.
 |     // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
 | ||||||
| #else |     //
 | ||||||
|   // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
 |     //   63593069 == Thu,  6 Jan 1972 23:59:59 -0044 (MMT)
 | ||||||
|   //
 |     //   63593070 == Fri,  7 Jan 1972 00:44:30 +0000 (GMT)
 | ||||||
|   //   63593069 == Thu,  6 Jan 1972 23:59:59 -0044 (MMT)
 |     auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz); | ||||||
|   //   63593070 == Fri,  7 Jan 1972 00:44:30 +0000 (GMT)
 |     ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT"); | ||||||
|   auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz); |     tp += absl::time_internal::cctz::seconds(1); | ||||||
|   ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT"); |     ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT"); | ||||||
|   tp += absl::time_internal::cctz::seconds(1); |   } | ||||||
|   ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT"); |  | ||||||
| #endif |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TEST(TimeZoneEdgeCase, AmericaJamaica) { | TEST(TimeZoneEdgeCase, AmericaJamaica) { | ||||||
|  | @ -1144,28 +1214,29 @@ TEST(TimeZoneEdgeCase, AmericaJamaica) { | ||||||
|   const time_zone tz = LoadZone("America/Jamaica"); |   const time_zone tz = LoadZone("America/Jamaica"); | ||||||
| 
 | 
 | ||||||
|   // Before the first transition.
 |   // Before the first transition.
 | ||||||
|   auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz); |   if (!tz.version().empty() && VersionCmp(tz, "2018d") >= 0) { | ||||||
| #if AMERICA_JAMAICA_PRE_1913_OFFSET_FIX |     // We avoid the expectations on the -18430 offset below unless we are
 | ||||||
|   // Commit 907241e: Fix off-by-1 error for Jamaica and T&C before 1913.
 |     // certain we have commit 907241e (Fix off-by-1 error for Jamaica and
 | ||||||
|   // Until that commit has made its way into a full release we avoid the
 |     // T&C before 1913) from 2018d.  TODO: Remove the "version() not empty"
 | ||||||
|   // expectations on the -18430 offset below.  TODO: Uncomment these.
 |     // part when 2018d is generally available from /usr/share/zoneinfo.
 | ||||||
|   ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false, |     auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz); | ||||||
|              tz.lookup(tp).abbr); |     ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false, | ||||||
|  |                tz.lookup(tp).abbr); | ||||||
| 
 | 
 | ||||||
|   // Over the first (abbreviation-change only) transition.
 |     // Over the first (abbreviation-change only) transition.
 | ||||||
|   //   -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
 |     //   -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
 | ||||||
|   //   -2524503169 == Wed,  1 Jan 1890 00:00:00 -0507 (KMT)
 |     //   -2524503169 == Wed,  1 Jan 1890 00:00:00 -0507 (KMT)
 | ||||||
|   tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz); |     tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz); | ||||||
|   ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false, |     ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false, | ||||||
|              tz.lookup(tp).abbr); |                tz.lookup(tp).abbr); | ||||||
|   tp += absl::time_internal::cctz::seconds(1); |     tp += absl::time_internal::cctz::seconds(1); | ||||||
|   ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT"); |     ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT"); | ||||||
| #endif |   } | ||||||
| 
 | 
 | ||||||
|   // Over the last (DST) transition.
 |   // Over the last (DST) transition.
 | ||||||
|   //     436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT)
 |   //     436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT)
 | ||||||
|   //     436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST)
 |   //     436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST)
 | ||||||
|   tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz); |   auto tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz); | ||||||
|   ExpectTime(tp, tz, 1983, 10, 30, 1, 59, 59, -4 * 3600, true, "EDT"); |   ExpectTime(tp, tz, 1983, 10, 30, 1, 59, 59, -4 * 3600, true, "EDT"); | ||||||
|   tp += absl::time_internal::cctz::seconds(1); |   tp += absl::time_internal::cctz::seconds(1); | ||||||
|   ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST"); |   ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST"); | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ namespace cctz { | ||||||
| 
 | 
 | ||||||
| // Defined out-of-line to avoid emitting a weak vtable in all TUs.
 | // Defined out-of-line to avoid emitting a weak vtable in all TUs.
 | ||||||
| ZoneInfoSource::~ZoneInfoSource() {} | ZoneInfoSource::~ZoneInfoSource() {} | ||||||
|  | std::string ZoneInfoSource::Version() const { return std::string(); } | ||||||
| 
 | 
 | ||||||
| }  // namespace cctz
 | }  // namespace cctz
 | ||||||
| }  // namespace time_internal
 | }  // namespace time_internal
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue