about summary refs log tree commit diff
path: root/absl
diff options
context:
space:
mode:
Diffstat (limited to 'absl')
-rw-r--r--absl/BUILD.bazel7
-rw-r--r--absl/base/BUILD.bazel1
-rw-r--r--absl/base/CMakeLists.txt2
-rw-r--r--absl/base/casts.h51
-rw-r--r--absl/base/internal/direct_mmap.h12
-rw-r--r--absl/base/internal/raw_logging.cc16
-rw-r--r--absl/base/internal/raw_logging.h43
-rw-r--r--absl/base/raw_logging_test.cc29
-rw-r--r--absl/compiler_config_setting.bzl39
-rw-r--r--absl/container/fixed_array.h260
-rw-r--r--absl/container/inlined_vector.h7
-rw-r--r--absl/copts.bzl1
-rw-r--r--absl/debugging/internal/stacktrace_config.h37
-rw-r--r--absl/meta/type_traits.h2
-rw-r--r--absl/strings/str_format_test.cc4
-rw-r--r--absl/synchronization/mutex.h4
-rw-r--r--absl/synchronization/mutex_test.cc575
-rw-r--r--absl/time/clock_test.cc94
-rw-r--r--absl/time/internal/cctz/include/cctz/civil_time_detail.h16
-rw-r--r--absl/time/internal/cctz/include/cctz/time_zone.h77
-rw-r--r--absl/time/internal/cctz/include/cctz/zone_info_source.h5
-rw-r--r--absl/time/internal/cctz/src/cctz_benchmark.cc14
-rw-r--r--absl/time/internal/cctz/src/time_zone_format.cc3
-rw-r--r--absl/time/internal/cctz/src/time_zone_format_test.cc25
-rw-r--r--absl/time/internal/cctz/src/time_zone_if.h8
-rw-r--r--absl/time/internal/cctz/src/time_zone_impl.cc9
-rw-r--r--absl/time/internal/cctz/src/time_zone_impl.h38
-rw-r--r--absl/time/internal/cctz/src/time_zone_info.cc74
-rw-r--r--absl/time/internal/cctz/src/time_zone_info.h8
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.cc16
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.h7
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup.cc33
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc155
-rw-r--r--absl/time/internal/cctz/src/zone_info_source.cc1
-rw-r--r--absl/types/variant.h2
35 files changed, 1099 insertions, 576 deletions
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index 439addbfc828..edd0274c5d83 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -18,11 +18,10 @@ package(default_visibility = ["//visibility:public"])
 
 licenses(["notice"])  # Apache 2.0
 
-config_setting(
+load(":compiler_config_setting.bzl", "create_llvm_config")
+
+create_llvm_config(
     name = "llvm_compiler",
-    values = {
-        "compiler": "llvm",
-    },
     visibility = [":__subpackages__"],
 )
 
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index 35414a252c62..06d092ebdfa8 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -362,6 +362,7 @@ cc_test(
     copts = ABSL_TEST_COPTS,
     deps = [
         ":base",
+        "//absl/strings",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 303533e26d1b..01d2af085f58 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -310,7 +310,7 @@ absl_test(
 
 # test raw_logging_test
 set(RAW_LOGGING_TEST_SRC "raw_logging_test.cc")
-set(RAW_LOGGING_TEST_PUBLIC_LIBRARIES absl::base)
+set(RAW_LOGGING_TEST_PUBLIC_LIBRARIES absl::base absl::strings)
 
 absl_test(
   TARGET
diff --git a/absl/base/casts.h b/absl/base/casts.h
index 8bd5264d9780..20fd34da7010 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -25,12 +25,36 @@
 #define ABSL_BASE_CASTS_H_
 
 #include <cstring>
+#include <memory>
 #include <type_traits>
 
 #include "absl/base/internal/identity.h"
+#include "absl/base/macros.h"
 
 namespace absl {
 
+namespace internal_casts {
+
+// NOTE: Not a fully compliant implementation of `std::is_trivially_copyable`.
+// TODO(calabrese) Branch on implementations that directly provide
+// `std::is_trivially_copyable`, create a more rigorous workaround, and publicly
+// expose in meta/type_traits.
+template <class T>
+struct is_trivially_copyable
+    : std::integral_constant<
+          bool, std::is_destructible<T>::value&& __has_trivial_destructor(T) &&
+                    __has_trivial_copy(T) && __has_trivial_assign(T)> {};
+
+template <class Dest, class Source>
+struct is_bitcastable
+    : std::integral_constant<bool,
+                             sizeof(Dest) == sizeof(Source) &&
+                                 is_trivially_copyable<Source>::value &&
+                                 is_trivially_copyable<Dest>::value &&
+                                 std::is_default_constructible<Dest>::value> {};
+
+}  // namespace internal_casts
+
 // implicit_cast()
 //
 // Performs an implicit conversion between types following the language
@@ -125,7 +149,32 @@ inline To implicit_cast(typename absl::internal::identity_t<To> to) {
 // and reading its bits back using a different type. A `bit_cast()` avoids this
 // issue by implementing its casts using `memcpy()`, which avoids introducing
 // this undefined behavior.
-template <typename Dest, typename Source>
+//
+// NOTE: The requirements here are more strict than the bit_cast of standard
+// proposal p0476 due to the need for workarounds and lack of intrinsics.
+// Specifically, this implementation also requires `Dest` to be
+// default-constructible.
+template <
+    typename Dest, typename Source,
+    typename std::enable_if<internal_casts::is_bitcastable<Dest, Source>::value,
+                            int>::type = 0>
+inline Dest bit_cast(const Source& source) {
+  Dest dest;
+  memcpy(static_cast<void*>(std::addressof(dest)),
+         static_cast<const void*>(std::addressof(source)), sizeof(dest));
+  return dest;
+}
+
+// NOTE: This overload is only picked if the requirements of bit_cast are not
+// met. It is therefore UB, but is provided temporarily as previous versions of
+// this function template were unchecked. Do not use this in new code.
+template <
+    typename Dest, typename Source,
+    typename std::enable_if<
+        !internal_casts::is_bitcastable<Dest, Source>::value, int>::type = 0>
+ABSL_DEPRECATED(
+    "absl::bit_cast type requirements were violated. Update the types being "
+    "used such that they are the same size and are both TriviallyCopyable.")
 inline Dest bit_cast(const Source& source) {
   static_assert(sizeof(Dest) == sizeof(Source),
                 "Source and destination types should have equal sizes.");
diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h
index 2fe345fc85aa..0426e11890b6 100644
--- a/absl/base/internal/direct_mmap.h
+++ b/absl/base/internal/direct_mmap.h
@@ -92,11 +92,13 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
 #endif
 #elif defined(__s390x__)
   // On s390x, mmap() arguments are passed in memory.
-  uint32_t buf[6] = {
-      reinterpret_cast<uint32_t>(start), static_cast<uint32_t>(length),
-      static_cast<uint32_t>(prot),       static_cast<uint32_t>(flags),
-      static_cast<uint32_t>(fd),         static_cast<uint32_t>(offset)};
-  return reintrepret_cast<void*>(syscall(SYS_mmap, buf));
+  unsigned long buf[6] = {reinterpret_cast<unsigned long>(start),  // NOLINT
+                          static_cast<unsigned long>(length),      // NOLINT
+                          static_cast<unsigned long>(prot),        // NOLINT
+                          static_cast<unsigned long>(flags),       // NOLINT
+                          static_cast<unsigned long>(fd),          // NOLINT
+                          static_cast<unsigned long>(offset)};     // NOLINT
+  return reinterpret_cast<void*>(syscall(SYS_mmap, buf));
 #elif defined(__x86_64__)
 // The x32 ABI has 32 bit longs, but the syscall interface is 64 bit.
 // We need to explicitly cast to an unsigned 64 bit type to avoid implicit
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index 1ce138887252..41101bd72eb8 100644
--- a/absl/base/internal/raw_logging.cc
+++ b/absl/base/internal/raw_logging.cc
@@ -206,6 +206,15 @@ void RawLog(absl::LogSeverity severity, const char* file, int line,
   va_end(ap);
 }
 
+// Non-formatting version of RawLog().
+//
+// TODO(gfalcon): When string_view no longer depends on base, change this
+// interface to take its message as a string_view instead.
+static void DefaultInternalLog(absl::LogSeverity severity, const char* file,
+                               int line, const std::string& message) {
+  RawLog(severity, file, line, "%s", message.c_str());
+}
+
 bool RawLoggingFullySupported() {
 #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
   return true;
@@ -214,5 +223,12 @@ bool RawLoggingFullySupported() {
 #endif  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
 }
 
+ABSL_CONST_INIT absl::base_internal::AtomicHook<InternalLogFunction>
+    internal_log_function(DefaultInternalLog);
+
+void RegisterInternalLogFunction(InternalLogFunction func) {
+  internal_log_function.Store(func);
+}
+
 }  // namespace raw_logging_internal
 }  // namespace absl
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index a2b7207a032c..67abfd30798d 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -19,7 +19,10 @@
 #ifndef ABSL_BASE_INTERNAL_RAW_LOGGING_H_
 #define ABSL_BASE_INTERNAL_RAW_LOGGING_H_
 
+#include <string>
+
 #include "absl/base/attributes.h"
+#include "absl/base/internal/atomic_hook.h"
 #include "absl/base/log_severity.h"
 #include "absl/base/macros.h"
 #include "absl/base/port.h"
@@ -57,6 +60,34 @@
     }                                                                  \
   } while (0)
 
+// ABSL_INTERNAL_LOG and ABSL_INTERNAL_CHECK work like the RAW variants above,
+// except that if the richer log library is linked into the binary, we dispatch
+// to that instead.  This is potentially useful for internal logging and
+// assertions, where we are using RAW_LOG neither for its async-signal-safety
+// nor for its non-allocating nature, but rather because raw logging has very
+// few other dependencies.
+//
+// The API is a subset of the above: each macro only takes two arguments.  Use
+// StrCat if you need to build a richer message.
+#define ABSL_INTERNAL_LOG(severity, message)                          \
+  do {                                                                \
+    constexpr const char* absl_raw_logging_internal_basename =        \
+        ::absl::raw_logging_internal::Basename(__FILE__,              \
+                                               sizeof(__FILE__) - 1); \
+    ::absl::raw_logging_internal::internal_log_function(              \
+        ABSL_RAW_LOGGING_INTERNAL_##severity,                         \
+        absl_raw_logging_internal_basename, __LINE__, message);       \
+  } while (0)
+
+#define ABSL_INTERNAL_CHECK(condition, message)               \
+  do {                                                        \
+    if (ABSL_PREDICT_FALSE(!(condition))) {                   \
+      std::string death_message = "Check " #condition " failed: "; \
+      death_message += std::string(message);                       \
+      ABSL_INTERNAL_LOG(FATAL, death_message);                \
+    }                                                         \
+  } while (0)
+
 #define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo
 #define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning
 #define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError
@@ -131,6 +162,18 @@ using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file,
 using AbortHook = void (*)(const char* file, int line, const char* buf_start,
                            const char* prefix_end, const char* buf_end);
 
+// Internal logging function for ABSL_INTERNAL_LOG to dispatch to.
+//
+// TODO(gfalcon): When string_view no longer depends on base, change this
+// interface to take its message as a string_view instead.
+using InternalLogFunction = void (*)(absl::LogSeverity severity,
+                                     const char* file, int line,
+                                     const std::string& message);
+
+extern base_internal::AtomicHook<InternalLogFunction> internal_log_function;
+
+void RegisterInternalLogFunction(InternalLogFunction func);
+
 }  // namespace raw_logging_internal
 }  // namespace absl
 
diff --git a/absl/base/raw_logging_test.cc b/absl/base/raw_logging_test.cc
index dae4b35138c5..ebbc5db90672 100644
--- a/absl/base/raw_logging_test.cc
+++ b/absl/base/raw_logging_test.cc
@@ -18,12 +18,20 @@
 
 #include "absl/base/internal/raw_logging.h"
 
+#include <tuple>
+
 #include "gtest/gtest.h"
+#include "absl/strings/str_cat.h"
 
 namespace {
 
 TEST(RawLoggingCompilationTest, Log) {
   ABSL_RAW_LOG(INFO, "RAW INFO: %d", 1);
+  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d", 1, 2);
+  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d", 1, 2, 3);
+  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d", 1, 2, 3, 4);
+  ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d %d", 1, 2, 3, 4, 5);
+  ABSL_RAW_LOG(WARNING, "RAW WARNING: %d", 1);
   ABSL_RAW_LOG(ERROR, "RAW ERROR: %d", 1);
 }
 
@@ -47,4 +55,25 @@ TEST(RawLoggingDeathTest, LogFatal) {
                             kExpectedDeathOutput);
 }
 
+TEST(InternalLog, CompilationTest) {
+  ABSL_INTERNAL_LOG(INFO, "Internal Log");
+  std::string log_msg = "Internal Log";
+  ABSL_INTERNAL_LOG(INFO, log_msg);
+
+  ABSL_INTERNAL_LOG(INFO, log_msg + " 2");
+
+  float d = 1.1f;
+  ABSL_INTERNAL_LOG(INFO, absl::StrCat("Internal log ", 3, " + ", d));
+}
+
+TEST(InternalLogDeathTest, FailingCheck) {
+  EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_CHECK(1 == 0, "explanation"),
+                            kExpectedDeathOutput);
+}
+
+TEST(InternalLogDeathTest, LogFatal) {
+  EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_LOG(FATAL, "my dog has fleas"),
+                            kExpectedDeathOutput);
+}
+
 }  // namespace
