Skip to content

Commit 9a89ea8

Browse files
Abseil Teamcopybara-github
Abseil Team
authored andcommitted
Import of CCTZ from GitHub.
PiperOrigin-RevId: 756908046 Change-Id: I4db2b90fd1f6097f582b90c6aa82cdc4704d8b66
1 parent 4bf37d8 commit 9a89ea8

File tree

1 file changed

+87
-110
lines changed

1 file changed

+87
-110
lines changed

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

Lines changed: 87 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,29 @@
3333
#endif
3434

3535
#if defined(_WIN32)
36-
#include <sdkddkver.h>
37-
// Include only when the SDK is for Windows 10 (and later), and the binary is
38-
// targeted for Windows XP and later.
39-
// Note: The Windows SDK added windows.globalization.h file for Windows 10, but
40-
// MinGW did not add it until NTDDI_WIN10_NI (SDK version 10.0.22621.0).
41-
#if ((defined(_WIN32_WINNT_WIN10) && !defined(__MINGW32__)) || \
42-
(defined(NTDDI_WIN10_NI) && NTDDI_VERSION >= NTDDI_WIN10_NI)) && \
43-
(_WIN32_WINNT >= _WIN32_WINNT_WINXP)
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>)
4441
#define USE_WIN32_LOCAL_TIME_ZONE
45-
#include <roapi.h>
46-
#include <tchar.h>
47-
#include <wchar.h>
48-
#include <windows.globalization.h>
4942
#include <windows.h>
50-
#include <winstring.h>
51-
#endif
52-
#endif
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
5359

