Import of CCTZ from GitHub.

PiperOrigin-RevId: 833899408
Change-Id: I5b6ee41676a8e0e207462e0b30dde3843478ef19
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index c49d621..9145d8b 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -448,7 +448,12 @@
   "debugging/leak_check.cc"
 )
 
-if(NOT MSVC)
+if(MSVC)
+  list(APPEND ABSL_INTERNAL_DLL_FILES
+    "time/internal/cctz/src/time_zone_name_win.cc"
+    "time/internal/cctz/src/time_zone_name_win.h"
+  )
+else()
   list(APPEND ABSL_INTERNAL_DLL_FILES
     "flags/commandlineflag.cc"
     "flags/commandlineflag.h"
diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt
index ea91ba3..34a5ad4 100644
--- a/absl/time/CMakeLists.txt
+++ b/absl/time/CMakeLists.txt
@@ -77,6 +77,8 @@
     "internal/cctz/src/time_zone_posix.h"
     "internal/cctz/src/tzfile.h"
     "internal/cctz/src/zone_info_source.cc"
+    $<$<PLATFORM_ID:Windows>:internal/cctz/src/time_zone_name_win.cc>
+    $<$<PLATFORM_ID:Windows>:internal/cctz/src/time_zone_name_win.h>
   COPTS
     ${ABSL_DEFAULT_COPTS}
   DEPS
diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel
index 6e17874..e7e2ee0 100644
--- a/absl/time/internal/cctz/BUILD.bazel
+++ b/absl/time/internal/cctz/BUILD.bazel
@@ -59,7 +59,13 @@
         "src/time_zone_posix.h",
         "src/tzfile.h",
         "src/zone_info_source.cc",
-    ],
+    ] + select({
+        "@platforms//os:windows": [
+            "src/time_zone_name_win.cc",
+            "src/time_zone_name_win.h",
+        ],
+        "//conditions:default": [],
+    }),
     hdrs = [
         "include/cctz/time_zone.h",
         "include/cctz/zone_info_source.h",
diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc
index e8f1d93..d1078de 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -32,31 +32,6 @@
 #include <zircon/types.h>
 #endif
 
-#if defined(_WIN32)
-// Include only when <icu.h> is available.
-// https://learn.microsoft.com/en-us/windows/win32/intl/international-components-for-unicode--icu-
-// https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255
-#if defined(__has_include)
-#if __has_include(<icu.h>)
-#define USE_WIN32_LOCAL_TIME_ZONE
-#include <windows.h>
-#pragma push_macro("_WIN32_WINNT")
-#pragma push_macro("NTDDI_VERSION")
-// Minimum _WIN32_WINNT and NTDDI_VERSION to use ucal_getTimeZoneIDForWindowsID
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0A00  // == _WIN32_WINNT_WIN10
-#undef NTDDI_VERSION
-#define NTDDI_VERSION 0x0A000004  // == NTDDI_WIN10_RS3
-#include <icu.h>
-#pragma pop_macro("NTDDI_VERSION")
-#pragma pop_macro("_WIN32_WINNT")
-#include <timezoneapi.h>
-
-#include <atomic>
-#endif  // __has_include(<icu.h>)
-#endif  // __has_include
-#endif  // _WIN32
-
 #include <array>
 #include <cstdint>
 #include <cstdlib>
@@ -66,87 +41,15 @@
 #include "absl/time/internal/cctz/src/time_zone_fixed.h"
 #include "absl/time/internal/cctz/src/time_zone_impl.h"
 
+#if defined(_WIN32)
+#include "absl/time/internal/cctz/src/time_zone_name_win.h"
+#endif  // _WIN32
+
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace time_internal {
 namespace cctz {
 
-namespace {
-#if defined(USE_WIN32_LOCAL_TIME_ZONE)
-// True if we have already failed to load the API.
-static std::atomic_bool g_ucal_getTimeZoneIDForWindowsIDUnavailable;
-static std::atomic<decltype(ucal_getTimeZoneIDForWindowsID)*>
-    g_ucal_getTimeZoneIDForWindowsIDRef;
-
-std::string win32_local_time_zone() {
-  // If we have already failed to load the API, then just give up.
-  if (g_ucal_getTimeZoneIDForWindowsIDUnavailable.load()) {
-    return "";
-  }
-
-  auto ucal_getTimeZoneIDForWindowsIDFunc =
-      g_ucal_getTimeZoneIDForWindowsIDRef.load();
-  if (ucal_getTimeZoneIDForWindowsIDFunc == nullptr) {
-    // If we have already failed to load the API, then just give up.
-    if (g_ucal_getTimeZoneIDForWindowsIDUnavailable.load()) {
-      return "";
-    }
-
-    const HMODULE icudll =
-        ::LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
-
-    if (icudll == nullptr) {
-      g_ucal_getTimeZoneIDForWindowsIDUnavailable.store(true);
-      return "";
-    }
-
-    ucal_getTimeZoneIDForWindowsIDFunc =
-        reinterpret_cast<decltype(ucal_getTimeZoneIDForWindowsID)*>(
-            ::GetProcAddress(icudll, "ucal_getTimeZoneIDForWindowsID"));
-
-    if (ucal_getTimeZoneIDForWindowsIDFunc == nullptr) {
-      g_ucal_getTimeZoneIDForWindowsIDUnavailable.store(true);
-      return "";
-    }
-    // store-race is not a problem here, because ::GetProcAddress() returns the
-    // same address for the same function in the same DLL.
-    g_ucal_getTimeZoneIDForWindowsIDRef.store(
-        ucal_getTimeZoneIDForWindowsIDFunc);
-
-    // We intentionally do not call ::FreeLibrary() here to avoid frequent DLL
-    // loadings and unloading. As "icu.dll" is a system library, keeping it on
-    // memory is supposed to have no major drawback.
-  }
-
-  DYNAMIC_TIME_ZONE_INFORMATION info = {};
-  if (::GetDynamicTimeZoneInformation(&info) == TIME_ZONE_ID_INVALID) {
-    return "";
-  }
-
-  std::array<UChar, 128> buffer;
-  UErrorCode status = U_ZERO_ERROR;
-  const auto num_chars_in_buffer = ucal_getTimeZoneIDForWindowsIDFunc(
-      reinterpret_cast<const UChar*>(info.TimeZoneKeyName), -1, nullptr,
-      buffer.data(), static_cast<int32_t>(buffer.size()), &status);
-  if (status != U_ZERO_ERROR || num_chars_in_buffer <= 0 ||
-      num_chars_in_buffer > static_cast<int32_t>(buffer.size())) {
-    return "";
-  }
-
-  const int num_bytes_in_utf8 = ::WideCharToMultiByte(
-      CP_UTF8, 0, reinterpret_cast<const wchar_t*>(buffer.data()),
-      static_cast<int>(num_chars_in_buffer), nullptr, 0, nullptr, nullptr);
-  std::string local_time_str;
-  local_time_str.resize(static_cast<size_t>(num_bytes_in_utf8));
-  ::WideCharToMultiByte(
-      CP_UTF8, 0, reinterpret_cast<const wchar_t*>(buffer.data()),
-      static_cast<int>(num_chars_in_buffer), &local_time_str[0],
-      num_bytes_in_utf8, nullptr, nullptr);
-  return local_time_str;
-}
-#endif  // USE_WIN32_LOCAL_TIME_ZONE
-}  // namespace
-
 std::string time_zone::name() const { return effective_impl().Name(); }
 
 time_zone::absolute_lookup time_zone::lookup(
@@ -261,8 +164,8 @@
     zone = primary_tz.c_str();
   }
 #endif
-#if defined(USE_WIN32_LOCAL_TIME_ZONE)
-  std::string win32_tz = win32_local_time_zone();
+#if defined(_WIN32)
+  std::string win32_tz = GetWindowsLocalTimeZone();
   if (!win32_tz.empty()) {
     zone = win32_tz.c_str();
   }
diff --git a/absl/time/internal/cctz/src/time_zone_name_win.cc b/absl/time/internal/cctz/src/time_zone_name_win.cc
new file mode 100644
index 0000000..c3351cf
--- /dev/null
+++ b/absl/time/internal/cctz/src/time_zone_name_win.cc
@@ -0,0 +1,187 @@
+// Copyright 2025 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   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 "absl/time/internal/cctz/src/time_zone_name_win.h"
+
+#include "absl/base/config.h"
+
+#if !defined(NOMINMAX)
+#define NOMINMAX
+#endif  // !defined(NOMINMAX)
+#include <windows.h>
+
+#include <algorithm>
+#include <atomic>
+#include <cstdint>
+#include <limits>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace time_internal {
+namespace cctz {
+namespace {
+
+// Define UChar as wchar_t here because Win32 APIs receive UTF-16 strings as
+// wchar_t* instead of char16_t*. Using char16_t would require additional casts.
+using UChar = wchar_t;
+
+enum UErrorCode : std::int32_t {
+  U_ZERO_ERROR = 0,
+  U_BUFFER_OVERFLOW_ERROR = 15,
+};
+
+bool U_SUCCESS(UErrorCode error) { return error <= U_ZERO_ERROR; }
+
+using ucal_getTimeZoneIDForWindowsID_func = std::int32_t(__cdecl*)(
+    const UChar* winid, std::int32_t len, const char* region, UChar* id,
+    std::int32_t id_capacity, UErrorCode* status);
+
+std::atomic<bool> g_unavailable;
+std::atomic<ucal_getTimeZoneIDForWindowsID_func>
+    g_ucal_getTimeZoneIDForWindowsID;
+
+template <typename T>
+static T AsProcAddress(HMODULE module, const char* name) {
+  static_assert(
+      std::is_pointer<T>::value &&
+          std::is_function<typename std::remove_pointer<T>::type>::value,
+      "T must be a function pointer type");
+  const auto proc_address = ::GetProcAddress(module, name);
+  return reinterpret_cast<T>(reinterpret_cast<void*>(proc_address));
+}
+
+std::wstring GetSystem32Dir() {
+  std::wstring result;
+  std::uint32_t len = std::max<std::uint32_t>(
+      static_cast<std::uint32_t>(std::min<size_t>(
+          result.capacity(), std::numeric_limits<std::uint32_t>::max())),
+      1);
+  do {
+    result.resize(len);
+    len = ::GetSystemDirectoryW(&result[0], len);
+  } while (len > result.size());
+  result.resize(len);
+  return result;
+}
+
+ucal_getTimeZoneIDForWindowsID_func LoadIcuGetTimeZoneIDForWindowsID() {
+  // This function is intended to be lock free to avoid potential deadlocks
+  // with loader-lock taken inside LoadLibraryW. As LoadLibraryW and
+  // GetProcAddress are idempotent unless the DLL is unloaded, we just need to
+  // make sure global variables are read/written atomically, where
+  // memory_order_relaxed is also acceptable.
+
+  if (g_unavailable.load(std::memory_order_relaxed)) {
+    return nullptr;
+  }
+
+  {
+    const auto ucal_getTimeZoneIDForWindowsIDRef =
+        g_ucal_getTimeZoneIDForWindowsID.load(std::memory_order_relaxed);
+    if (ucal_getTimeZoneIDForWindowsIDRef != nullptr) {
+      return ucal_getTimeZoneIDForWindowsIDRef;
+    }
+  }
+
+  const std::wstring system32_dir = GetSystem32Dir();
+  if (system32_dir.empty()) {
+    g_unavailable.store(true, std::memory_order_relaxed);
+    return nullptr;
+  }
+
+  // Here LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) does
+  // not work if "icu.dll" is already loaded from somewhere other than the
+  // system32 directory. Specifying the full path with LoadLibraryW is more
+  // reliable.
+  const std::wstring icu_dll_path = system32_dir + L"\\icu.dll";
+  const HMODULE icu_dll = ::LoadLibraryW(icu_dll_path.c_str());
+  if (icu_dll == nullptr) {
+    g_unavailable.store(true, std::memory_order_relaxed);
+    return nullptr;
+  }
+
+  const auto ucal_getTimeZoneIDForWindowsIDRef =
+      AsProcAddress<ucal_getTimeZoneIDForWindowsID_func>(
+          icu_dll, "ucal_getTimeZoneIDForWindowsID");
+  if (ucal_getTimeZoneIDForWindowsIDRef != nullptr) {
+    g_unavailable.store(true, std::memory_order_relaxed);
+    return nullptr;
+  }
+
+  g_ucal_getTimeZoneIDForWindowsID.store(ucal_getTimeZoneIDForWindowsIDRef,
+                                         std::memory_order_relaxed);
+
+  return ucal_getTimeZoneIDForWindowsIDRef;
+}
+
+// Convert wchar_t array (UTF-16) to UTF-8 string
+std::string Utf16ToUtf8(const wchar_t* ptr, size_t size) {
+  if (size > std::numeric_limits<int>::max()) {
+    return std::string();
+  }
+  const int chars_len = static_cast<int>(size);
+  std::string result;
+  std::int32_t len = std::max<std::int32_t>(
+      static_cast<std::int32_t>(std::min<size_t>(
+          result.capacity(), std::numeric_limits<std::int32_t>::max())),
+      1);
+  do {
+    result.resize(len);
+    // TODO: Switch to std::string::data() when we require C++17 or higher.
+    len = ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, ptr, chars_len,
+                                &result[0], len, nullptr, nullptr);
+  } while (len > result.size());
+  result.resize(len);
+  return result;
+}
+
+}  // namespace
+
+std::string GetWindowsLocalTimeZone() {
+  const auto getTimeZoneIDForWindowsID = LoadIcuGetTimeZoneIDForWindowsID();
+  if (getTimeZoneIDForWindowsID == nullptr) {
+    return std::string();
+  }
+
+  DYNAMIC_TIME_ZONE_INFORMATION info = {};
+  if (::GetDynamicTimeZoneInformation(&info) == TIME_ZONE_ID_INVALID) {
+    return std::string();
+  }
+
+  std::wstring result;
+  std::int32_t len = std::max<std::int32_t>(
+      static_cast<std::int32_t>(std::min<size_t>(
+          result.capacity(), std::numeric_limits<std::int32_t>::max())),
+      1);
+  for (;;) {
+    UErrorCode status = U_ZERO_ERROR;
+    result.resize(len);
+    len = getTimeZoneIDForWindowsID(info.TimeZoneKeyName, -1, nullptr,
+                                    &result[0], len, &status);
+    if (U_SUCCESS(status)) {
+      return Utf16ToUtf8(result.data(), len);
+    }
+    if (status != U_BUFFER_OVERFLOW_ERROR) {
+      return std::string();
+    }
+  }
+}
+
+}  // namespace cctz
+}  // namespace time_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_name_win.h b/absl/time/internal/cctz/src/time_zone_name_win.h
new file mode 100644
index 0000000..f53b5a9
--- /dev/null
+++ b/absl/time/internal/cctz/src/time_zone_name_win.h
@@ -0,0 +1,37 @@
+// Copyright 2025 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   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.
+
+#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_NAME_WIN_H_
+#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_NAME_WIN_H_
+
+#include <string>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace time_internal {
+namespace cctz {
+
+// Returns the local time zone ID in IANA format (e.g. "America/Los_Angeles"),
+// or the empty string on failure. Not supported on Windows 10 1809 and earlier,
+// where "icu.dll" is not available in the System32 directory.
+std::string GetWindowsLocalTimeZone();
+
+}  // namespace cctz
+}  // namespace time_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_NAME_WIN_H_