diff --git a/absl/compiler_config_setting.bzl b/absl/compiler_config_setting.bzl
new file mode 100644
index 000000000000..b77c4f563b95
--- /dev/null
+++ b/absl/compiler_config_setting.bzl
@@ -0,0 +1,39 @@
+#
+# Copyright 2018 The Abseil Authors.
+#
+# 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.
+#
+
+"""Creates config_setting that allows selecting based on 'compiler' value."""
+
+def create_llvm_config(name, visibility):
+  # The "do_not_use_tools_cpp_compiler_present" attribute exists to
+  # distinguish between older versions of Bazel that do not support
+  # "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do.
+  # In the future, the only way to select on the compiler will be through
+  # flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can
+  # be removed.
+  if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"):
+    native.config_setting(
+      name = name,
+      flag_values = {
+          "@bazel_tools//tools/cpp:compiler": "llvm",
+      },
+      visibility = visibility,
+    )
+  else:
+    native.config_setting(
+        name = name,
+        values = {"compiler": "llvm"},
+        visibility = visibility,
+    )
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h
index 295f01088d21..62600df05be3 100644
--- a/absl/container/fixed_array.h
+++ b/absl/container/fixed_array.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Abseil Authors.
+// Copyright 2018 The Abseil Authors.
 //
 // Licensed under the Apache License, Version 2.0 (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
 // -----------------------------------------------------------------------------
 //
-// A `FixedArray` provides a run-time fixed-size array, allocating small arrays
-// inline for efficiency and correctness.
+// A `FixedArray` provides a run-time fixed-size array, allocating a small array
+// inline for efficiency.
 //
 // 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
-// `FixedArray<>` implementation will inline arrays of
+// `FixedArray` implementation will use inline storage for arrays with a
 // length <= `inline_elements`.
 //
 // Note that a `FixedArray` constructed with a `size_type` argument will
@@ -78,19 +78,18 @@ constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
 // operators.
 template <typename T, size_t inlined = kFixedArrayUseDefault>
 class FixedArray {
+  static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
+                "Arrays with unknown bounds cannot be used with FixedArray.");
   static constexpr size_t kInlineBytesDefault = 256;
 
   // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
   // but this seems to be mostly pedantic.
-  template <typename Iter>
-  using EnableIfForwardIterator = typename std::enable_if<
-      std::is_convertible<
-          typename std::iterator_traits<Iter>::iterator_category,
-          std::forward_iterator_tag>::value,
-      int>::type;
+  template <typename Iterator>
+  using EnableIfForwardIterator = absl::enable_if_t<std::is_convertible<
+      typename std::iterator_traits<Iterator>::iterator_category,
+      std::forward_iterator_tag>::value>;
 
  public:
-  // For playing nicely with stl:
   using value_type = T;
   using iterator = T*;
   using const_iterator = const T*;
@@ -112,40 +111,38 @@ class FixedArray {
       : FixedArray(other.begin(), other.end()) {}
 
   FixedArray(FixedArray&& other) noexcept(
-  // clang-format off
-      absl::allocator_is_nothrow<std::allocator<value_type>>::value &&
-  // clang-format on
-          std::is_nothrow_move_constructible<value_type>::value)
+      absl::conjunction<absl::allocator_is_nothrow<std::allocator<value_type>>,
+                        std::is_nothrow_move_constructible<value_type>>::value)
       : FixedArray(std::make_move_iterator(other.begin()),
                    std::make_move_iterator(other.end())) {}
 
   // Creates an array object that can store `n` elements.
   // Note that trivially constructible elements will be uninitialized.
