Export of internal Abseil changes

--
05a099a580753f8e96cee38572e94dcdc079361b by Abseil Team <absl-team@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 405966217

--
c6b81e9ebc183d8389f14ecd091c8bad08cfe0aa by Abseil Team <absl-team@google.com>:

Add `inline_element_size` to hashtablez (so that we can compute the weighted load factors properly e.g., in b/187896534).

PiperOrigin-RevId: 405917711

--
3e3673de4e54e4142c54b09e1644dfa3de4bb296 by Abseil Team <absl-team@google.com>:

align indent of code comment in mutex.h

PiperOrigin-RevId: 405871997

--
2248301a5b14f8d2be5b2e9088f3528a353ea491 by Derek Mauro <dmauro@google.com>:

Internal change

PiperOrigin-RevId: 405639236

--
bc7d3c56fdad3dde4b89324af142529f2afe5f1b by Abseil Team <absl-team@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 405508045

--
66472387276ef02505d99195747be862768bb35b by Laramie Leavitt <lar@google.com>:

Also use uint8_t golden values in randen_test.cc

This makes randen_test, randen_slow_test, and randen_hwaes_test essentially
identical, as is the intent.

PiperOrigin-RevId: 405484423
GitOrigin-RevId: 05a099a580753f8e96cee38572e94dcdc079361b
Change-Id: I3dd5b0cfdb98d6e1ab02266194ba67d15428c2f8
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index d1df524..ffaee19 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -876,6 +876,22 @@
     ],
 )
 
+cc_test(
+    name = "sample_element_size_test",
+    srcs = ["sample_element_size_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    tags = NOTEST_TAGS_NONMOBILE,
+    visibility = ["//visibility:private"],
+    deps = [
+        ":flat_hash_map",
+        ":flat_hash_set",
+        ":node_hash_map",
+        ":node_hash_set",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
 cc_library(
     name = "btree",
     srcs = [
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 9b8a750..78584d2 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -894,3 +894,18 @@
     absl::unordered_map_modifiers_test
     GTest::gmock_main
 )
+
+absl_cc_test(
+  NAME
+    sample_element_size_test
+  SRCS
+    "sample_element_size_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::flat_hash_map
+    absl::flat_hash_set
+    absl::node_hash_map
+    absl::node_hash_set
+    GTest::gmock_main
+)
diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc
index 7070912..40cce04 100644
--- a/absl/container/internal/hashtablez_sampler.cc
+++ b/absl/container/internal/hashtablez_sampler.cc
@@ -55,6 +55,9 @@
   return *sampler;
 }
 
+// TODO(bradleybear): The comments at this constructors declaration say that the
+// fields are not initialized, but this definition does initialize the fields.
+// Something needs to be cleaned up.
 HashtablezInfo::HashtablezInfo() { PrepareForSampling(); }
 HashtablezInfo::~HashtablezInfo() = default;
 
@@ -98,10 +101,12 @@
   return state == kForce;
 }
 
-HashtablezInfo* SampleSlow(int64_t* next_sample) {
+HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) {
   if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
     *next_sample = 1;
-    return GlobalHashtablezSampler().Register();
+    HashtablezInfo* result = GlobalHashtablezSampler().Register();
+    result->inline_element_size = inline_element_size;
+    return result;
   }
 
 #if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@@ -123,10 +128,12 @@
   // that case.
   if (first) {
     if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr;
-    return SampleSlow(next_sample);
+    return SampleSlow(next_sample, inline_element_size);
   }
 
-  return GlobalHashtablezSampler().Register();
+  HashtablezInfo* result = GlobalHashtablezSampler().Register();
+  result->inline_element_size = inline_element_size;
+  return result;
 #endif
 }
 
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h
index 812118e..91fcdb3 100644
--- a/absl/container/internal/hashtablez_sampler.h
+++ b/absl/container/internal/hashtablez_sampler.h
@@ -91,6 +91,7 @@
   absl::Time create_time;
   int32_t depth;
   void* stack[kMaxStackDepth];
+  size_t inline_element_size;
 };
 
 inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
@@ -143,7 +144,7 @@
       std::memory_order_relaxed);
 }
 
-HashtablezInfo* SampleSlow(int64_t* next_sample);
+HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size);
 void UnsampleSlow(HashtablezInfo* info);
 
 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
@@ -238,12 +239,14 @@
 
 // Returns an RAII sampling handle that manages registration and unregistation
 // with the global sampler.