5460
#include <cstdlib>
5561
#include <cstring>
@@ -65,80 +71,78 @@ namespace cctz {
6571

6672
namespace {
6773
#if defined(USE_WIN32_LOCAL_TIME_ZONE)
68-
// Calls the WinRT Calendar.GetTimeZone method to obtain the IANA ID of the
69-
// local time zone. Returns an empty vector in case of an error.
70-
std::string win32_local_time_zone(const HMODULE combase) {
71-
std::string result;
72-
const auto ro_activate_instance =
73-
reinterpret_cast<decltype(&RoActivateInstance)>(
74-
GetProcAddress(combase, "RoActivateInstance"));
75-
if (!ro_activate_instance) {
76-
return result;
77-
}
78-
const auto windows_create_string_reference =
79-
reinterpret_cast<decltype(&WindowsCreateStringReference)>(
80-
GetProcAddress(combase, "WindowsCreateStringReference"));
81-
if (!windows_create_string_reference) {
82-
return result;
83-
}
84-
const auto windows_delete_string =
85-
reinterpret_cast<decltype(&WindowsDeleteString)>(
86-
GetProcAddress(combase, "WindowsDeleteString"));
87-
if (!windows_delete_string) {
88-
return result;
89-
}
90-
const auto windows_get_string_raw_buffer =
91-
reinterpret_cast<decltype(&WindowsGetStringRawBuffer)>(
92-
GetProcAddress(combase, "WindowsGetStringRawBuffer"));
93-
if (!windows_get_string_raw_buffer) {
94-
return result;
74+
// True if we have already failed to load the API.
75+
static std::atomic_bool g_ucal_getTimeZoneIDForWindowsIDUnavailable;
76+
static std::atomic<decltype(ucal_getTimeZoneIDForWindowsID)*>
77+
g_ucal_getTimeZoneIDForWindowsIDRef;
78+
79+
std::string win32_local_time_zone() {
80+
// If we have already failed to load the API, then just give up.
81+
if (g_ucal_getTimeZoneIDForWindowsIDUnavailable.load()) {
82+
return "";
9583
}
9684

97-
// The string returned by WindowsCreateStringReference doesn't need to be
98-
// deleted.
99-
HSTRING calendar_class_id;
100-
HSTRING_HEADER calendar_class_id_header;
101-
HRESULT hr = windows_create_string_reference(
102-
RuntimeClass_Windows_Globalization_Calendar,
103-
sizeof(RuntimeClass_Windows_Globalization_Calendar) / sizeof(wchar_t) - 1,
104-
&calendar_class_id_header, &calendar_class_id);
105-
if (FAILED(hr)) {
106-
return result;
107-
}
85+
auto ucal_getTimeZoneIDForWindowsIDFunc =
86+
g_ucal_getTimeZoneIDForWindowsIDRef.load();
87+
if (ucal_getTimeZoneIDForWindowsIDFunc == nullptr) {
88+
// If we have already failed to load the API, then just give up.
89+
if (g_ucal_getTimeZoneIDForWindowsIDUnavailable.load()) {
90+
return "";
91+
}
10892

109-
IInspectable* calendar;
110-
hr = ro_activate_instance(calendar_class_id, &calendar);
111-
if (FAILED(hr)) {
112-
return result;
93+
const HMODULE icudll =
94+
::LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
95+
96+
if (icudll == nullptr) {
97+
g_ucal_getTimeZoneIDForWindowsIDUnavailable.store(true);
98+
return "";
99+
}
100+
101+
ucal_getTimeZoneIDForWindowsIDFunc =
102+
reinterpret_cast<decltype(ucal_getTimeZoneIDForWindowsID)*>(
103+
::GetProcAddress(icudll, "ucal_getTimeZoneIDForWindowsID"));
104+
105+
if (ucal_getTimeZoneIDForWindowsIDFunc == nullptr) {
106+
g_ucal_getTimeZoneIDForWindowsIDUnavailable.store(true);
107+
return "";
108+
}
109+
// store-race is not a problem here, because ::GetProcAddress() returns the
110+
// same address for the same function in the same DLL.
111+
g_ucal_getTimeZoneIDForWindowsIDRef.store(
112+
ucal_getTimeZoneIDForWindowsIDFunc);
113+
114+
// We intentionally do not call ::FreeLibrary() here to avoid frequent DLL
115+
// loadings and unloading. As "icu.dll" is a system library, keeping it on
116+
// memory is supposed to have no major drawback.
113117
}
114118

115-
ABI::Windows::Globalization::ITimeZoneOnCalendar* time_zone;
116-
hr = calendar->QueryInterface(IID_PPV_ARGS(&time_zone));
117-
if (FAILED(hr)) {
118-
calendar->Release();
119-
return result;
119+
DYNAMIC_TIME_ZONE_INFORMATION info = {};
120+
if (::GetDynamicTimeZoneInformation(&info) == TIME_ZONE_ID_INVALID) {
121+
return "";
120122
}
121123

122-
HSTRING tz_hstr;
123-
hr = time_zone->GetTimeZone(&tz_hstr);
124-
if (SUCCEEDED(hr)) {
125-
UINT32 wlen;
126-
const PCWSTR tz_wstr = windows_get_string_raw_buffer(tz_hstr, &wlen);
127-
if (tz_wstr) {
128-
const int size =
129-
WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
130-
nullptr, 0, nullptr, nullptr);
131-
result.resize(static_cast<size_t>(size));
132-
WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
133-
&result[0], size, nullptr, nullptr);
134-
}
135-
windows_delete_string(tz_hstr);
124+
UChar buffer[128];
125+
UErrorCode status = U_ZERO_ERROR;
126+
const auto num_chars_in_buffer = ucal_getTimeZoneIDForWindowsIDFunc(
127+
reinterpret_cast<const UChar*>(info.TimeZoneKeyName), -1, nullptr, buffer,
128+
ARRAYSIZE(buffer), &status);
129+
if (status != U_ZERO_ERROR || num_chars_in_buffer <= 0 ||
130+
num_chars_in_buffer > ARRAYSIZE(buffer)) {
131+
return "";
136132
}
137-
time_zone->Release();
138-
calendar->Release();
139-
return result;
133+
134+
const int num_bytes_in_utf8 = ::WideCharToMultiByte(
135+
CP_UTF8, 0, reinterpret_cast<const wchar_t*>(buffer),
136+
static_cast<int>(num_chars_in_buffer), nullptr, 0, nullptr, nullptr);
137+
std::string local_time_str;
138+
local_time_str.resize(static_cast<size_t>(num_bytes_in_utf8));
139+
::WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<const wchar_t*>(buffer),
140+
static_cast<int>(num_chars_in_buffer),
141+
&local_time_str[0], num_bytes_in_utf8, nullptr,
142+
nullptr);
143+
return local_time_str;
140144
}
141-
#endif
145+
#endif // USE_WIN32_LOCAL_TIME_ZONE
142146
} // namespace
143147

144148
std::string time_zone::name() const { return effective_impl().Name(); }
@@ -256,36 +260,9 @@ time_zone local_time_zone() {
256260
}
257261
#endif
258262
#if defined(USE_WIN32_LOCAL_TIME_ZONE)
259-
// Use the WinRT Calendar class to get the local time zone. This feature is
260-
// available on Windows 10 and later. The library is dynamically linked to
261-
// maintain binary compatibility with Windows XP - Windows 7. On Windows 8,
262-
// The combase.dll API functions are available but the RoActivateInstance
263-
// call will fail for the Calendar class.
264-
std::string winrt_tz;
265-
const HMODULE combase =
266-
LoadLibraryEx(_T("combase.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
267-
if (combase) {
268-
const auto ro_initialize = reinterpret_cast<decltype(&::RoInitialize)>(
269-
GetProcAddress(combase, "RoInitialize"));
270-
const auto ro_uninitialize = reinterpret_cast<decltype(&::RoUninitialize)>(
271-
GetProcAddress(combase, "RoUninitialize"));
272-
if (ro_initialize && ro_uninitialize) {
273-
const HRESULT hr = ro_initialize(RO_INIT_MULTITHREADED);
274-
// RPC_E_CHANGED_MODE means that a previous RoInitialize call specified
275-
// a different concurrency model. The WinRT runtime is initialized and
276-
// should work for our purpose here, but we should *not* call
277-
// RoUninitialize because it's a failure.
278-
if (SUCCEEDED(hr) || hr == RPC_E_CHANGED_MODE) {
279-
winrt_tz = win32_local_time_zone(combase);
280-
if (SUCCEEDED(hr)) {
281-
ro_uninitialize();
282-
}
283-
}
284-
}
285-
FreeLibrary(combase);
286-
}
287-
if (!winrt_tz.empty()) {
288-
zone = winrt_tz.c_str();
263+
std::string win32_tz = win32_local_time_zone();
264+
if (!win32_tz.empty()) {
265+
zone = win32_tz.c_str();
289266
}
290267
#endif
291268

0 commit comments

Comments
 (0)