-  explicit FixedArray(size_type n) : rep_(n) {
-    absl::memory_internal::uninitialized_default_construct_n(rep_.begin(),
+  explicit FixedArray(size_type n) : storage_(n) {
+    absl::memory_internal::uninitialized_default_construct_n(storage_.begin(),
                                                              size());
   }
 
   // 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);
   }
 
   // Creates an array initialized with the elements from the input
   // range. The array's size will always be `std::distance(first, last)`.
-  // REQUIRES: Iter must be a forward_iterator or better.
-  template <typename Iter, EnableIfForwardIterator<Iter> = 0>
-  FixedArray(Iter first, Iter last) : rep_(std::distance(first, last)) {
+  // REQUIRES: Iterator must be a forward_iterator or better.
+  template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr>
+  FixedArray(Iterator first, Iterator last)
+      : storage_(std::distance(first, last)) {
     std::uninitialized_copy(first, last, data());
   }
 
-  // Creates the array from an initializer_list.
-  FixedArray(std::initializer_list<T> init_list)
+  FixedArray(std::initializer_list<value_type> init_list)
       : FixedArray(init_list.begin(), init_list.end()) {}
 
   ~FixedArray() noexcept {
-    for (Holder* cur = rep_.begin(); cur != rep_.end(); ++cur) {
-      cur->~Holder();
+    for (const StorageElement& cur : storage_) {
+      cur.~StorageElement();
     }
   }
 
@@ -157,7 +154,7 @@ class FixedArray {
   // FixedArray::size()
   //
   // Returns the length of the fixed array.
-  size_type size() const { return rep_.size(); }
+  size_type size() const { return storage_.size(); }
 
   // FixedArray::max_size()
   //
@@ -182,12 +179,12 @@ class FixedArray {
   //
   // Returns a const T* pointer to elements of the `FixedArray`. This pointer
   // 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
   // fixed array. This pointer can be used to access and modify the contained
   // elements.
-  pointer data() { return AsValue(rep_.begin()); }
+  pointer data() { return AsValueType(storage_.begin()); }
 
   // FixedArray::operator[]
   //
@@ -307,7 +304,7 @@ class FixedArray {
   // FixedArray::fill()
   //
   // 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
   // `operator==`, while order operators order FixedArrays lexicographically.
@@ -337,17 +334,18 @@ class FixedArray {
   }
 
  private:
-  // HolderTraits
+  // StorageElement
   //
-  // Wrapper to hold elements of type T for the case where T is an array type.
-  // If 'T' is an array type, HolderTraits::type is a struct with a 'T v;'.
-  // Otherwise, HolderTraits::type is simply 'T'.
+  // For FixedArrays with a C-style-array value_type, StorageElement is a POD
+  // wrapper struct called StorageElementWrapper that holds the value_type
+  // instance inside. This is needed for construction and destruction of the
+  // 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
-  // struct whether it's an array or not: 'struct Holder { T v; };', but
-  // that causes some paranoid diagnostics to misfire about uses of data(),
-  // believing that 'data()' (aka '&rep_.begin().v') is a pointer to a single
-  // element, rather than the packed array that it really is.
+  // Maintainer's Note: The simpler solution would be to simply wrap value_type
+  // in a struct whether it's an array or not. That causes some paranoid
+  // diagnostics to misfire, believing that 'data()' returns a pointer to a
+  // single element, rather than the packed array that it really is.
   // e.g.:
   //
   //     FixedArray<char> buf(1);
@@ -356,130 +354,98 @@ class FixedArray {
   //     error: call to int __builtin___sprintf_chk(etc...)
   //     will always overflow destination buffer [-Werror]
   //
-  class HolderTraits {
-    template <typename U>
-    struct SelectImpl {
-      using type = U;
-      static pointer AsValue(type* p) { return p; }
-    };
-
-    // Partial specialization for elements of array type.
-    template <typename U, size_t N>
-    struct SelectImpl<U[N]> {
-      struct Holder { U v[N]; };
-      using type = Holder;
-      static pointer AsValue(type* p) { return &p->v; }
-    };
-    using Impl = SelectImpl<value_type>;
-
-   public:
-    using type = typename Impl::type;
-
-    static pointer AsValue(type *p) { return Impl::AsValue(p); }
-
-    // TODO(billydonahue): fix the type aliasing violation
-    // this assertion hints at.
-    static_assert(sizeof(type) == sizeof(value_type),
-                  "Holder must be same size as value_type");
+  template <typename OuterT = value_type,
+            typename InnerT = absl::remove_extent_t<OuterT>,
+            size_t InnerN = std::extent<OuterT>::value>
+  struct StorageElementWrapper {
+    InnerT array[InnerN];
   };
 
-  using Holder = typename HolderTraits::type;
-  static pointer AsValue(Holder *p) { return HolderTraits::AsValue(p); }
+  using StorageElement =
+      absl::conditional_t<std::is_array<value_type>::value,
+                          StorageElementWrapper<value_type>, value_type>;
 
-  // InlineSpace
-  //
-  // 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); }
+  static pointer AsValueType(pointer ptr) { return ptr; }
+  static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
+    return std::addressof(ptr->array);
+  }
 
-   private:
-#ifndef ADDRESS_SANITIZER
-    void Annotate(size_t, bool) const { }
-#else
-    void Annotate(size_t n, bool creating) const {
-      if (!n) return;
-      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);
-      }
+  static_assert(sizeof(StorageElement) == sizeof(value_type), "");
+  static_assert(alignof(StorageElement) == alignof(value_type), "");
+
+  struct NonEmptyInlinedStorage {
+    using StorageElementBuffer =
+        absl::aligned_storage_t<sizeof(StorageElement),
+                                alignof(StorageElement)>;
+    StorageElement* data() {
+      return reinterpret_cast<StorageElement*>(inlined_storage_.data());
     }
+
+#ifdef ADDRESS_SANITIZER
+    void* RedzoneBegin() { return &redzone_begin_; }
+    void* RedzoneEnd() { return &redzone_end_ + 1; }
 #endif  // ADDRESS_SANITIZER
 
-    using Buffer =
-        typename std::aligned_storage<sizeof(Holder), alignof(Holder)>::type;
+    void AnnotateConstruct(size_t);
+    void AnnotateDestruct(size_t);
 
-    ADDRESS_SANITIZER_REDZONE(left_redzone_);
-    std::array<Buffer, N> space_;
-    ADDRESS_SANITIZER_REDZONE(right_redzone_);
+    ADDRESS_SANITIZER_REDZONE(redzone_begin_);
+    std::array<StorageElementBuffer, inline_elements> inlined_storage_;
+    ADDRESS_SANITIZER_REDZONE(redzone_end_);
   };
 
-  // specialization when N = 0.
-  template <typename U>
-  class InlineSpace<0, U> {
-   public:
-    Holder* data() { return nullptr; }
-    void AnnotateConstruct(size_t) const {}
-    void AnnotateDestruct(size_t) const {}
+  struct EmptyInlinedStorage {
+    StorageElement* data() { return nullptr; }
+    void AnnotateConstruct(size_t) {}
+    void AnnotateDestruct(size_t) {}
   };
 
-  // Rep
+  using InlinedStorage =
+      absl::conditional_t<inline_elements == 0, EmptyInlinedStorage,
+                          NonEmptyInlinedStorage>;
+
+  // Storage
   //
-  // An instance of Rep manages the inline and out-of-line memory for FixedArray
+  // An instance of Storage manages the inline and out-of-line memory for
+  // 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 Rep : public InlineSpace<inline_elements> {
+  class Storage : public InlinedStorage {
    public:
-    explicit Rep(size_type n) : n_(n), p_(MakeHolder(n)) {}
-
-    ~Rep() noexcept {
-      if (IsAllocated(size())) {
-        std::allocator<Holder>().deallocate(p_, n_);
-      } else {
+    explicit Storage(size_type n) : data_(CreateStorage(n)), size_(n) {}
+    ~Storage() noexcept {
+      if (UsingInlinedStorage(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 n_; }
+
+    size_type size() const { return size_; }
+    StorageElement* begin() const { return data_; }
+    StorageElement* end() const { return begin() + size(); }
 
    private:
-    Holder* MakeHolder(size_type n) {
-      if (IsAllocated(n)) {
-        return std::allocator<Holder>().allocate(n);
-      } else {
+    static bool UsingInlinedStorage(size_type n) {
+      return n <= inline_elements;
+    }
+
+    StorageElement* CreateStorage(size_type n) {
+      if (UsingInlinedStorage(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; }
-
-    const size_type n_;
-    Holder* const p_;
+    StorageElement* const data_;
+    const size_type size_;
   };
 
-
-  // Data members
-  Rep rep_;
+  const Storage storage_;
 };
 
 template <typename T, size_t N>
@@ -488,5 +454,25 @@ constexpr size_t FixedArray<T, N>::inline_elements;
 template <typename T, size_t N>
 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
 #endif  // ABSL_CONTAINER_FIXED_ARRAY_H_
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 03660f165353..75d980274a57 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -689,11 +689,14 @@ class InlinedVector {
     new (&rep_.allocation_storage.allocation) Allocation(allocation);
   }
 
+  // TODO(absl-team): investigate whether the reinterpret_cast is appropriate.
   value_type* inlined_space() {
-    return reinterpret_cast<value_type*>(&rep_.inlined_storage.inlined);
+    return reinterpret_cast<value_type*>(
+        std::addressof(rep_.inlined_storage.inlined[0]));
   }
   const value_type* inlined_space() const {
-    return reinterpret_cast<const value_type*>(&rep_.inlined_storage.inlined);
+    return reinterpret_cast<const value_type*>(
+        std::addressof(rep_.inlined_storage.inlined[0]));
   }
 
   value_type* allocated_space() { return allocation().buffer(); }
diff --git a/absl/copts.bzl b/absl/copts.bzl
index 20c9b6190dc9..0168ac5abddc 100644
--- a/absl/copts.bzl
+++ b/absl/copts.bzl
@@ -31,7 +31,6 @@ GCC_TEST_FLAGS = [
     "-Wno-unused-private-field",
 ]
 
-
 # Docs on single flags is preceded by a comment.
 # Docs on groups of flags is preceded by ###.
 
diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h
index 48adfccc62e5..dd713da8c0c7 100644
--- a/absl/debugging/internal/stacktrace_config.h
+++ b/absl/debugging/internal/stacktrace_config.h
@@ -21,26 +21,16 @@
 #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
 #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
 
-// First, test platforms which only support a stub.
-#if ABSL_STACKTRACE_INL_HEADER
+#if defined(ABSL_STACKTRACE_INL_HEADER)
 #error ABSL_STACKTRACE_INL_HEADER cannot be directly set
-#elif defined(__native_client__) || defined(__APPLE__) || \
-    defined(__FreeBSD__) || defined(__ANDROID__) || defined(__myriad2__) || \
-    defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__)
-#define ABSL_STACKTRACE_INL_HEADER \
-    "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
 
-// Next, test for Mips and Windows.
-// TODO(marmstrong): Mips case, remove the check for ABSL_STACKTRACE_INL_HEADER
-#elif defined(__mips__) && !defined(ABSL_STACKTRACE_INL_HEADER)
-#define ABSL_STACKTRACE_INL_HEADER \
-    "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
-#elif defined(_WIN32)  // windows
+#elif defined(_WIN32)
 #define ABSL_STACKTRACE_INL_HEADER \
     "absl/debugging/internal/stacktrace_win32-inl.inc"
 
-// Finally, test NO_FRAME_POINTER.
-#elif !defined(NO_FRAME_POINTER)
+#elif defined(__linux__) && !defined(__ANDROID__)
+
+#if !defined(NO_FRAME_POINTER)
 # if defined(__i386__) || defined(__x86_64__)
 #define ABSL_STACKTRACE_INL_HEADER \
     "absl/debugging/internal/stacktrace_x86-inl.inc"
@@ -53,22 +43,27 @@
 # elif defined(__arm__)
 #define ABSL_STACKTRACE_INL_HEADER \
     "absl/debugging/internal/stacktrace_arm-inl.inc"
+# else
+#define ABSL_STACKTRACE_INL_HEADER \
+   "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
 # endif
 #else  // defined(NO_FRAME_POINTER)
 # if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
 #define ABSL_STACKTRACE_INL_HEADER \
-    "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
+    "absl/debugging/internal/stacktrace_generic-inl.inc"
 # elif defined(__ppc__) || defined(__PPC__)
-//  Use glibc's backtrace.
 #define ABSL_STACKTRACE_INL_HEADER \
     "absl/debugging/internal/stacktrace_generic-inl.inc"
-# elif defined(__arm__)
-#   error stacktrace without frame pointer is not supported on ARM
+# else
+#define ABSL_STACKTRACE_INL_HEADER \
+   "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
 # endif
 #endif  // NO_FRAME_POINTER
 
-#if !defined(ABSL_STACKTRACE_INL_HEADER)
-#error Not supported yet
+#else
+#define ABSL_STACKTRACE_INL_HEADER \
+  "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
+
 #endif
 
 #endif  // ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index 88af17c395df..c3e01fe2990c 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -369,4 +369,6 @@ struct IsHashEnabled
 }  // namespace type_traits_internal
 
 }  // namespace absl
+
+
 #endif  // ABSL_META_TYPE_TRAITS_H_
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index fe742bf99533..fed75fafb5c6 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -384,8 +384,8 @@ TEST(StrFormat, BehavesAsDocumented) {
   EXPECT_EQ(StrFormat("%G", 1e10), "1E+10");
   //     a/A - lower,upper case hex    Eg: -3.0 -> "-0x1.8p+1"/"-0X1.8P+1"
 
-// On NDK r16, there is a regression in hexfloat formatting.
-#if !defined(__NDK_MAJOR__) || __NDK_MAJOR__ != 16
+// On Android platform <=21, there is a regression in hexfloat formatting.
+#if !defined(__ANDROID_API__) || __ANDROID_API__ > 21
   EXPECT_EQ(StrFormat("%.1a", -3.0), "-0x1.8p+1");  // .1 to fix MSVC output
   EXPECT_EQ(StrFormat("%.1A", -3.0), "-0X1.8P+1");  // .1 to fix MSVC output
 #endif
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index 840b9d6b0e70..4d9e03123e3a 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -24,7 +24,7 @@
 // Unlike a `std::mutex`, the Abseil `Mutex` provides the following additional
 // features:
 //   * Conditional predicates intrinsic to the `Mutex` object
-//   * Reader/writer locks, in addition to standard exclusive/writer locks
+//   * Shared/reader locks, in addition to standard exclusive/writer locks
 //   * Deadlock detection and debug support.
 //
 // The following helper classes are also defined within this file:
@@ -290,7 +290,7 @@ class LOCKABLE Mutex {
   // Mutex::ReaderLockWhen()
   // Mutex::WriterLockWhen()
   //
-  // Blocks until simultaneously both `cond` is `true` and this` Mutex` can
+  // Blocks until simultaneously both `cond` is `true` and this `Mutex` can
   // be acquired, then atomically acquires this `Mutex`. `LockWhen()` is
   // logically equivalent to `*Lock(); Await();` though they may have different
   // performance characteristics.
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index 53b937843a34..b2820e2068d5 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -29,6 +29,7 @@
 #include <vector>
 
 #include "gtest/gtest.h"
+#include "absl/base/attributes.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/internal/sysinfo.h"
 #include "absl/memory/memory.h"
@@ -54,8 +55,8 @@ CreateDefaultPool() {
 // Hack to schedule a function to run on a thread pool thread after a
 // duration has elapsed.
 static void ScheduleAfter(absl::synchronization_internal::ThreadPool *tp,
-                          const std::function<void()> &func,
-                          absl::Duration after) {
+                          absl::Duration after,
+                          const std::function<void()> &func) {
   tp->Schedule([func, after] {
     absl::SleepFor(after);
     func();
@@ -1150,249 +1151,369 @@ TEST(Mutex, DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS {
 // and so never expires/passes, and one that will expire/pass in the near
 // future.
 
-// Encapsulate a Mutex-protected bool with its associated Condition/CondVar.
-class Cond {
- public:
-  explicit Cond(bool use_deadline) : use_deadline_(use_deadline), c_(&b_) {}
-
-  void Set(bool v) {
-    absl::MutexLock lock(&mu_);
-    b_ = v;
+static absl::Duration TimeoutTestAllowedSchedulingDelay() {
+  // Note: we use a function here because Microsoft Visual Studio fails to
+  // properly initialize constexpr static absl::Duration variables.
+  return absl::Milliseconds(150);
+}
+
+// Returns true if `actual_delay` is close enough to `expected_delay` to pass
+// the timeouts/deadlines test.  Otherwise, logs warnings and returns false.
+ABSL_MUST_USE_RESULT
+static bool DelayIsWithinBounds(absl::Duration expected_delay,
+                                absl::Duration actual_delay) {
+  bool pass = true;
+  // Do not allow the observed delay to be less than expected.  This may occur
+  // in practice due to clock skew or when the synchronization primitives use a
+  // different clock than absl::Now(), but these cases should be handled by the
+  // the retry mechanism in each TimeoutTest.
+  if (actual_delay < expected_delay) {
+    ABSL_RAW_LOG(WARNING,
+                 "Actual delay %s was too short, expected %s (difference %s)",
+                 absl::FormatDuration(actual_delay).c_str(),
+                 absl::FormatDuration(expected_delay).c_str(),
+                 absl::FormatDuration(actual_delay - expected_delay).c_str());
+    pass = false;
   }
-
-  bool AwaitWithTimeout(absl::Duration timeout) {
-    absl::MutexLock lock(&mu_);
-    return use_deadline_ ? mu_.AwaitWithDeadline(c_, absl::Now() + timeout)
-                         : mu_.AwaitWithTimeout(c_, timeout);
+  // If the expected delay is <= zero then allow a small error tolerance, since
+  // we do not expect context switches to occur during test execution.
+  // Otherwise, thread scheduling delays may be substantial in rare cases, so
+  // tolerate up to kTimeoutTestAllowedSchedulingDelay of error.
+  absl::Duration tolerance = expected_delay <= absl::ZeroDuration()
+                                 ? absl::Milliseconds(10)
+                                 : TimeoutTestAllowedSchedulingDelay();
+  if (actual_delay > expected_delay + tolerance) {
+    ABSL_RAW_LOG(WARNING,
+                 "Actual delay %s was too long, expected %s (difference %s)",
+                 absl::FormatDuration(actual_delay).c_str(),
+                 absl::FormatDuration(expected_delay).c_str(),
+                 absl::FormatDuration(actual_delay - expected_delay).c_str());
+    pass = false;
   }
+  return pass;
+}
+
+// Parameters for TimeoutTest, below.
+struct TimeoutTestParam {
+  // The file and line number (used for logging purposes only).
+  const char *from_file;
+  int from_line;
+
+  // Should the absolute deadline API based on absl::Time be tested?  If false,
+  // the relative deadline API based on absl::Duration is tested.
+  bool use_absolute_deadline;
+
+  // The deadline/timeout used when calling the API being tested
+  // (e.g. Mutex::LockWhenWithDeadline).
+  absl::Duration wait_timeout;
+
+  // The delay before the condition will be set true by the test code.  If zero
+  // or negative, the condition is set true immediately (before calling the API
+  // being tested).  Otherwise, if infinite, the condition is never set true.
+  // Otherwise a closure is scheduled for the future that sets the condition
+  // true.
+  absl::Duration satisfy_condition_delay;
+
+  // The expected result of the condition after the call to the API being
+  // tested. Generally `true` means the condition was true when the API returns,
+  // `false` indicates an expected timeout.
+  bool expected_result;
+
+  // The expected delay before the API under test returns.  This is inherently
+  // flaky, so some slop is allowed (see `DelayIsWithinBounds` above), and the
+  // test keeps trying indefinitely until this constraint passes.
+  absl::Duration expected_delay;
+};
 
-  bool LockWhenWithTimeout(absl::Duration timeout) {
-    bool b = use_deadline_ ? mu_.LockWhenWithDeadline(c_, absl::Now() + timeout)
-                           : mu_.LockWhenWithTimeout(c_, timeout);
-    mu_.Unlock();
-    return b;
+// Print a `TimeoutTestParam` to a debug log.
+std::ostream &operator<<(std::ostream &os, const TimeoutTestParam &param) {
+  return os << "from: " << param.from_file << ":" << param.from_line
+            << " use_absolute_deadline: "
+            << (param.use_absolute_deadline ? "true" : "false")
+            << " wait_timeout: " << param.wait_timeout
+            << " satisfy_condition_delay: " << param.satisfy_condition_delay
+            << " expected_result: "
+            << (param.expected_result ? "true" : "false")
+            << " expected_delay: " << param.expected_delay;
+}
+
+std::string FormatString(const TimeoutTestParam &param) {
+  std::ostringstream os;
+  os << param;
+  return os.str();
+}
+
+// Like `thread::Executor::ScheduleAt` except:
+// a) Delays zero or negative are executed immediately in the current thread.
+// b) Infinite delays are never scheduled.
+// c) Calls this test's `ScheduleAt` helper instead of using `pool` directly.
+static void RunAfterDelay(absl::Duration delay,
+                          absl::synchronization_internal::ThreadPool *pool,
+                          const std::function<void()> &callback) {
+  if (delay <= absl::ZeroDuration()) {
+    callback();  // immediate
+  } else if (delay != absl::InfiniteDuration()) {
+    ScheduleAfter(pool, delay, callback);
   }
+}
 
-  bool ReaderLockWhenWithTimeout(absl::Duration timeout) {
-    bool b = use_deadline_
-                 ? mu_.ReaderLockWhenWithDeadline(c_, absl::Now() + timeout)
-                 : mu_.ReaderLockWhenWithTimeout(c_, timeout);
-    mu_.ReaderUnlock();
-    return b;
-  }
+class TimeoutTest : public ::testing::Test,
+                    public ::testing::WithParamInterface<TimeoutTestParam> {};
 
-  void Await() {
-    absl::MutexLock lock(&mu_);
-    mu_.Await(c_);
-  }
+std::vector<TimeoutTestParam> MakeTimeoutTestParamValues() {
+  // The `finite` delay is a finite, relatively short, delay.  We make it larger
+  // than our allowed scheduling delay (slop factor) to avoid confusion when
+  // diagnosing test failures.  The other constants here have clear meanings.
+  const absl::Duration finite = 3 * TimeoutTestAllowedSchedulingDelay();
+  const absl::Duration never = absl::InfiniteDuration();
+  const absl::Duration negative = -absl::InfiniteDuration();
+  const absl::Duration immediate = absl::ZeroDuration();
 
-  void Signal(bool v) {
-    absl::MutexLock lock(&mu_);
-    b_ = v;
-    cv_.Signal();
+  // Every test case is run twice; once using the absolute deadline API and once
+  // using the relative timeout API.
+  std::vector<TimeoutTestParam> values;
+  for (bool use_absolute_deadline : {false, true}) {
+    // Tests with a negative timeout (deadline in the past), which should
+    // immediately return current state of the condition.
+
+    // The condition is already true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        negative,   // wait_timeout
+        immediate,  // satisfy_condition_delay
+        true,       // expected_result
+        immediate,  // expected_delay
+    });
+
+    // The condition becomes true, but the timeout has already expired:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        negative,  // wait_timeout
+        finite,    // satisfy_condition_delay
+        false,     // expected_result
+        immediate  // expected_delay
+    });
+
+    // The condition never becomes true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        negative,  // wait_timeout
+        never,     // satisfy_condition_delay
+        false,     // expected_result
+        immediate  // expected_delay
+    });
+
+    // Tests with an infinite timeout (deadline in the infinite future), which
+    // should only return when the condition becomes true.
+
+    // The condition is already true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        never,      // wait_timeout
+        immediate,  // satisfy_condition_delay
+        true,       // expected_result
+        immediate   // expected_delay
+    });
+
+    // The condition becomes true before the (infinite) expiry:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        never,   // wait_timeout
+        finite,  // satisfy_condition_delay
+        true,    // expected_result
+        finite,  // expected_delay
+    });
+
+    // Tests with a (small) finite timeout (deadline soon), with the condition
+    // becoming true both before and after its expiry.
+
+    // The condition is already true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        never,      // wait_timeout
+        immediate,  // satisfy_condition_delay
+        true,       // expected_result
+        immediate   // expected_delay
+    });
+
+    // The condition becomes true before the expiry:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        finite * 2,  // wait_timeout
+        finite,      // satisfy_condition_delay
+        true,        // expected_result
+        finite       // expected_delay
+    });
+
+    // The condition becomes true, but the timeout has already expired:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        finite,      // wait_timeout
+        finite * 2,  // satisfy_condition_delay
+        false,       // expected_result
+        finite       // expected_delay
+    });
+
+    // The condition never becomes true:
+    values.push_back(TimeoutTestParam{
+        __FILE__, __LINE__, use_absolute_deadline,
+        finite,  // wait_timeout
+        never,   // satisfy_condition_delay
+        false,   // expected_result
+        finite   // expected_delay
+    });
   }
-
-  bool WaitWithTimeout(absl::Duration timeout) {
-    absl::MutexLock lock(&mu_);
-    absl::Time deadline = absl::Now() + timeout;
-    if (use_deadline_) {
-      while (!b_ && !cv_.WaitWithDeadline(&mu_, deadline)) {
-      }
-    } else {
-      while (!b_ && !cv_.WaitWithTimeout(&mu_, timeout)) {
-        timeout = deadline - absl::Now();  // recompute timeout
-      }
+  return values;
+}
+
+// Instantiate `TimeoutTest` with `MakeTimeoutTestParamValues()`.
+INSTANTIATE_TEST_CASE_P(All, TimeoutTest,
+                        testing::ValuesIn(MakeTimeoutTestParamValues()));
+
+TEST_P(TimeoutTest, Await) {
+  const TimeoutTestParam params = GetParam();
+  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+  // Because this test asserts bounds on scheduling delays it is flaky.  To
+  // compensate it loops forever until it passes.  Failures express as test
+  // timeouts, in which case the test log can be used to diagnose the issue.
+  for (int attempt = 1;; ++attempt) {
+    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+    absl::Mutex mu;
+    bool value = false;  // condition value (under mu)
+
+    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+        CreateDefaultPool();
+    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+      absl::MutexLock l(&mu);
+      value = true;
+    });
+
+    absl::MutexLock lock(&mu);
+    absl::Time start_time = absl::Now();
+    absl::Condition cond(&value);
+    bool result =
+        params.use_absolute_deadline
+            ? mu.AwaitWithDeadline(cond, start_time + params.wait_timeout)
+            : mu.AwaitWithTimeout(cond, params.wait_timeout);
+    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+      EXPECT_EQ(params.expected_result, result);
+      break;
     }
-    return b_;
-  }
-
-  void Wait() {
-    absl::MutexLock lock(&mu_);
-    while (!b_) cv_.Wait(&mu_);
-  }
-
- private:
-  const bool use_deadline_;
-
-  bool b_;
-  absl::Condition c_;
-  absl::CondVar cv_;
-  absl::Mutex mu_;
-};
-
-class OperationTimer {
- public:
-  OperationTimer() : start_(absl::Now()) {}
-  absl::Duration Get() const { return absl::Now() - start_; }
-
- private:
-  const absl::Time start_;
-};
-
-static void CheckResults(bool exp_result, bool act_result,
-                         absl::Duration exp_duration,
-                         absl::Duration act_duration) {
-  ABSL_RAW_CHECK(exp_result == act_result, "CheckResults failed");
-  // Allow for some worse-case scheduling delay and clock skew.
-  if ((exp_duration - absl::Milliseconds(40) > act_duration) ||
-      (exp_duration + absl::Milliseconds(150) < act_duration)) {
-    ABSL_RAW_LOG(FATAL, "CheckResults failed: operation took %s, expected %s",
-                 absl::FormatDuration(act_duration).c_str(),
-                 absl::FormatDuration(exp_duration).c_str());
   }
 }
 
-static void TestAwaitTimeout(Cond *cp, absl::Duration timeout, bool exp_result,
-                             absl::Duration exp_duration) {
-  OperationTimer t;
-  bool act_result = cp->AwaitWithTimeout(timeout);
-  CheckResults(exp_result, act_result, exp_duration, t.Get());
-}
-
-static void TestLockWhenTimeout(Cond *cp, absl::Duration timeout,
-                                bool exp_result, absl::Duration exp_duration) {
-  OperationTimer t;
-  bool act_result = cp->LockWhenWithTimeout(timeout);
-  CheckResults(exp_result, act_result, exp_duration, t.Get());
-}
+TEST_P(TimeoutTest, LockWhen) {
+  const TimeoutTestParam params = GetParam();
+  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+  // Because this test asserts bounds on scheduling delays it is flaky.  To
+  // compensate it loops forever until it passes.  Failures express as test
+  // timeouts, in which case the test log can be used to diagnose the issue.
+  for (int attempt = 1;; ++attempt) {
+    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+    absl::Mutex mu;
+    bool value = false;  // condition value (under mu)
+
+    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+        CreateDefaultPool();
+    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+      absl::MutexLock l(&mu);
+      value = true;
+    });
+
+    absl::Time start_time = absl::Now();
+    absl::Condition cond(&value);
+    bool result =
+        params.use_absolute_deadline
+            ? mu.LockWhenWithDeadline(cond, start_time + params.wait_timeout)
+            : mu.LockWhenWithTimeout(cond, params.wait_timeout);
+    mu.Unlock();
 
-static void TestReaderLockWhenTimeout(Cond *cp, absl::Duration timeout,
-                                      bool exp_result,
-                                      absl::Duration exp_duration) {
-  OperationTimer t;
-  bool act_result = cp->ReaderLockWhenWithTimeout(timeout);
-  CheckResults(exp_result, act_result, exp_duration, t.Get());
+    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+      EXPECT_EQ(params.expected_result, result);
+      break;
+    }
+  }
 }
 
-static void TestWaitTimeout(Cond *cp, absl::Duration timeout, bool exp_result,
-                            absl::Duration exp_duration) {
-  OperationTimer t;
-  bool act_result = cp->WaitWithTimeout(timeout);
-  CheckResults(exp_result, act_result, exp_duration, t.Get());
+TEST_P(TimeoutTest, ReaderLockWhen) {
+  const TimeoutTestParam params = GetParam();
+  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+  // Because this test asserts bounds on scheduling delays it is flaky.  To
+  // compensate it loops forever until it passes.  Failures express as test
+  // timeouts, in which case the test log can be used to diagnose the issue.
+  for (int attempt = 0;; ++attempt) {
+    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+    absl::Mutex mu;
+    bool value = false;  // condition value (under mu)
+
+    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+        CreateDefaultPool();
+    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+      absl::MutexLock l(&mu);
+      value = true;
+    });
+
+    absl::Time start_time = absl::Now();
+    bool result =
+        params.use_absolute_deadline
+            ? mu.ReaderLockWhenWithDeadline(absl::Condition(&value),
+                                            start_time + params.wait_timeout)
+            : mu.ReaderLockWhenWithTimeout(absl::Condition(&value),
+                                           params.wait_timeout);
+    mu.ReaderUnlock();
+
+    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+      EXPECT_EQ(params.expected_result, result);
+      break;
+    }
+  }
 }
 
-// Tests with a negative timeout (deadline in the past), which should
-// immediately return the current state of the condition.
-static void TestNegativeTimeouts(absl::synchronization_internal::ThreadPool *tp,
-                                 Cond *cp) {
-  const absl::Duration negative = -absl::InfiniteDuration();
-  const absl::Duration immediate = absl::ZeroDuration();
-
-  // The condition is already true:
-  cp->Set(true);
-  TestAwaitTimeout(cp, negative, true, immediate);
-  TestLockWhenTimeout(cp, negative, true, immediate);
-  TestReaderLockWhenTimeout(cp, negative, true, immediate);
-  TestWaitTimeout(cp, negative, true, immediate);
-
-  // The condition becomes true, but the timeout has already expired:
-  const absl::Duration delay = absl::Milliseconds(200);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), 3 * delay);
-  TestAwaitTimeout(cp, negative, false, immediate);
-  TestLockWhenTimeout(cp, negative, false, immediate);
-  TestReaderLockWhenTimeout(cp, negative, false, immediate);
-  cp->Await();  // wait for the scheduled Set() to complete
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay);
-  TestWaitTimeout(cp, negative, false, immediate);
-  cp->Wait();  // wait for the scheduled Signal() to complete
-
-  // The condition never becomes true:
-  cp->Set(false);
-  TestAwaitTimeout(cp, negative, false, immediate);
-  TestLockWhenTimeout(cp, negative, false, immediate);
-  TestReaderLockWhenTimeout(cp, negative, false, immediate);
-  TestWaitTimeout(cp, negative, false, immediate);
-}
-
-// Tests with an infinite timeout (deadline in the infinite future), which
-// should only return when the condition becomes true.
-static void TestInfiniteTimeouts(absl::synchronization_internal::ThreadPool *tp,
-                                 Cond *cp) {
-  const absl::Duration infinite = absl::InfiniteDuration();
-  const absl::Duration immediate = absl::ZeroDuration();
-
-  // The condition is already true:
-  cp->Set(true);
-  TestAwaitTimeout(cp, infinite, true, immediate);
-  TestLockWhenTimeout(cp, infinite, true, immediate);
-  TestReaderLockWhenTimeout(cp, infinite, true, immediate);
-  TestWaitTimeout(cp, infinite, true, immediate);
-
-  // The condition becomes true before the (infinite) expiry:
-  const absl::Duration delay = absl::Milliseconds(200);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
-  TestAwaitTimeout(cp, infinite, true, delay);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
-  TestLockWhenTimeout(cp, infinite, true, delay);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
-  TestReaderLockWhenTimeout(cp, infinite, true, delay);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay);
-  TestWaitTimeout(cp, infinite, true, delay);
-}
-
-// Tests with a (small) finite timeout (deadline soon), with the condition
-// becoming true both before and after its expiry.
-static void TestFiniteTimeouts(absl::synchronization_internal::ThreadPool *tp,
-                               Cond *cp) {
-  const absl::Duration finite = absl::Milliseconds(400);
-  const absl::Duration immediate = absl::ZeroDuration();
+TEST_P(TimeoutTest, Wait) {
+  const TimeoutTestParam params = GetParam();
+  ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str());
+
+  // Because this test asserts bounds on scheduling delays it is flaky.  To
+  // compensate it loops forever until it passes.  Failures express as test
+  // timeouts, in which case the test log can be used to diagnose the issue.
+  for (int attempt = 0;; ++attempt) {
+    ABSL_RAW_LOG(INFO, "Attempt %d", attempt);
+
+    absl::Mutex mu;
+    bool value = false;  // condition value (under mu)
+    absl::CondVar cv;    // signals a change of `value`
+
+    std::unique_ptr<absl::synchronization_internal::ThreadPool> pool =
+        CreateDefaultPool();
+    RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] {
+      absl::MutexLock l(&mu);
+      value = true;
+      cv.Signal();
+    });
+
+    absl::MutexLock lock(&mu);
+    absl::Time start_time = absl::Now();
+    absl::Duration timeout = params.wait_timeout;
+    absl::Time deadline = start_time + timeout;
+    while (!value) {
+      if (params.use_absolute_deadline ? cv.WaitWithDeadline(&mu, deadline)
+                                       : cv.WaitWithTimeout(&mu, timeout)) {
+        break;  // deadline/timeout exceeded
+      }
+      timeout = deadline - absl::Now();  // recompute
+    }
+    bool result = value;  // note: `mu` is still held
 