-inline HashtablezInfoHandle Sample() {
+inline HashtablezInfoHandle Sample(
+    size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) {
 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
   if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) {
     return HashtablezInfoHandle(nullptr);
   }
-  return HashtablezInfoHandle(SampleSlow(&global_next_sample));
+  return HashtablezInfoHandle(
+      SampleSlow(&global_next_sample, inline_element_size));
 #else
   return HashtablezInfoHandle(nullptr);
 #endif  // !ABSL_PER_THREAD_TLS
diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc
index f053c19..449619a 100644
--- a/absl/container/internal/hashtablez_sampler_test.cc
+++ b/absl/container/internal/hashtablez_sampler_test.cc
@@ -78,10 +78,12 @@
 
 TEST(HashtablezInfoTest, PrepareForSampling) {
   absl::Time test_start = absl::Now();
+  const size_t test_element_size = 17;
   HashtablezInfo info;
   absl::MutexLock l(&info.init_mu);
   info.PrepareForSampling();
 
+  info.inline_element_size = test_element_size;
   EXPECT_EQ(info.capacity.load(), 0);
   EXPECT_EQ(info.size.load(), 0);
   EXPECT_EQ(info.num_erases.load(), 0);
@@ -93,6 +95,7 @@
   EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
   EXPECT_EQ(info.max_reserve.load(), 0);
   EXPECT_GE(info.create_time, test_start);
+  EXPECT_EQ(info.inline_element_size, test_element_size);
 
   info.capacity.store(1, std::memory_order_relaxed);
   info.size.store(1, std::memory_order_relaxed);
@@ -116,6 +119,7 @@
   EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
   EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
   EXPECT_EQ(info.max_reserve.load(), 0);
+  EXPECT_EQ(info.inline_element_size, test_element_size);
   EXPECT_GE(info.create_time, test_start);
 }
 
@@ -154,9 +158,11 @@
 }
 
 TEST(HashtablezInfoTest, RecordErase) {
+  const size_t test_element_size = 29;
   HashtablezInfo info;
   absl::MutexLock l(&info.init_mu);
   info.PrepareForSampling();
+  info.inline_element_size = test_element_size;
   EXPECT_EQ(info.num_erases.load(), 0);
   EXPECT_EQ(info.size.load(), 0);
   RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
@@ -164,12 +170,15 @@
   RecordEraseSlow(&info);
   EXPECT_EQ(info.size.load(), 0);
   EXPECT_EQ(info.num_erases.load(), 1);
+  EXPECT_EQ(info.inline_element_size, test_element_size);
 }
 
 TEST(HashtablezInfoTest, RecordRehash) {
+  const size_t test_element_size = 31;
   HashtablezInfo info;
   absl::MutexLock l(&info.init_mu);
   info.PrepareForSampling();
+  info.inline_element_size = test_element_size;
   RecordInsertSlow(&info, 0x1, 0);
   RecordInsertSlow(&info, 0x2, kProbeLength);
   RecordInsertSlow(&info, 0x4, kProbeLength);
@@ -188,6 +197,7 @@
   EXPECT_EQ(info.total_probe_length.load(), 3);
   EXPECT_EQ(info.num_erases.load(), 0);
   EXPECT_EQ(info.num_rehashes.load(), 1);
+  EXPECT_EQ(info.inline_element_size, test_element_size);
 }
 
 TEST(HashtablezInfoTest, RecordReservation) {
@@ -208,12 +218,13 @@
 
 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
 TEST(HashtablezSamplerTest, SmallSampleParameter) {
+  const size_t test_element_size = 31;
   SetHashtablezEnabled(true);
   SetHashtablezSampleParameter(100);
 
   for (int i = 0; i < 1000; ++i) {
     int64_t next_sample = 0;
-    HashtablezInfo* sample = SampleSlow(&next_sample);
+    HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
     EXPECT_GT(next_sample, 0);
     EXPECT_NE(sample, nullptr);
     UnsampleSlow(sample);
@@ -221,12 +232,13 @@
 }
 
 TEST(HashtablezSamplerTest, LargeSampleParameter) {
+  const size_t test_element_size = 31;
   SetHashtablezEnabled(true);
   SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
 
   for (int i = 0; i < 1000; ++i) {
     int64_t next_sample = 0;
-    HashtablezInfo* sample = SampleSlow(&next_sample);
+    HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
     EXPECT_GT(next_sample, 0);
     EXPECT_NE(sample, nullptr);
     UnsampleSlow(sample);
@@ -234,13 +246,14 @@
 }
 
 TEST(HashtablezSamplerTest, Sample) {
+  const size_t test_element_size = 31;
   SetHashtablezEnabled(true);
   SetHashtablezSampleParameter(100);
   int64_t num_sampled = 0;
   int64_t total = 0;
   double sample_rate = 0.0;
   for (int i = 0; i < 1000000; ++i) {
-    HashtablezInfoHandle h = Sample();
+    HashtablezInfoHandle h = Sample(test_element_size);
     ++total;
     if (HashtablezInfoHandlePeer::IsSampled(h)) {
       ++num_sampled;
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 47e5a22..12682b3 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -1643,7 +1643,7 @@
     // bound more carefully.
     if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value &&
         slots_ == nullptr) {
-      infoz() = Sample();
+      infoz() = Sample(sizeof(slot_type));
     }
 
     char* mem = static_cast<char*>(Allocate<alignof(slot_type)>(
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index b46c492..362b3ca 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -2075,6 +2075,7 @@
           std::memory_order_relaxed)]++;
       reservations[info.max_reserve.load(std::memory_order_relaxed)]++;
     }
+    EXPECT_EQ(info.inline_element_size, sizeof(int64_t));
     ++end_size;
   });
 
diff --git a/absl/container/sample_element_size_test.cc b/absl/container/sample_element_size_test.cc
new file mode 100644
index 0000000..b23626b
--- /dev/null
+++ b/absl/container/sample_element_size_test.cc
@@ -0,0 +1,114 @@
+// 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
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/container/node_hash_map.h"
+#include "absl/container/node_hash_set.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+namespace {
+
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+// Create some tables of type `Table`, then look at all the new
+// `HashtablezInfo`s to make sure that the `inline_element_size ==
+// expected_element_size`.  The `inline_element_size` is the amount of memory
+// allocated for each slot of a hash table, that is `sizeof(slot_type)`.  Add
+// the new `HashtablezInfo`s to `preexisting_info`.  Store all the new tables
+// into `tables`.
+template <class Table>
+void TestInlineElementSize(
+    HashtablezSampler& sampler,
+    // clang-tidy gives a false positive on this declaration.  This unordered
+    // set cannot be flat_hash_set, however, since that would introduce a mutex
+    // deadlock.
+    std::unordered_set<const HashtablezInfo*>& preexisting_info,  // NOLINT
+    std::vector<Table>& tables, const typename Table::value_type& elt,
+    size_t expected_element_size) {
+  for (int i = 0; i < 10; ++i) {
+    // We create a new table and must store it somewhere so that when we store
+    // a pointer to the resulting `HashtablezInfo` into `preexisting_info`
+    // that we aren't storing a dangling pointer.
+    tables.emplace_back();
+    // We must insert an element to get a hashtablez to instantiate.
+    tables.back().insert(elt);
+  }
+  size_t new_count = 0;
+  sampler.Iterate([&](const HashtablezInfo& info) {
+    if (preexisting_info.insert(&info).second) {
+      EXPECT_EQ(info.inline_element_size, expected_element_size);
+      ++new_count;
+    }
+  });
+  // Make sure we actually did get a new hashtablez.
+  EXPECT_GT(new_count, 0);
+}
+
+struct bigstruct {
+  char a[1000];
+  friend bool operator==(const bigstruct& x, const bigstruct& y) {
+    return memcmp(x.a, y.a, sizeof(x.a)) == 0;
+  }
+  template <typename H>
+  friend H AbslHashValue(H h, const bigstruct& c) {
+    return H::combine_contiguous(std::move(h), c.a, sizeof(c.a));
+  }
+};
+#endif
+
+TEST(FlatHashMap, SampleElementSize) {
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+  // Enable sampling even if the prod default is off.
+  SetHashtablezEnabled(true);
+  SetHashtablezSampleParameter(1);
+
+  auto& sampler = GlobalHashtablezSampler();
+  std::vector<flat_hash_map<int, bigstruct>> flat_map_tables;
+  std::vector<flat_hash_set<bigstruct>> flat_set_tables;
+  std::vector<node_hash_map<int, bigstruct>> node_map_tables;
+  std::vector<node_hash_set<bigstruct>> node_set_tables;
+
+  // It takes thousands of new tables after changing the sampling parameters
+  // before you actually get some instrumentation.  And if you must actually
+  // put something into those tables.
+  for (int i = 0; i < 10000; ++i) {
+    flat_map_tables.emplace_back();
+    flat_map_tables.back()[i] = bigstruct{};
+  }
+
+  // clang-tidy gives a false positive on this declaration.  This unordered set
+  // cannot be a flat_hash_set, however, since that would introduce a mutex
+  // deadlock.
+  std::unordered_set<const HashtablezInfo*> preexisting_info;  // NOLINT
+  sampler.Iterate(
+      [&](const HashtablezInfo& info) { preexisting_info.insert(&info); });
+  TestInlineElementSize(sampler, preexisting_info, flat_map_tables,
+                        {0, bigstruct{}}, sizeof(int) + sizeof(bigstruct));
+  TestInlineElementSize(sampler, preexisting_info, node_map_tables,
+                        {0, bigstruct{}}, sizeof(void*));
+  TestInlineElementSize(sampler, preexisting_info, flat_set_tables,  //
+                        bigstruct{}, sizeof(bigstruct));
+  TestInlineElementSize(sampler, preexisting_info, node_set_tables,  //
+                        bigstruct{}, sizeof(void*));
+#endif
+}
+
+}  // namespace
+}  // namespace container_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/random/internal/randen_test.cc b/absl/random/internal/randen_test.cc
index c186fe0..92773b8 100644
--- a/absl/random/internal/randen_test.cc
+++ b/absl/random/internal/randen_test.cc
@@ -23,9 +23,6 @@
 
 using absl::random_internal::Randen;
 
