Skip to content

Commit 31ef391

Browse files
derekmaurocopybara-github
authored andcommitted
Import of CCTZ from GitHub.
PiperOrigin-RevId: 833899408 Change-Id: I5b6ee41676a8e0e207462e0b30dde3843478ef19
1 parent 48bf10f commit 31ef391

File tree

6 files changed

+245
-105
lines changed

6 files changed

+245
-105
lines changed

CMake/AbseilDll.cmake

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,12 @@ set(ABSL_INTERNAL_DLL_FILES
448448
"debugging/leak_check.cc"
449449
)
450450

451-
if(NOT MSVC)
451+
if(MSVC)
452+
list(APPEND ABSL_INTERNAL_DLL_FILES
453+
"time/internal/cctz/src/time_zone_name_win.cc"
454+
"time/internal/cctz/src/time_zone_name_win.h"
455+
)
456+
else()
452457
list(APPEND ABSL_INTERNAL_DLL_FILES
453458
"flags/commandlineflag.cc"
454459
"flags/commandlineflag.h"

absl/time/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ absl_cc_library(
7777
"internal/cctz/src/time_zone_posix.h"
7878
"internal/cctz/src/tzfile.h"
7979
"internal/cctz/src/zone_info_source.cc"
80+
$<$<PLATFORM_ID:Windows>:internal/cctz/src/time_zone_name_win.cc>
81+
$<$<PLATFORM_ID:Windows>:internal/cctz/src/time_zone_name_win.h>
8082
COPTS
8183
${ABSL_DEFAULT_COPTS}
8284
DEPS

absl/time/internal/cctz/BUILD.bazel

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,13 @@ cc_library(
5959
"src/time_zone_posix.h",
6060
"src/tzfile.h",
6161
"src/zone_info_source.cc",
62-
],
62+
] + select({
63+
"@platforms//os:windows": [
64+
"src/time_zone_name_win.cc",
65+
"src/time_zone_name_win.h",
66+
],
67+
"//conditions:default": [],
68+
}),
6369
hdrs = [
6470
"include/cctz/time_zone.h",
6571
"include/cctz/zone_info_source.h",

absl/time/internal/cctz/src/time_zone_lookup.cc

Lines changed: 6 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -32,31 +32,6 @@
3232
#include <zircon/types.h>
3333
#endif
3434

35-
#if defined(_WIN32)
36-
// Include only when <icu.h> is available.
37-
// https://learn.microsoft.com/en-us/windows/win32/intl/international-components-for-unicode--icu-
38-
// https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255
39-
#if defined(__has_include)
40-
#if __has_include(<icu.h>)
41-
#define USE_WIN32_LOCAL_TIME_ZONE
42-
#include <windows.h>
43-
#pragma push_macro("_WIN32_WINNT")
44-
#pragma push_macro("NTDDI_VERSION")
45-
// Minimum _WIN32_WINNT and NTDDI_VERSION to use ucal_getTimeZoneIDForWindowsID
46-
#undef _WIN32_WINNT
47-
#define _WIN32_WINNT 0x0A00 // == _WIN32_WINNT_WIN10
48-
#undef NTDDI_VERSION
49-
#define NTDDI_VERSION 0x0A000004 // == NTDDI_WIN10_RS3
50-
#include <icu.h>
51-
#pragma pop_macro("NTDDI_VERSION")
52-
#pragma pop_macro("_WIN32_WINNT")
53-
#include <timezoneapi.h>
54-
55-
#include <atomic>
56-
#endif // __has_include(<icu.h>)
57-
#endif // __has_include
58-
#endif // _WIN32
59-
6035
#include <array>
6136
#include <cstdint>
6237
#include <cstdlib>
@@ -66,87 +41,15 @@
6641
#include "absl/time/internal/cctz/src/time_zone_fixed.h"
6742
#include "absl/time/internal/cctz/src/time_zone_impl.h"
6843

44+
#if defined(_WIN32)
45+
#include "absl/time/internal/cctz/src/time_zone_name_win.h"
46+
#endif // _WIN32
47+
6948
namespace absl {
7049
ABSL_NAMESPACE_BEGIN
7150
namespace time_internal {
7251
namespace cctz {
7352

74-
namespace {
75-
#if defined(USE_WIN32_LOCAL_TIME_ZONE)
76-
// True if we have already failed to load the API.
77-
static std::atomic_bool g_ucal_getTimeZoneIDForWindowsIDUnavailable;
78-
static std::atomic<decltype(ucal_getTimeZoneIDForWindowsID)*>
79-
g_ucal_getTimeZoneIDForWindowsIDRef;
80-
81-
std::string win32_local_time_zone() {
82-
// If we have already failed to load the API, then just give up.
83-
if (g_ucal_getTimeZoneIDForWindowsIDUnavailable.load()) {
84-
return "";
85-
}
86-
87-
auto ucal_getTimeZoneIDForWindowsIDFunc =
88-
g_ucal_getTimeZoneIDForWindowsIDRef.load();
89-
if (ucal_getTimeZoneIDForWindowsIDFunc == nullptr) {
90-
// If we have already failed to load the API, then just give up.
91-
if (g_ucal_getTimeZoneIDForWindowsIDUnavailable.load()) {
92-
return "";
93-
}
94-
95-
const HMODULE icudll =
96-
::LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
97-
98-
if (icudll == nullptr) {
99-
g_ucal_getTimeZoneIDForWindowsIDUnavailable.store(true);
100-
return "";
101-
}
102-
103-
ucal_getTimeZoneIDForWindowsIDFunc =
104-
reinterpret_cast<decltype(ucal_getTimeZoneIDForWindowsID)*>(
105-
::GetProcAddress(icudll, "ucal_getTimeZoneIDForWindowsID"));
106-
107-
if (ucal_getTimeZoneIDForWindowsIDFunc == nullptr) {
108-
g_ucal_getTimeZoneIDForWindowsIDUnavailable.store(true);
109-
return "";
110-
}
111-
// store-race is not a problem here, because ::GetProcAddress() returns the
112-
// same address for the same function in the same DLL.
113-
g_ucal_getTimeZoneIDForWindowsIDRef.store(
114-
ucal_getTimeZoneIDForWindowsIDFunc);
115-
116-
// We intentionally do not call ::FreeLibrary() here to avoid frequent DLL
117-
// loadings and unloading. As "icu.dll" is a system library, keeping it on
118-
// memory is supposed to have no major drawback.
119-
}
120-
121-
DYNAMIC_TIME_ZONE_INFORMATION info = {};
122-
if (::GetDynamicTimeZoneInformation(&info) == TIME_ZONE_ID_INVALID) {
123-
return "";
124-
}
125-
126-
std::array<UChar, 128> buffer;
127-
UErrorCode status = U_ZERO_ERROR;
128-
const auto num_chars_in_buffer = ucal_getTimeZoneIDForWindowsIDFunc(
129-
reinterpret_cast<const UChar*>(info.TimeZoneKeyName), -1, nullptr,
130-
buffer.data(), static_cast<int32_t>(buffer.size()), &status);
131-
if (status != U_ZERO_ERROR || num_chars_in_buffer <= 0 ||
132-
num_chars_in_buffer > static_cast<int32_t>(buffer.size())) {
133-
return "";
134-
}
135-
136-
const int num_bytes_in_utf8 = ::WideCharToMultiByte(
137-
CP_UTF8, 0, reinterpret_cast<const wchar_t*>(buffer.data()),
138-
static_cast<int>(num_chars_in_buffer), nullptr, 0, nullptr, nullptr);
139-
std::string local_time_str;
140-
local_time_str.resize(static_cast<size_t>(num_bytes_in_utf8));
141-
::WideCharToMultiByte(
142-
CP_UTF8, 0, reinterpret_cast<const wchar_t*>(buffer.data()),
143-
static_cast<int>(num_chars_in_buffer), &local_time_str[0],
144-
num_bytes_in_utf8, nullptr, nullptr);
145-
return local_time_str;
146-
}
147-
#endif // USE_WIN32_LOCAL_TIME_ZONE
148-
} // namespace
149-
15053
std::string time_zone::name() const { return effective_impl().Name(); }
15154

15255
time_zone::absolute_lookup time_zone::lookup(
@@ -261,8 +164,8 @@ time_zone local_time_zone() {
261164
zone = primary_tz.c_str();
262165
}
263166
#endif
264-
#if defined(USE_WIN32_LOCAL_TIME_ZONE)
265-
std::string win32_tz = win32_local_time_zone();
167+
#if defined(_WIN32)
168+
std::string win32_tz = GetWindowsLocalTimeZone();
266169
if (!win32_tz.empty()) {
267170
zone = win32_tz.c_str();
268171
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// Copyright 2025 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "absl/time/internal/cctz/src/time_zone_name_win.h"
16+
17+
#include "absl/base/config.h"
18+
19+
#if !defined(NOMINMAX)
20+
#define NOMINMAX
21+
#endif // !defined(NOMINMAX)
22+
#include <windows.h>
23+
24+
#include <algorithm>
25+
#include <atomic>
26+
#include <cstdint>
27+
#include <limits>
28+
#include <string>
29+
#include <type_traits>
30+
#include <utility>
31+
32+
namespace absl {
33+
ABSL_NAMESPACE_BEGIN
34+
namespace time_internal {
35+
namespace cctz {
36+
namespace {
37+
38+
// Define UChar as wchar_t here because Win32 APIs receive UTF-16 strings as
39+
// wchar_t* instead of char16_t*. Using char16_t would require additional casts.
40+
using UChar = wchar_t;
41+
42+
enum UErrorCode : std::int32_t {
43+
U_ZERO_ERROR = 0,
44+
U_BUFFER_OVERFLOW_ERROR = 15,
45+
};
46+
47+
bool U_SUCCESS(UErrorCode error) { return error <= U_ZERO_ERROR; }
48+
49+
using ucal_getTimeZoneIDForWindowsID_func = std::int32_t(__cdecl*)(
50+
const UChar* winid, std::int32_t len, const char* region, UChar* id,
51+
std::int32_t id_capacity, UErrorCode* status);
52+
53+
std::atomic<bool> g_unavailable;
54+
std::atomic<ucal_getTimeZoneIDForWindowsID_func>
55+
g_ucal_getTimeZoneIDForWindowsID;
56+
57+
template <typename T>
58+
static T AsProcAddress(HMODULE module, const char* name) {
59+
static_assert(
60+
std::is_pointer<T>::value &&
61+
std::is_function<typename std::remove_pointer<T>::type>::value,
62+
"T must be a function pointer type");
63+
const auto proc_address = ::GetProcAddress(module, name);
64+
return reinterpret_cast<T>(reinterpret_cast<void*>(proc_address));
65+
}
66+
67+
std::wstring GetSystem32Dir() {
68+
std::wstring result;
69+
std::uint32_t len = std::max<std::uint32_t>(
70+
static_cast<std::uint32_t>(std::min<size_t>(
71+
result.capacity(), std::numeric_limits<std::uint32_t>::max())),
72+
1);
73+
do {
74+
result.resize(len);
75+
len = ::GetSystemDirectoryW(&result[0], len);
76+
} while (len > result.size());
77+
result.resize(len);
78+
return result;
79+
}
80+
81+
ucal_getTimeZoneIDForWindowsID_func LoadIcuGetTimeZoneIDForWindowsID() {
82+
// This function is intended to be lock free to avoid potential deadlocks
83+
// with loader-lock taken inside LoadLibraryW. As LoadLibraryW and
84+
// GetProcAddress are idempotent unless the DLL is unloaded, we just need to
85+
// make sure global variables are read/written atomically, where
86+
// memory_order_relaxed is also acceptable.
87+
88+
if (g_unavailable.load(std::memory_order_relaxed)) {
89+
return nullptr;
90+
}
91+
92+
{
93+
const auto ucal_getTimeZoneIDForWindowsIDRef =
94+
g_ucal_getTimeZoneIDForWindowsID.load(std::memory_order_relaxed);
95+
if (ucal_getTimeZoneIDForWindowsIDRef != nullptr) {
96+
return ucal_getTimeZoneIDForWindowsIDRef;
97+
}
98+
}
99+
100+
const std::wstring system32_dir = GetSystem32Dir();
101+
if (system32_dir.empty()) {
102+
g_unavailable.store(true, std::memory_order_relaxed);
103+
return nullptr;
104+
}
105+
106+
// Here LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) does
107+
// not work if "icu.dll" is already loaded from somewhere other than the
108+
// system32 directory. Specifying the full path with LoadLibraryW is more
109+
// reliable.
110+
const std::wstring icu_dll_path = system32_dir + L"\\icu.dll";
111+
const HMODULE icu_dll = ::LoadLibraryW(icu_dll_path.c_str());
112+
if (icu_dll == nullptr) {
113+
g_unavailable.store(true, std::memory_order_relaxed);
114+
return nullptr;
115+
}
116+
117+
const auto ucal_getTimeZoneIDForWindowsIDRef =
118+
AsProcAddress<ucal_getTimeZoneIDForWindowsID_func>(
119+
icu_dll, "ucal_getTimeZoneIDForWindowsID");
120+
if (ucal_getTimeZoneIDForWindowsIDRef != nullptr) {
121+
g_unavailable.store(true, std::memory_order_relaxed);
122+
return nullptr;
123+
}
124+
125+
g_ucal_getTimeZoneIDForWindowsID.store(ucal_getTimeZoneIDForWindowsIDRef,
126+
std::memory_order_relaxed);
127+
128+
return ucal_getTimeZoneIDForWindowsIDRef;
129+
}
130+
131+
// Convert wchar_t array (UTF-16) to UTF-8 string
132+
std::string Utf16ToUtf8(const wchar_t* ptr, size_t size) {
133+
if (size > std::numeric_limits<int>::max()) {
134+
return std::string();
135+
}
136+
const int chars_len = static_cast<int>(size);
137+
std::string result;
138+
std::int32_t len = std::max<std::int32_t>(
139+
static_cast<std::int32_t>(std::min<size_t>(
140+
result.capacity(), std::numeric_limits<std::int32_t>::max())),
141+
1);
142+
do {
143+
result.resize(len);
144+
// TODO: Switch to std::string::data() when we require C++17 or higher.
145+
len = ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, ptr, chars_len,
146+
&result[0], len, nullptr, nullptr);
147+
} while (len > result.size());
148+
result.resize(len);
149+
return result;
150+
}
151+
152+
} // namespace
153+
154+
std::string GetWindowsLocalTimeZone() {
155+
const auto getTimeZoneIDForWindowsID = LoadIcuGetTimeZoneIDForWindowsID();
156+
if (getTimeZoneIDForWindowsID == nullptr) {
157+
return std::string();
158+
}
159+
160+
DYNAMIC_TIME_ZONE_INFORMATION info = {};
161+
if (::GetDynamicTimeZoneInformation(&info) == TIME_ZONE_ID_INVALID) {
162+
return std::string();
163+
}
164+
165+
std::wstring result;
166+
std::int32_t len = std::max<std::int32_t>(
167+
static_cast<std::int32_t>(std::min<size_t>(
168+
result.capacity(), std::numeric_limits<std::int32_t>::max())),
169+
1);
170+
for (;;) {
171+
UErrorCode status = U_ZERO_ERROR;
172+
result.resize(len);
173+
len = getTimeZoneIDForWindowsID(info.TimeZoneKeyName, -1, nullptr,
174+
&result[0], len, &status);
175+
if (U_SUCCESS(status)) {
176+
return Utf16ToUtf8(result.data(), len);
177+
}
178+
if (status != U_BUFFER_OVERFLOW_ERROR) {
179+
return std::string();
180+
}
181+
}
182+
}
183+
184+
} // namespace cctz
185+
} // namespace time_internal
186+
ABSL_NAMESPACE_END
187+
} // namespace absl
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2025 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_NAME_WIN_H_
16+
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_NAME_WIN_H_
17+
18+
#include <string>
19+
20+
#include "absl/base/config.h"
21+
22+
namespace absl {
23+
ABSL_NAMESPACE_BEGIN
24+
namespace time_internal {
25+
namespace cctz {
26+
27+
// Returns the local time zone ID in IANA format (e.g. "America/Los_Angeles"),
28+
// or the empty string on failure. Not supported on Windows 10 1809 and earlier,
29+
// where "icu.dll" is not available in the System32 directory.
30+
std::string GetWindowsLocalTimeZone();
31+
32+
} // namespace cctz
33+
} // namespace time_internal
34+
ABSL_NAMESPACE_END
35+
} // namespace absl
36+
37+
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_NAME_WIN_H_

0 commit comments

Comments
 (0)