-  // The condition is already true:
-  cp->Set(true);
-  TestAwaitTimeout(cp, finite, true, immediate);
-  TestLockWhenTimeout(cp, finite, true, immediate);
-  TestReaderLockWhenTimeout(cp, finite, true, immediate);
-  TestWaitTimeout(cp, finite, true, immediate);
-
-  // The condition becomes true before the expiry:
-  const absl::Duration delay1 = finite / 2;
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
-  TestAwaitTimeout(cp, finite, true, delay1);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
-  TestLockWhenTimeout(cp, finite, true, delay1);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
-  TestReaderLockWhenTimeout(cp, finite, true, delay1);
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay1);
-  TestWaitTimeout(cp, finite, true, delay1);
-
-  // The condition becomes true, but the timeout has already expired:
-  const absl::Duration delay2 = finite * 2;
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), 3 * delay2);
-  TestAwaitTimeout(cp, finite, false, finite);
-  TestLockWhenTimeout(cp, finite, false, finite);
-  TestReaderLockWhenTimeout(cp, finite, false, finite);
-  cp->Await();  // wait for the scheduled Set() to complete
-  cp->Set(false);
-  ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay2);
-  TestWaitTimeout(cp, finite, false, finite);
-  cp->Wait();  // wait for the scheduled Signal() to complete
-
-  // The condition never becomes true:
-  cp->Set(false);
-  TestAwaitTimeout(cp, finite, false, finite);
-  TestLockWhenTimeout(cp, finite, false, finite);
-  TestReaderLockWhenTimeout(cp, finite, false, finite);
-  TestWaitTimeout(cp, finite, false, finite);
-}
-
-TEST(Mutex, Timeouts) {
-  auto tp = CreateDefaultPool();
-  for (bool use_deadline : {false, true}) {
-    Cond cond(use_deadline);
-    TestNegativeTimeouts(tp.get(), &cond);
-    TestInfiniteTimeouts(tp.get(), &cond);
-    TestFiniteTimeouts(tp.get(), &cond);
+    if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) {
+      EXPECT_EQ(params.expected_result, result);
+      break;
+    }
   }
 }
 