-// Local state parameters.
-constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t);
-
 TEST(RandenTest, CopyAndMove) {
   static_assert(std::is_copy_constructible<Randen>::value,
                 "Randen must be copy constructible");
@@ -41,30 +38,38 @@
 }
 
 TEST(RandenTest, Default) {
-  constexpr uint64_t kGolden[] = {
-      0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977,
-      0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912,
-      0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6,
-      0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a,
-      0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37,
-      0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556,
-      0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42,
-      0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc,
-      0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f,
-      0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3,
-      0x026ff374c101da7e, 0x811ef0821c3de851,
+  constexpr uint8_t kGolden[] = {
+      0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d,
+      0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3,
+      0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f,
+      0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0,
+      0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff,
+      0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2,
+      0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5,
+      0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c,
+      0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56,
+      0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4,
+      0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91,
+      0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5,
+      0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d,
+      0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58,
+      0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e,
+      0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49,
+      0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0,
+      0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6,
+      0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61,
+      0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35,
+      0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c,
+      0x82, 0xf0, 0x1e, 0x81,
   };
 
-  alignas(16) uint64_t state[kStateSizeT];
+  alignas(16) uint8_t state[Randen::kStateBytes];
   std::memset(state, 0, sizeof(state));
 
   Randen r;
   r.Generate(state);
 
-  auto id = std::begin(state);
-  for (const auto& elem : kGolden) {
-    EXPECT_EQ(elem, *id++);
-  }
+  EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state)));
 }
 
 }  // namespace
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index f49e0c8..38338f2 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -778,9 +778,9 @@
 //
 // Usage to wake T is:
 //       mu.Lock();