diff --git a/absl/time/clock_test.cc b/absl/time/clock_test.cc
index f143c0360b0d..707166d0c289 100644
--- a/absl/time/clock_test.cc
+++ b/absl/time/clock_test.cc
@@ -35,36 +35,84 @@ TEST(Time, Now) {
   EXPECT_GE(after, now);
 }
 
-TEST(SleepForTest, BasicSanity) {
-  absl::Duration sleep_time = absl::Milliseconds(2500);
-  absl::Time start = absl::Now();
-  absl::SleepFor(sleep_time);
-  absl::Time end = absl::Now();
-  EXPECT_LE(sleep_time - absl::Milliseconds(100), end - start);
-  EXPECT_GE(sleep_time + absl::Milliseconds(200), end - start);
-}
+enum class AlarmPolicy { kWithoutAlarm, kWithAlarm };
 
-#ifdef ABSL_HAVE_ALARM
-// Helper for test SleepFor.
+#if defined(ABSL_HAVE_ALARM)
 bool alarm_handler_invoked = false;
+
 void AlarmHandler(int signo) {
   ASSERT_EQ(signo, SIGALRM);
   alarm_handler_invoked = true;
 }
+#endif
+
+// Does SleepFor(d) take between lower_bound and upper_bound at least
+// once between now and (now + timeout)?  If requested (and supported),
+// add an alarm for the middle of the sleep period and expect it to fire.
+bool SleepForBounded(absl::Duration d, absl::Duration lower_bound,
+                     absl::Duration upper_bound, absl::Duration timeout,
+                     AlarmPolicy alarm_policy, int* attempts) {
+  const absl::Time deadline = absl::Now() + timeout;
+  while (absl::Now() < deadline) {
+#if defined(ABSL_HAVE_ALARM)
+    sig_t old_alarm = SIG_DFL;
+    if (alarm_policy == AlarmPolicy::kWithAlarm) {
+      alarm_handler_invoked = false;
+      old_alarm = signal(SIGALRM, AlarmHandler);
+      alarm(absl::ToInt64Seconds(d / 2));
+    }
+#else
+    EXPECT_EQ(alarm_policy, AlarmPolicy::kWithoutAlarm);
+#endif
+    ++*attempts;
+    absl::Time start = absl::Now();
+    absl::SleepFor(d);
+    absl::Duration actual = absl::Now() - start;
+#if defined(ABSL_HAVE_ALARM)
+    if (alarm_policy == AlarmPolicy::kWithAlarm) {
+      signal(SIGALRM, old_alarm);
+      if (!alarm_handler_invoked) continue;
+    }
+#endif
+    if (lower_bound <= actual && actual <= upper_bound) {
+      return true;  // yes, the SleepFor() was correctly bounded
+    }
+  }
+  return false;
+}
 