-//      // process data, possibly establishing C
-//      if (C) { cv->Signal(); }
-//      mu.Unlock();
+//       // process data, possibly establishing C
+//       if (C) { cv->Signal(); }
+//       mu.Unlock();
 //
 // If C may be useful to more than one waiter, use `SignalAll()` instead of
 // `Signal()`.
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index f2777d9..4f175d9 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -39,6 +39,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <fstream>
 #include <functional>
 #include <memory>
 #include <sstream>
@@ -649,7 +650,7 @@
 
   // Open the zoneinfo file.
   auto fp = FOpen(path.c_str(), "rb");
-  if (fp.get() == nullptr) return nullptr;
+  if (fp == nullptr) return nullptr;
   return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(std::move(fp)));
 }
 
@@ -674,7 +675,7 @@
   for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
                              "/system/usr/share/zoneinfo/tzdata"}) {
     auto fp = FOpen(tzdata, "rb");
-    if (fp.get() == nullptr) continue;
+    if (fp == nullptr) continue;
 
     char hbuf[24];  // covers header.zonetab_offset too
     if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
@@ -708,6 +709,69 @@
   return nullptr;
 }
 
+// A zoneinfo source for use inside Fuchsia components. This attempts to
+// read zoneinfo files from one of several known paths in a component's
+// incoming namespace. [Config data][1] is preferred, but package-specific
+// resources are also supported.
+//
+// Fuchsia's implementation supports `FileZoneInfoSource::Version()`.
+//
+// [1]:
+// https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component
+class FuchsiaZoneInfoSource : public FileZoneInfoSource {
+ public:
+  static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
+  std::string Version() const override { return version_; }
+
+ private:
+  explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version)
+      : FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {}
+  std::string version_;
+};
+
+std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open(
+    const std::string& name) {
+  // Use of the "file:" prefix is intended for testing purposes only.
+  const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
+
+  // Prefixes where a Fuchsia component might find zoneinfo files,
+  // in descending order of preference.
+  const auto kTzdataPrefixes = {
+      "/config/data/tzdata/",
+      "/pkg/data/tzdata/",
+      "/data/tzdata/",
+  };
+  const auto kEmptyPrefix = {""};
+  const bool name_absolute = (pos != name.size() && name[pos] == '/');
+  const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes;
+
+  // Fuchsia builds place zoneinfo files at "<prefix><format><name>".
+  for (const std::string prefix : prefixes) {
+    std::string path = prefix;
+    if (!prefix.empty()) path += "zoneinfo/tzif2/";  // format
+    path.append(name, pos, std::string::npos);
+
+    auto fp = FOpen(path.c_str(), "rb");
+    if (fp == nullptr) continue;
+
+    std::string version;
+    if (!prefix.empty()) {
+      // Fuchsia builds place the version in "<prefix>revision.txt".
+      std::ifstream version_stream(prefix + "revision.txt");
+      if (version_stream.is_open()) {
+        // revision.txt should contain no newlines, but to be
+        // defensive we read just the first line.
+        std::getline(version_stream, version);
+      }
+    }
+
+    return std::unique_ptr<ZoneInfoSource>(
+        new FuchsiaZoneInfoSource(std::move(fp), std::move(version)));
+  }
+
+  return nullptr;
+}
+
 }  // namespace
 
 bool TimeZoneInfo::Load(const std::string& name) {
@@ -725,6 +789,7 @@
       name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> {
         if (auto z = FileZoneInfoSource::Open(n)) return z;
         if (auto z = AndroidZoneInfoSource::Open(n)) return z;
+        if (auto z = FuchsiaZoneInfoSource::Open(n)) return z;
         return nullptr;
       });
   return zip != nullptr && Load(zip.get());
diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc
index efdea64..898d04c 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -28,6 +28,13 @@
 #include <vector>
 #endif
 
+#if defined(__Fuchsia__)
+#include <fuchsia/intl/cpp/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/sys/cpp/component_context.h>
+#include <zircon/types.h>
+#endif
+
 #include <cstdlib>
 #include <cstring>
 #include <string>
@@ -140,6 +147,48 @@
   }
   CFRelease(tz_default);
 #endif
+#if defined(__Fuchsia__)
+  std::string primary_tz;
+  [&]() {
+    // Note: We can't use the synchronous FIDL API here because it doesn't
+    // allow timeouts; if the FIDL call failed, local_time_zone() would never
+    // return.
+
+    const zx::duration kTimeout = zx::msec(500);
+
+    // Don't attach to the thread because otherwise the thread's dispatcher
+    // would be set to null when the loop is destroyed, causing any other FIDL
+    // code running on the same thread to crash.
+    async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
+    std::unique_ptr<sys::ComponentContext> context =
+        sys::ComponentContext::Create();
+
+    fuchsia::intl::PropertyProviderHandle handle;
+    zx_status_t status = context->svc()->Connect(handle.NewRequest());
+    if (status != ZX_OK) {
+      return;
+    }
+
+    fuchsia::intl::PropertyProviderPtr intl_provider;
+    status = intl_provider.Bind(std::move(handle), loop.dispatcher());
+    if (status != ZX_OK) {
+      return;
+    }
+
+    intl_provider->GetProfile(
+        [&loop, &primary_tz](fuchsia::intl::Profile profile) {
+          if (!profile.time_zones().empty()) {
+            primary_tz = profile.time_zones()[0].id;
+          }
+          loop.Quit();
+        });
+    loop.Run(zx::deadline_after(kTimeout));
+  }();
+
+  if (!primary_tz.empty()) {
+    zone = primary_tz.c_str();
+  }
+#endif
 
   // Allow ${TZ} to override to default zone.
   char* tz_env = nullptr;
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 8751b34..0226ab7 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -1027,8 +1027,12 @@
 #endif
     const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
     tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut);
+#if defined(__Fuchsia__)
+    // Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527).
+#else
     EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
 #endif
+#endif
   }
 }
 
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index 51191b5..8ee898b 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2021c
+2021e
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
index 58e9fdf..ccc57c9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
index aeda06b..906d8d5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
index e3934e4..8b2dd52 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
Binary files differ