-TEST(SleepForTest, AlarmSupport) {
-  alarm_handler_invoked = false;
-  sig_t old_alarm = signal(SIGALRM, AlarmHandler);
-  alarm(2);
-  absl::Duration sleep_time = absl::Milliseconds(3500);
-  absl::Time start = absl::Now();
-  absl::SleepFor(sleep_time);
-  absl::Time end = absl::Now();
-  EXPECT_TRUE(alarm_handler_invoked);
-  EXPECT_LE(sleep_time - absl::Milliseconds(100), end - start);
-  EXPECT_GE(sleep_time + absl::Milliseconds(200), end - start);
-  signal(SIGALRM, old_alarm);
+testing::AssertionResult AssertSleepForBounded(absl::Duration d,
+                                               absl::Duration early,
+                                               absl::Duration late,
+                                               absl::Duration timeout,
+                                               AlarmPolicy alarm_policy) {
+  const absl::Duration lower_bound = d - early;
+  const absl::Duration upper_bound = d + late;
+  int attempts = 0;
+  if (SleepForBounded(d, lower_bound, upper_bound, timeout, alarm_policy,
+                      &attempts)) {
+    return testing::AssertionSuccess();
+  }
+  return testing::AssertionFailure()
+         << "SleepFor(" << d << ") did not return within [" << lower_bound
+         << ":" << upper_bound << "] in " << attempts << " attempt"
+         << (attempts == 1 ? "" : "s") << " over " << timeout
+         << (alarm_policy == AlarmPolicy::kWithAlarm ? " with" : " without")
+         << " an alarm";
+}
+
+// Tests that SleepFor() returns neither too early nor too late.
+TEST(SleepFor, Bounded) {
+  const absl::Duration d = absl::Milliseconds(2500);
+  const absl::Duration early = absl::Milliseconds(100);
+  const absl::Duration late = absl::Milliseconds(300);
+  const absl::Duration timeout = 48 * d;
+  EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
+                                    AlarmPolicy::kWithoutAlarm));
+#if defined(ABSL_HAVE_ALARM)
+  EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
+                                    AlarmPolicy::kWithAlarm));
+#endif
 }
-#endif  // ABSL_HAVE_ALARM
 
 }  // namespace
diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
index d52eddcdba06..2362a4f4fbc9 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
@@ -403,20 +403,16 @@ class civil_time {
   }
 
   // Binary arithmetic operators.
-  inline friend CONSTEXPR_M civil_time operator+(civil_time a,
-                                                 diff_t n) noexcept {
+  friend CONSTEXPR_F 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 {
+  friend CONSTEXPR_F 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 {
+  friend CONSTEXPR_F 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 {
+  friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept {
     return difference(T{}, lhs.f_, rhs.f_);
   }
 
@@ -434,8 +430,8 @@ class civil_time {
 
 // 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;
+template <typename T, typename U>
+CONSTEXPR_F diff_t operator-(civil_time<T>, civil_time<U>) = delete;
 
 using civil_year = civil_time<year_tag>;
 using civil_month = civil_time<month_tag>;
diff --git a/absl/time/internal/cctz/include/cctz/time_zone.h b/absl/time/internal/cctz/include/cctz/time_zone.h
index 55804ba68abb..0b9764ea72a9 100644
--- a/absl/time/internal/cctz/include/cctz/time_zone.h
+++ b/absl/time/internal/cctz/include/cctz/time_zone.h
@@ -119,7 +119,7 @@ class time_zone {
   // 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
+  // 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<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;
 
+  // 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;
 
  private:
   explicit time_zone(const Impl* impl) : impl_(impl) {}
+  const Impl& effective_impl() const;  // handles implicit UTC
   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.
@@ -184,6 +246,7 @@ time_zone utc_time_zone();
 time_zone fixed_time_zone(const seconds& offset);
 
 // 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();
 
 // 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 '*')
 //   - %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'.
 //
 // 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
 // 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
+// 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
diff --git a/absl/time/internal/cctz/include/cctz/zone_info_source.h b/absl/time/internal/cctz/include/cctz/zone_info_source.h
index 4d9d8f875ed8..20a76979370f 100644
--- a/absl/time/internal/cctz/include/cctz/zone_info_source.h
+++ b/absl/time/internal/cctz/include/cctz/zone_info_source.h
@@ -31,6 +31,11 @@ class 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()
+
+  // 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
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc
index f13cb4ee6b5f..c97df78c09c8 100644
--- a/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -754,23 +754,21 @@ void BM_Zone_LoadAllTimeZonesCached(benchmark::State& state) {
 }
 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::Impl::get(tz);
   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::Impl::get(tz);
   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) {
   cctz::time_zone tz;
diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc
index 592ab7d3cfdf..1b023848efa1 100644
--- a/absl/time/internal/cctz/src/time_zone_format.cc
+++ b/absl/time/internal/cctz/src/time_zone_format.cc
@@ -141,6 +141,9 @@ char* Format02d(char* ep, int v) {
 
 // Formats a UTC offset, like +00:00.
 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 = '+';
   if (offset < 0) {
     offset = -offset;  // bounded by 24h so no overflow
diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc
index 33c239841118..a90dda7603a8 100644
--- a/absl/time/internal/cctz/src/time_zone_format_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_format_test.cc
@@ -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));
 }
 
+// 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
 
 //
@@ -453,8 +464,8 @@ TEST(Format, ExtendedSecondOffset) {
   EXPECT_TRUE(load_time_zone("America/New_York", &tz));
   tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc);
   if (tz.lookup(tp).offset == -5 * 60 * 60) {
-    // We're likely dealing with zoneinfo that doesn't support really old
-    // timestamps, so America/New_York never looks to be on local mean time.
+    // It looks like the tzdata is only 32 bit (probably macOS),
+    // which bottoms out at 1901-12-13T20:45:52+00:00.
   } else {
     TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02");
     TestFormatSpecifier(tp, tz, "%Ez", "-04:56");
@@ -464,12 +475,10 @@ TEST(Format, ExtendedSecondOffset) {
 
   EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz));
   tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc);
-#if defined(__ANDROID__) && __ANDROID_API__ < 25
-  // Only Android 'N'.1 and beyond have this tz2016g transition.
-#else
-  TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
-  TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
-#endif
+  if (VersionCmp(tz, "2016g") >= 0) {
+    TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
+    TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
+  }
   tp += chrono::seconds(1);
   TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00");
 }
diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h
index f10972ae2b0f..e4bd3866a87b 100644
--- a/absl/time/internal/cctz/src/time_zone_if.h
+++ b/absl/time/internal/cctz/src/time_zone_if.h
@@ -41,9 +41,13 @@ class TimeZoneIf {
   virtual time_zone::civil_lookup MakeTime(
       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 bool NextTransition(time_point<seconds>* tp) const = 0;
-  virtual bool PrevTransition(time_point<seconds>* tp) const = 0;
 
  protected:
   TimeZoneIf() {}
diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc
index eb96c7ef5995..3062ccd3ceb0 100644
--- a/absl/time/internal/cctz/src/time_zone_impl.cc
+++ b/absl/time/internal/cctz/src/time_zone_impl.cc
@@ -83,15 +83,6 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
   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) {
diff --git a/absl/time/internal/cctz/src/time_zone_impl.h b/absl/time/internal/cctz/src/time_zone_impl.h
index fef7f22672dc..14965ef54bfb 100644
--- a/absl/time/internal/cctz/src/time_zone_impl.h
+++ b/absl/time/internal/cctz/src/time_zone_impl.h
@@ -37,15 +37,15 @@ class time_zone::Impl {
   // 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_; }
+  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.
   time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const {
@@ -59,28 +59,22 @@ class time_zone::Impl {
     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<seconds>* tp) const {
-    return zone_->NextTransition(tp);
+  bool NextTransition(const time_point<seconds>& tp,
+                      time_zone::civil_transition* trans) const {
+    return zone_->NextTransition(tp, trans);
   }
-  bool PrevTransition(time_point<seconds>* tp) const {
-    return zone_->PrevTransition(tp);
+  bool PrevTransition(const time_point<seconds>& 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:
   explicit Impl(const std::string& name);
   static const Impl* UTCImpl();
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index cdd11810583a..bf73635d4c6a 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -186,14 +186,13 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
   tt.is_dst = false;
   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().
   // 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
@@ -203,6 +202,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
            1546300800LL,  // 2019-01-01T00:00:00+00:00
            1577836800LL,  // 2020-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
        }) {
     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.
 
+  // 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
   // differences between the glibc and reference implementations (see
   // 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;
     return rc;
   }
+  std::string Version() const override {
+    // TODO: It would nice if the zoneinfo data included the tzdb version.
+    return std::string();
+  }
 
  protected:
   explicit FileZoneInfoSource(
@@ -654,14 +666,15 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
   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);
+  std::string Version() const override { return version_; }
 
  private:
-  explicit AndroidZoneInfoSource(FILE* fp, std::size_t len)
-      : FileZoneInfoSource(fp, len) {}
+  explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers)
+      : FileZoneInfoSource(fp, len), version_(vers) {}
+  std::string version_;
 };
 
 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.
   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.
   for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
                              "/system/usr/share/zoneinfo/tzdata"}) {
@@ -678,6 +692,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
     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 char* vers = (hbuf[11] == '\0') ? hbuf + 6 : "";
     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;
@@ -698,13 +713,13 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
       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)));
+            fp.release(), static_cast<std::size_t>(length), vers));
       }
     }
   }
+#endif  // __ANDROID__
   return nullptr;
 }
-#endif
 
 }  // namespace
 
@@ -722,9 +737,7 @@ bool TimeZoneInfo::Load(const std::string& name) {
   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());
@@ -885,17 +898,20 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
   return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
 }
 
+std::string TimeZoneInfo::Version() const {
+  return version_;
+}
+
 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<seconds>* tp) const {
+bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
+                                  time_zone::civil_transition* trans) const {
   if (transitions_.empty()) return false;
   const Transition* begin = &transitions_[0];
   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.
     ++begin;
   }
-  std::int_fast64_t unix_time = ToUnixSeconds(*tp);
+  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;
-    }
+  for (; tr != end; ++tr) {  // skip no-op transitions
+    std::uint_fast8_t prev_type_index =
+        (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_.
   if (tr == end) return false;
-  *tp = FromUnixSeconds(tr->unix_time);
+  trans->from = tr->prev_civil_sec + 1;
+  trans->to = tr->civil_sec;
   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;
   const Transition* begin = &transitions_[0];
   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.
     ++begin;
   }
-  std::int_fast64_t unix_time = ToUnixSeconds(*tp);
-  if (FromUnixSeconds(unix_time) != *tp) {
+  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);
+      trans->from = (--end)->prev_civil_sec + 1;
+      trans->to = end->civil_sec;
       return true;
     }
     unix_time += 1;  // ceils
@@ -940,14 +959,15 @@ bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const {
   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;
-    }
+  for (; tr != begin; --tr) {  // skip no-op transitions
+    std::uint_fast8_t prev_type_index =
+        (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_.
   if (tr == begin) return false;
-  *tp = FromUnixSeconds((--tr)->unix_time);
+  trans->from = (--tr)->prev_civil_sec + 1;
+  trans->to = tr->civil_sec;
   return true;
 }
 
diff --git a/absl/time/internal/cctz/src/time_zone_info.h b/absl/time/internal/cctz/src/time_zone_info.h
index d28443e26398..958e9b6bd744 100644
--- a/absl/time/internal/cctz/src/time_zone_info.h
+++ b/absl/time/internal/cctz/src/time_zone_info.h
@@ -74,9 +74,12 @@ class TimeZoneInfo : public TimeZoneIf {
       const time_point<seconds>& tp) const override;
   time_zone::civil_lookup MakeTime(
       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;
-  bool NextTransition(time_point<seconds>* tp) const override;
-  bool PrevTransition(time_point<seconds>* tp) const override;
 
  private:
   struct Header {  // counts of:
@@ -114,6 +117,7 @@ class TimeZoneInfo : public TimeZoneIf {
   std::uint_fast8_t default_transition_type_;  // for before first transition
   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
   bool extended_;            // future_spec_ was used to generate transitions
   year_t last_year_;         // the final year of the generated transitions
diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc
index 1d727bded982..074c8d0a4a40 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.cc
+++ b/absl/time/internal/cctz/src/time_zone_libc.cc
@@ -139,16 +139,22 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
   return cl;
 }
 
-std::string TimeZoneLibC::Description() const {
-  return local_ ? "localtime" : "UTC";
+bool TimeZoneLibC::NextTransition(const time_point<seconds>& tp,
+                                  time_zone::civil_transition* trans) const {
+  return false;
 }
 
-bool TimeZoneLibC::NextTransition(time_point<seconds>* tp) const {
+bool TimeZoneLibC::PrevTransition(const time_point<seconds>& tp,
+                                  time_zone::civil_transition* trans) const {
   return false;
 }
 
-bool TimeZoneLibC::PrevTransition(time_point<seconds>* tp) const {
-  return false;
+std::string TimeZoneLibC::Version() const {
+  return std::string();  // unknown
+}
+
+std::string TimeZoneLibC::Description() const {
+  return local_ ? "localtime" : "UTC";
 }
 
 }  // namespace cctz
diff --git a/absl/time/internal/cctz/src/time_zone_libc.h b/absl/time/internal/cctz/src/time_zone_libc.h
index 4c64cd3462f8..4e40c61ab243 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.h
+++ b/absl/time/internal/cctz/src/time_zone_libc.h
@@ -35,9 +35,12 @@ class TimeZoneLibC : public TimeZoneIf {
       const time_point<seconds>& tp) const override;
   time_zone::civil_lookup MakeTime(
       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;
-  bool NextTransition(time_point<seconds>* tp) const override;
-  bool PrevTransition(time_point<seconds>* tp) const override;
 
  private:
   const bool local_;  // localtime or UTC
diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc
index 2f6cd98b9b43..f2d151e4d5e5 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -61,20 +61,43 @@ int __system_property_get(const char* name, char* value) {
 #endif
 
 std::string time_zone::name() const {
-  return time_zone::Impl::get(*this).name();
+  return effective_impl().Name();
 }
 
 time_zone::absolute_lookup time_zone::lookup(
     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 {
-  return time_zone::Impl::get(*this).MakeTime(cs);
+  return effective_impl().MakeTime(cs);
 }
 
-bool operator==(time_zone lhs, time_zone rhs) {
-  return &time_zone::Impl::get(lhs) == &time_zone::Impl::get(rhs);
+bool time_zone::next_transition(const time_point<seconds>& tp,
+                                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) {
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index cd9fc2362752..551292fb55e0 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -651,6 +651,17 @@ time_zone LoadZone(const std::string& name) {
     /* EXPECT_STREQ(zone, al.abbr); */                            \
   } 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
 
 TEST(TimeZones, LoadZonesConcurrently) {
@@ -981,6 +992,69 @@ TEST(MakeTime, SysSecondsLimits) {
   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) {
   const time_zone tz = LoadZone("America/New_York");
 
@@ -1104,35 +1178,31 @@ TEST(TimeZoneEdgeCase, PacificApia) {
 TEST(TimeZoneEdgeCase, AfricaCairo) {
   const time_zone tz = LoadZone("Africa/Cairo");
 
-#if defined(__ANDROID__) && __ANDROID_API__ < 21
-  // Only Android 'L' and beyond have this tz2014c transition.
-#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)
-  auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
-#endif
+  if (VersionCmp(tz, "2014c") >= 0) {
+    // An interesting case of midnight not existing.
+    //
+    //   1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
+    //   1400191200 == Fri, 16 May 2014 01:00:00 +0300 (EEST)
+    auto tp = convert(civil_second(2014, 5, 15, 23, 59, 59), tz);
+    ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
+    tp += absl::time_internal::cctz::seconds(1);
+    ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
+  }
 }
 
 TEST(TimeZoneEdgeCase, AfricaMonrovia) {
   const time_zone tz = LoadZone("Africa/Monrovia");
 
-#if defined(__ANDROID__) && __ANDROID_API__ < 26
-  // Only Android 'O' and beyond have this tz2017b transition.
-#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)
-  auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
-#endif
+  if (VersionCmp(tz, "2017b") >= 0) {
+    // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
+    //
+    //   63593069 == Thu,  6 Jan 1972 23:59:59 -0044 (MMT)
+    //   63593070 == Fri,  7 Jan 1972 00:44:30 +0000 (GMT)
+    auto tp = convert(civil_second(1972, 1, 6, 23, 59, 59), tz);
+    ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
+    tp += absl::time_internal::cctz::seconds(1);
+    ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
+  }
 }
 
 TEST(TimeZoneEdgeCase, AmericaJamaica) {
@@ -1144,28 +1214,29 @@ TEST(TimeZoneEdgeCase, AmericaJamaica) {
   const time_zone tz = LoadZone("America/Jamaica");
 
   // Before the first transition.
-  auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz);
-#if AMERICA_JAMAICA_PRE_1913_OFFSET_FIX
-  // Commit 907241e: Fix off-by-1 error for Jamaica and T&C before 1913.
-  // Until that commit has made its way into a full release we avoid the
-  // expectations on the -18430 offset below.  TODO: Uncomment these.
-  ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false,
-             tz.lookup(tp).abbr);
-
-  // Over the first (abbreviation-change only) transition.
-  //   -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
-  //   -2524503169 == Wed,  1 Jan 1890 00:00:00 -0507 (KMT)
-  tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz);
-  ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false,
-             tz.lookup(tp).abbr);
-  tp += absl::time_internal::cctz::seconds(1);
-  ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT");
-#endif
+  if (!tz.version().empty() && VersionCmp(tz, "2018d") >= 0) {
+    // We avoid the expectations on the -18430 offset below unless we are
+    // certain we have commit 907241e (Fix off-by-1 error for Jamaica and
+    // T&C before 1913) from 2018d.  TODO: Remove the "version() not empty"
+    // part when 2018d is generally available from /usr/share/zoneinfo.
+    auto tp = convert(civil_second(1889, 12, 31, 0, 0, 0), tz);
+    ExpectTime(tp, tz, 1889, 12, 31, 0, 0, 0, -18430, false,
+               tz.lookup(tp).abbr);
+
+    // Over the first (abbreviation-change only) transition.
+    //   -2524503170 == Tue, 31 Dec 1889 23:59:59 -0507 (LMT)
+    //   -2524503169 == Wed,  1 Jan 1890 00:00:00 -0507 (KMT)
+    tp = convert(civil_second(1889, 12, 31, 23, 59, 59), tz);
+    ExpectTime(tp, tz, 1889, 12, 31, 23, 59, 59, -18430, false,
+               tz.lookup(tp).abbr);
+    tp += absl::time_internal::cctz::seconds(1);
+    ExpectTime(tp, tz, 1890, 1, 1, 0, 0, 0, -18430, false, "KMT");
+  }
 
   // Over the last (DST) transition.
   //     436341599 == Sun, 30 Oct 1983 01:59:59 -0400 (EDT)
   //     436341600 == Sun, 30 Oct 1983 01:00:00 -0500 (EST)
-  tp = convert(civil_second(1983, 10, 30, 1, 59, 59), tz);
+  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");
   tp += absl::time_internal::cctz::seconds(1);
   ExpectTime(tp, tz, 1983, 10, 30, 1, 0, 0, -5 * 3600, false, "EST");
diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc
index ee7500b64f28..bf2d2d2d2b53 100644
--- a/absl/time/internal/cctz/src/zone_info_source.cc
+++ b/absl/time/internal/cctz/src/zone_info_source.cc
@@ -20,6 +20,7 @@ namespace cctz {
 
 // Defined out-of-line to avoid emitting a weak vtable in all TUs.
 ZoneInfoSource::~ZoneInfoSource() {}
+std::string ZoneInfoSource::Version() const { return std::string(); }
 
 }  // namespace cctz
 }  // namespace time_internal
diff --git a/absl/types/variant.h b/absl/types/variant.h
index fd1d49ac890a..9d98a9ed0edb 100644
--- a/absl/types/variant.h
+++ b/absl/types/variant.h
@@ -248,7 +248,7 @@ using variant_alternative_t = typename variant_alternative<I, T>::type;
 //
 // Example:
 //
-//   absl::variant<int, std::string> bar = 42;
+//   absl::variant<int, std::string> foo = 42;
 //   if (absl::holds_alternative<int>(foo)) {
 //       std::cout << "The variant holds an integer";
 //   }