Fix the timezone detection on Windows with RDP.
This is to cherry-pick fixes from the upstream to fix
the timezone detection issue when a remote Windows
session is used. The detected timezone should be that of
a remote machine Chrome is running on, but without this
fix, it uses the timezone of machine where a RDP connection
originates.
Bugs:
https://unicode-org.atlassian.net/browse/ICU-13842
https://unicode-org.atlassian.net/browse/ICU-13827
Fixes (included in the upstream ICU 63 release candidate)
https://github.com/unicode-org/icu/pull/55
https://github.com/unicode-org/icu/pull/129
TBR=yangguo@chromium.org
Bug: 854387
Test: Manual. See the bug
Change-Id: Ic21e82987c1569e107d9b5e17bdc0d21e65fda3c
Reviewed-on: https://chromium-review.googlesource.com/c/1270635
Reviewed-by: Jungshik Shin <jshin@chromium.org>
diff --git a/README.chromium b/README.chromium
index fdc4ed2..de9906f 100644
--- a/README.chromium
+++ b/README.chromium
@@ -290,3 +290,15 @@
https://unicode-org.atlassian.net/browse/ICU-20140
- fix: https://github.com/unicode-org/icu/pull/136
+15. Cherry pick an upstream patch for Windows timezone detection
+
+ - patches/wintz_detection.patch
+ - upstream bugs:
+ https://unicode-org.atlassian.net/browse/ICU-13842
+ https://unicode-org.atlassian.net/browse/ICU-13827
+ - fixes:
+ https://github.com/unicode-org/icu/pull/55
+ https://github.com/unicode-org/icu/pull/129
+
+
+
diff --git a/patches/wintz_detection.patch b/patches/wintz_detection.patch
new file mode 100644
index 0000000..91c6a0c
--- /dev/null
+++ b/patches/wintz_detection.patch
@@ -0,0 +1,537 @@
+diff --git a/source/common/putil.cpp b/source/common/putil.cpp
+index 2d2de524..229656d6 100644
+--- a/source/common/putil.cpp
++++ b/source/common/putil.cpp
+@@ -102,9 +102,8 @@
+ # define NOMCX
+ # include <windows.h>
+ # include "unicode/uloc.h"
+-#if U_PLATFORM_HAS_WINUWP_API == 0
+ # include "wintz.h"
+-#else // U_PLATFORM_HAS_WINUWP_API
++#if U_PLATFORM_HAS_WINUWP_API
+ typedef PVOID LPMSG; // TODO: figure out how to get rid of this typedef
+ #include <Windows.Globalization.h>
+ #include <windows.system.userprofile.h>
+@@ -1062,53 +1061,13 @@ uprv_tzname_clear_cache()
+ #endif
+ }
+
+-// With the Universal Windows Platform we can just ask Windows for the name
+-#if U_PLATFORM_HAS_WINUWP_API
+-U_CAPI const char* U_EXPORT2
+-uprv_getWindowsTimeZone()
+-{
+- // Get default Windows timezone.
+- ComPtr<IInspectable> calendar;
+- HRESULT hr = RoActivateInstance(
+- HStringReference(RuntimeClass_Windows_Globalization_Calendar).Get(),
+- &calendar);
+- if (SUCCEEDED(hr))
+- {
+- ComPtr<ABI::Windows::Globalization::ITimeZoneOnCalendar> timezone;
+- hr = calendar.As(&timezone);
+- if (SUCCEEDED(hr))
+- {
+- HString timezoneString;
+- hr = timezone->GetTimeZone(timezoneString.GetAddressOf());
+- if (SUCCEEDED(hr))
+- {
+- int32_t length = static_cast<int32_t>(wcslen(timezoneString.GetRawBuffer(NULL)));
+- char* asciiId = (char*)uprv_calloc(length + 1, sizeof(char));
+- if (asciiId != nullptr)
+- {
+- u_UCharsToChars((UChar*)timezoneString.GetRawBuffer(NULL), asciiId, length);
+- return asciiId;
+- }
+- }
+- }
+- }
+-
+- // Failed
+- return nullptr;
+-}
+-#endif
+-
+ U_CAPI const char* U_EXPORT2
+ uprv_tzname(int n)
+ {
+ (void)n; // Avoid unreferenced parameter warning.
+ const char *tzid = NULL;
+ #if U_PLATFORM_USES_ONLY_WIN32_API
+-#if U_PLATFORM_HAS_WINUWP_API > 0
+- tzid = uprv_getWindowsTimeZone();
+-#else
+ tzid = uprv_detectWindowsTimeZone();
+-#endif
+
+ if (tzid != NULL) {
+ return tzid;
+diff --git a/source/common/wintz.cpp b/source/common/wintz.cpp
+index 3aaa36a0..9cf79d7b 100644
+--- a/source/common/wintz.cpp
++++ b/source/common/wintz.cpp
+@@ -13,9 +13,7 @@
+
+ #include "unicode/utypes.h"
+
+-// This file contains only desktop Windows behavior
+-// Windows UWP calls Windows::Globalization directly, so this isn't needed there.
+-#if U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0)
++#if U_PLATFORM_HAS_WIN32_API
+
+ #include "wintz.h"
+ #include "cmemory.h"
+@@ -23,6 +21,7 @@
+
+ #include "unicode/ures.h"
+ #include "unicode/ustring.h"
++#include "uresimp.h"
+
+ #ifndef WIN32_LEAN_AND_MEAN
+ # define WIN32_LEAN_AND_MEAN
+@@ -34,363 +33,94 @@
+ # define NOMCX
+ #include <windows.h>
+
+-#define MAX_LENGTH_ID 40
++U_NAMESPACE_BEGIN
+
+-/* The layout of the Tzi value in the registry */
+-typedef struct
+-{
+- int32_t bias;
+- int32_t standardBias;
+- int32_t daylightBias;
+- SYSTEMTIME standardDate;
+- SYSTEMTIME daylightDate;
+-} TZI;
+-
+-/**
+- * Various registry keys and key fragments.
+- */
+-static const wchar_t CURRENT_ZONE_REGKEY[] = L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\";
+-static const char STANDARD_TIME_REGKEY[] = " Standard Time";
+-static const char TZI_REGKEY[] = "TZI";
+-static const char STD_REGKEY[] = "Std";
++// The value of MAX_TIMEZONE_ID_LENGTH is 128, which is defined in DYNAMIC_TIME_ZONE_INFORMATION
++#define MAX_TIMEZONE_ID_LENGTH 128
+
+ /**
+- * The time zone root keys (under HKLM) for Win7+
+- */
+-static const char TZ_REGKEY[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\";
+-
+-static LONG openTZRegKey(HKEY *hkey, const char *winid)
+-{
+- char subKeyName[110]; /* TODO: why 110?? */
+- char *name;
+- LONG result;
+-
+- uprv_strcpy(subKeyName, TZ_REGKEY);
+- name = &subKeyName[strlen(subKeyName)];
+- uprv_strcat(subKeyName, winid);
+-
+- result = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+- subKeyName,
+- 0,
+- KEY_QUERY_VALUE,
+- hkey);
+- return result;
+-}
+-
+-static LONG getTZI(const char *winid, TZI *tzi)
+-{
+- DWORD cbData = sizeof(TZI);
+- LONG result;
+- HKEY hkey;
+-
+- result = openTZRegKey(&hkey, winid);
+-
+- if (result == ERROR_SUCCESS)
+- {
+- result = RegQueryValueExA(hkey,
+- TZI_REGKEY,
+- NULL,
+- NULL,
+- (LPBYTE)tzi,
+- &cbData);
+- RegCloseKey(hkey);
+- }
+-
+- return result;
+-}
+-
+-static LONG getSTDName(const char *winid, char *regStdName, int32_t length)
+-{
+- DWORD cbData = length;
+- LONG result;
+- HKEY hkey;
+-
+- result = openTZRegKey(&hkey, winid);
+-
+- if (result == ERROR_SUCCESS)
+- {
+- result = RegQueryValueExA(hkey,
+- STD_REGKEY,
+- NULL,
+- NULL,
+- (LPBYTE)regStdName,
+- &cbData);
+- RegCloseKey(hkey);
+- }
+-
+- return result;
+-}
+-
+-static LONG getTZKeyName(char* tzKeyName, int32_t tzKeyNamelength)
+-{
+- HKEY hkey;
+- LONG result = FALSE;
+- WCHAR timeZoneKeyNameData[128];
+- DWORD timeZoneKeyNameLength = static_cast<DWORD>(sizeof(timeZoneKeyNameData));
+-
+- if(ERROR_SUCCESS == RegOpenKeyExW(
+- HKEY_LOCAL_MACHINE,
+- CURRENT_ZONE_REGKEY,
+- 0,
+- KEY_QUERY_VALUE,
+- &hkey))
+- {
+- if (ERROR_SUCCESS == RegQueryValueExW(
+- hkey,
+- L"TimeZoneKeyName",
+- NULL,
+- NULL,
+- (LPBYTE)timeZoneKeyNameData,
+- &timeZoneKeyNameLength))
+- {
+- // Ensure null termination.
+- timeZoneKeyNameData[UPRV_LENGTHOF(timeZoneKeyNameData) - 1] = L'\0';
+-
+- // Convert the UTF-16 string to UTF-8.
+- UErrorCode status = U_ZERO_ERROR;
+- u_strToUTF8(tzKeyName, tzKeyNamelength, NULL, reinterpret_cast<const UChar *>(timeZoneKeyNameData), -1, &status);
+- if (U_ZERO_ERROR == status)
+- {
+- result = ERROR_SUCCESS;
+- }
+- }
+- RegCloseKey(hkey);
+- }
+-
+- return result;
+-}
+-
+-/*
+- This code attempts to detect the Windows time zone directly,
+- as set in the Windows Date and Time control panel. It attempts
+- to work on versions greater than Windows Vista and on localized
+- installs. It works by directly interrogating the registry and
+- comparing the data there with the data returned by the
+- GetTimeZoneInformation API, along with some other strategies. The
+- registry contains time zone data under this key:
+-
+- HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\
+-
+- Under this key are several subkeys, one for each time zone. For
+- example these subkeys are named "Pacific Standard Time" on Vista+.
+- There are some other wrinkles; see the code for
+- details. The subkey name is NOT LOCALIZED, allowing us to support
+- localized installs.
+-
+- Under the subkey are data values. We care about:
+-
+- Std Standard time display name, localized
+- TZI Binary block of data
+-
+- The TZI data is of particular interest. It contains the offset, two
+- more offsets for standard and daylight time, and the start and end
+- rules. This is the same data returned by the GetTimeZoneInformation
+- API. The API may modify the data on the way out, so we have to be
+- careful, but essentially we do a binary comparison against the TZI
+- blocks of various registry keys. When we find a match, we know what
+- time zone Windows is set to. Since the registry key is not
+- localized, we can then translate the key through a simple table
+- lookup into the corresponding ICU time zone.
+-
+- This strategy doesn't always work because there are zones which
+- share an offset and rules, so more than one TZI block will match.
+- For example, both Tokyo and Seoul are at GMT+9 with no DST rules;
+- their TZI blocks are identical. For these cases, we fall back to a
+- name lookup. We attempt to match the display name as stored in the
+- registry for the current zone to the display name stored in the
+- registry for various Windows zones. By comparing the registry data
+- directly we avoid conversion complications.
+-
+- Author: Alan Liu
+- Since: ICU 2.6
+- Based on original code by Carl Brown <cbrown@xnetinc.com>
++* Main Windows time zone detection function.
++* Returns the Windows time zone converted to an ICU time zone as a heap-allocated buffer, or nullptr upon failure.
++* Note: We use the Win32 API GetDynamicTimeZoneInformation to get the current time zone info.
++* This API returns a non-localized time zone name, which we can then map to an ICU time zone name.
+ */
+-
+-/**
+- * Main Windows time zone detection function. Returns the Windows
+- * time zone, translated to an ICU time zone, or NULL upon failure.
+- */
+ U_CFUNC const char* U_EXPORT2
+-uprv_detectWindowsTimeZone()
++uprv_detectWindowsTimeZone()
+ {
+ UErrorCode status = U_ZERO_ERROR;
+- UResourceBundle* bundle = NULL;
+- char* icuid = NULL;
+- char apiStdName[MAX_LENGTH_ID];
+- char regStdName[MAX_LENGTH_ID];
+- char tmpid[MAX_LENGTH_ID];
++ char* icuid = nullptr;
++ char dynamicTZKeyName[MAX_TIMEZONE_ID_LENGTH];
++ char tmpid[MAX_TIMEZONE_ID_LENGTH];
+ int32_t len;
+- int id;
++ int id = GEOID_NOT_AVAILABLE;
+ int errorCode;
+- wchar_t ISOcodeW[3]; /* 2 letter iso code in UTF-16*/
+- char ISOcodeA[3]; /* 2 letter iso code in ansi */
++ wchar_t ISOcodeW[3] = {}; /* 2 letter ISO code in UTF-16 */
++ char ISOcode[3] = {}; /* 2 letter ISO code in UTF-8 */
+
+- LONG result;
+- TZI tziKey;
+- TZI tziReg;
+- TIME_ZONE_INFORMATION apiTZI;
++ DYNAMIC_TIME_ZONE_INFORMATION dynamicTZI;
++ uprv_memset(&dynamicTZI, 0, sizeof(dynamicTZI));
++ uprv_memset(dynamicTZKeyName, 0, sizeof(dynamicTZKeyName));
++ uprv_memset(tmpid, 0, sizeof(tmpid));
+
+- BOOL tryPreVistaFallback;
+- OSVERSIONINFO osVerInfo;
++ /* Obtain TIME_ZONE_INFORMATION from the API and get the non-localized time zone name. */
++ if (TIME_ZONE_ID_INVALID == GetDynamicTimeZoneInformation(&dynamicTZI)) {
++ return nullptr;
++ }
+
+- /* Obtain TIME_ZONE_INFORMATION from the API, and then convert it
+- to TZI. We could also interrogate the registry directly; we do
+- this below if needed. */
+- uprv_memset(&apiTZI, 0, sizeof(apiTZI));
+- uprv_memset(&tziKey, 0, sizeof(tziKey));
+- uprv_memset(&tziReg, 0, sizeof(tziReg));
+- GetTimeZoneInformation(&apiTZI);
+- tziKey.bias = apiTZI.Bias;
+- uprv_memcpy((char *)&tziKey.standardDate, (char*)&apiTZI.StandardDate,
+- sizeof(apiTZI.StandardDate));
+- uprv_memcpy((char *)&tziKey.daylightDate, (char*)&apiTZI.DaylightDate,
+- sizeof(apiTZI.DaylightDate));
++ id = GetUserGeoID(GEOCLASS_NATION);
++ errorCode = GetGeoInfoW(id, GEO_ISO2, ISOcodeW, 3, 0);
+
+- /* Convert the wchar_t* standard name to char* */
+- uprv_memset(apiStdName, 0, sizeof(apiStdName));
+- wcstombs(apiStdName, apiTZI.StandardName, MAX_LENGTH_ID);
++ // convert from wchar_t* (UTF-16 on Windows) to char* (UTF-8).
++ u_strToUTF8(ISOcode, UPRV_LENGTHOF(ISOcode), nullptr,
++ reinterpret_cast<const UChar*>(ISOcodeW), UPRV_LENGTHOF(ISOcodeW), &status);
+
+- tmpid[0] = 0;
++ LocalUResourceBundlePointer bundle(ures_openDirect(nullptr, "windowsZones", &status));
++ ures_getByKey(bundle.getAlias(), "mapTimezones", bundle.getAlias(), &status);
+
+- id = GetUserGeoID(GEOCLASS_NATION);
+- errorCode = GetGeoInfoW(id, GEO_ISO2, ISOcodeW, 3, 0);
+- u_strToUTF8(ISOcodeA, 3, NULL, (const UChar *)ISOcodeW, 3, &status);
++ // convert from wchar_t* (UTF-16 on Windows) to char* (UTF-8).
++ u_strToUTF8(dynamicTZKeyName, UPRV_LENGTHOF(dynamicTZKeyName), nullptr,
++ reinterpret_cast<const UChar*>(dynamicTZI.TimeZoneKeyName), UPRV_LENGTHOF(dynamicTZI.TimeZoneKeyName), &status);
+
+- bundle = ures_openDirect(NULL, "windowsZones", &status);
+- ures_getByKey(bundle, "mapTimezones", bundle, &status);
++ if (U_FAILURE(status)) {
++ return nullptr;
++ }
+
+- /*
+- Windows Vista+ provides us with a "TimeZoneKeyName" that is not localized
+- and can be used to directly map a name in our bundle. Try to use that first
+- if we're on Vista or higher
+- */
+- uprv_memset(&osVerInfo, 0, sizeof(osVerInfo));
+- osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
+- tryPreVistaFallback = TRUE;
+- result = getTZKeyName(regStdName, sizeof(regStdName));
+- if(ERROR_SUCCESS == result)
+- {
+- UResourceBundle* winTZ = ures_getByKey(bundle, regStdName, NULL, &status);
+- if(U_SUCCESS(status))
+- {
+- const UChar* icuTZ = NULL;
+- if (errorCode != 0)
+- {
+- icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
++ if (dynamicTZI.TimeZoneKeyName[0] != 0) {
++ UResourceBundle winTZ;
++ ures_initStackObject(&winTZ);
++ ures_getByKey(bundle.getAlias(), dynamicTZKeyName, &winTZ, &status);
++
++ if (U_SUCCESS(status)) {
++ const UChar* icuTZ = nullptr;
++ if (errorCode != 0) {
++ icuTZ = ures_getStringByKey(&winTZ, ISOcode, &len, &status);
+ }
+- if (errorCode==0 || icuTZ==NULL)
+- {
++ if (errorCode == 0 || icuTZ == nullptr) {
+ /* fallback to default "001" and reset status */
+ status = U_ZERO_ERROR;
+- icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
+- }
+-
+- if(U_SUCCESS(status))
+- {
+- int index=0;
+- while (! (*icuTZ == '\0' || *icuTZ ==' '))
+- {
+- tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */
+- }
+- tmpid[index]='\0';
+- tryPreVistaFallback = FALSE;
+- }
+- }
+- ures_close(winTZ);
+- }
+-
+- if(tryPreVistaFallback)
+- {
+- /* Note: We get the winid not from static tables but from resource bundle. */
+- while (U_SUCCESS(status) && ures_hasNext(bundle))
+- {
+- UBool idFound = FALSE;
+- const char* winid;
+- UResourceBundle* winTZ = ures_getNextResource(bundle, NULL, &status);
+- if (U_FAILURE(status))
+- {
+- break;
++ icuTZ = ures_getStringByKey(&winTZ, "001", &len, &status);
+ }
+- winid = ures_getKey(winTZ);
+- result = getTZI(winid, &tziReg);
+-
+- if (result == ERROR_SUCCESS)
+- {
+- /* Windows alters the DaylightBias in some situations.
+- Using the bias and the rules suffices, so overwrite
+- these unreliable fields. */
+- tziKey.standardBias = tziReg.standardBias;
+- tziKey.daylightBias = tziReg.daylightBias;
+
+- if (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) == 0)
+- {
+- const UChar* icuTZ = NULL;
+- if (errorCode != 0)
+- {
+- icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
+- }
+- if (errorCode==0 || icuTZ==NULL)
+- {
+- /* fallback to default "001" and reset status */
+- status = U_ZERO_ERROR;
+- icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
+- }
++ if (U_SUCCESS(status)) {
++ int index = 0;
+
+- if (U_SUCCESS(status))
+- {
+- /* Get the standard name from the registry key to compare with
+- the one from Windows API call. */
+- uprv_memset(regStdName, 0, sizeof(regStdName));
+- result = getSTDName(winid, regStdName, sizeof(regStdName));
+- if (result == ERROR_SUCCESS)
+- {
+- if (uprv_strcmp(apiStdName, regStdName) == 0)
+- {
+- idFound = TRUE;
+- }
+- }
+-
+- /* tmpid buffer holds the ICU timezone ID corresponding to the timezone ID from Windows.
+- * If none is found, tmpid buffer will contain a fallback ID (i.e. the time zone ID matching
+- * the current time zone information)
+- */
+- if (idFound || tmpid[0] == 0)
+- {
+- /* if icuTZ has more than one city, take only the first (i.e. terminate icuTZ at first space) */
+- int index=0;
+- while (! (*icuTZ == '\0' || *icuTZ ==' '))
+- {
+- tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */
+- }
+- tmpid[index]='\0';
+- }
+- }
++ while (!(*icuTZ == '\0' || *icuTZ == ' ')) {
++ // time zone IDs only contain ASCII invariant characters.
++ tmpid[index++] = (char)(*icuTZ++);
+ }
+- }
+- ures_close(winTZ);
+- if (idFound)
+- {
+- break;
++ tmpid[index] = '\0';
+ }
+ }
++ ures_close(&winTZ);
+ }
+
+- /*
+- * Copy the timezone ID to icuid to be returned.
+- */
+- if (tmpid[0] != 0)
+- {
+- len = uprv_strlen(tmpid);
+- icuid = (char*)uprv_calloc(len + 1, sizeof(char));
+- if (icuid != NULL)
+- {
+- uprv_strcpy(icuid, tmpid);
+- }
++ // Copy the timezone ID to icuid to be returned.
++ if (tmpid[0] != 0) {
++ icuid = uprv_strdup(tmpid);
+ }
+
+- ures_close(bundle);
+-
+ return icuid;
+ }
+
+-#endif /* U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0) */
++U_NAMESPACE_END
++#endif /* U_PLATFORM_HAS_WIN32_API */
+diff --git a/source/common/wintz.h b/source/common/wintz.h
+index 7be30eb4..19b7cfe9 100644
+--- a/source/common/wintz.h
++++ b/source/common/wintz.h
+@@ -16,9 +16,7 @@
+
+ #include "unicode/utypes.h"
+
+-// This file contains only desktop windows behavior
+-// Windows UWP calls Windows::Globalization directly, so this isn't needed there.
+-#if U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0)
++#if U_PLATFORM_HAS_WIN32_API
+
+ /**
+ * \file
+@@ -33,6 +31,6 @@ U_CDECL_END
+ U_CFUNC const char* U_EXPORT2
+ uprv_detectWindowsTimeZone();
+
+-#endif /* U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0) */
++#endif /* U_PLATFORM_HAS_WIN32_API */
+
+ #endif /* __WINTZ */
diff --git a/source/common/putil.cpp b/source/common/putil.cpp
index 2d2de52..229656d 100644
--- a/source/common/putil.cpp
+++ b/source/common/putil.cpp
@@ -102,9 +102,8 @@
# define NOMCX
# include <windows.h>
# include "unicode/uloc.h"
-#if U_PLATFORM_HAS_WINUWP_API == 0
# include "wintz.h"
-#else // U_PLATFORM_HAS_WINUWP_API
+#if U_PLATFORM_HAS_WINUWP_API
typedef PVOID LPMSG; // TODO: figure out how to get rid of this typedef
#include <Windows.Globalization.h>
#include <windows.system.userprofile.h>
@@ -1062,53 +1061,13 @@
#endif
}
-// With the Universal Windows Platform we can just ask Windows for the name
-#if U_PLATFORM_HAS_WINUWP_API
-U_CAPI const char* U_EXPORT2
-uprv_getWindowsTimeZone()
-{
- // Get default Windows timezone.
- ComPtr<IInspectable> calendar;
- HRESULT hr = RoActivateInstance(
- HStringReference(RuntimeClass_Windows_Globalization_Calendar).Get(),
- &calendar);
- if (SUCCEEDED(hr))
- {
- ComPtr<ABI::Windows::Globalization::ITimeZoneOnCalendar> timezone;
- hr = calendar.As(&timezone);
- if (SUCCEEDED(hr))
- {
- HString timezoneString;
- hr = timezone->GetTimeZone(timezoneString.GetAddressOf());
- if (SUCCEEDED(hr))
- {
- int32_t length = static_cast<int32_t>(wcslen(timezoneString.GetRawBuffer(NULL)));
- char* asciiId = (char*)uprv_calloc(length + 1, sizeof(char));
- if (asciiId != nullptr)
- {
- u_UCharsToChars((UChar*)timezoneString.GetRawBuffer(NULL), asciiId, length);
- return asciiId;
- }
- }
- }
- }
-
- // Failed
- return nullptr;
-}
-#endif
-
U_CAPI const char* U_EXPORT2
uprv_tzname(int n)
{
(void)n; // Avoid unreferenced parameter warning.
const char *tzid = NULL;
#if U_PLATFORM_USES_ONLY_WIN32_API
-#if U_PLATFORM_HAS_WINUWP_API > 0
- tzid = uprv_getWindowsTimeZone();
-#else
tzid = uprv_detectWindowsTimeZone();
-#endif
if (tzid != NULL) {
return tzid;
diff --git a/source/common/wintz.cpp b/source/common/wintz.cpp
index 3aaa36a..9cf79d7 100644
--- a/source/common/wintz.cpp
+++ b/source/common/wintz.cpp
@@ -13,9 +13,7 @@
#include "unicode/utypes.h"
-// This file contains only desktop Windows behavior
-// Windows UWP calls Windows::Globalization directly, so this isn't needed there.
-#if U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0)
+#if U_PLATFORM_HAS_WIN32_API
#include "wintz.h"
#include "cmemory.h"
@@ -23,6 +21,7 @@
#include "unicode/ures.h"
#include "unicode/ustring.h"
+#include "uresimp.h"
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
@@ -34,363 +33,94 @@
# define NOMCX
#include <windows.h>
-#define MAX_LENGTH_ID 40
+U_NAMESPACE_BEGIN
-/* The layout of the Tzi value in the registry */
-typedef struct
-{
- int32_t bias;
- int32_t standardBias;
- int32_t daylightBias;
- SYSTEMTIME standardDate;
- SYSTEMTIME daylightDate;
-} TZI;
+// The value of MAX_TIMEZONE_ID_LENGTH is 128, which is defined in DYNAMIC_TIME_ZONE_INFORMATION
+#define MAX_TIMEZONE_ID_LENGTH 128
/**
- * Various registry keys and key fragments.
- */
-static const wchar_t CURRENT_ZONE_REGKEY[] = L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\";
-static const char STANDARD_TIME_REGKEY[] = " Standard Time";
-static const char TZI_REGKEY[] = "TZI";
-static const char STD_REGKEY[] = "Std";
-
-/**
- * The time zone root keys (under HKLM) for Win7+
- */
-static const char TZ_REGKEY[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\";
-
-static LONG openTZRegKey(HKEY *hkey, const char *winid)
-{
- char subKeyName[110]; /* TODO: why 110?? */
- char *name;
- LONG result;
-
- uprv_strcpy(subKeyName, TZ_REGKEY);
- name = &subKeyName[strlen(subKeyName)];
- uprv_strcat(subKeyName, winid);
-
- result = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
- subKeyName,
- 0,
- KEY_QUERY_VALUE,
- hkey);
- return result;
-}
-
-static LONG getTZI(const char *winid, TZI *tzi)
-{
- DWORD cbData = sizeof(TZI);
- LONG result;
- HKEY hkey;
-
- result = openTZRegKey(&hkey, winid);
-
- if (result == ERROR_SUCCESS)
- {
- result = RegQueryValueExA(hkey,
- TZI_REGKEY,
- NULL,
- NULL,
- (LPBYTE)tzi,
- &cbData);
- RegCloseKey(hkey);
- }
-
- return result;
-}
-
-static LONG getSTDName(const char *winid, char *regStdName, int32_t length)
-{
- DWORD cbData = length;
- LONG result;
- HKEY hkey;
-
- result = openTZRegKey(&hkey, winid);
-
- if (result == ERROR_SUCCESS)
- {
- result = RegQueryValueExA(hkey,
- STD_REGKEY,
- NULL,
- NULL,
- (LPBYTE)regStdName,
- &cbData);
- RegCloseKey(hkey);
- }
-
- return result;
-}
-
-static LONG getTZKeyName(char* tzKeyName, int32_t tzKeyNamelength)
-{
- HKEY hkey;
- LONG result = FALSE;
- WCHAR timeZoneKeyNameData[128];
- DWORD timeZoneKeyNameLength = static_cast<DWORD>(sizeof(timeZoneKeyNameData));
-
- if(ERROR_SUCCESS == RegOpenKeyExW(
- HKEY_LOCAL_MACHINE,
- CURRENT_ZONE_REGKEY,
- 0,
- KEY_QUERY_VALUE,
- &hkey))
- {
- if (ERROR_SUCCESS == RegQueryValueExW(
- hkey,
- L"TimeZoneKeyName",
- NULL,
- NULL,
- (LPBYTE)timeZoneKeyNameData,
- &timeZoneKeyNameLength))
- {
- // Ensure null termination.
- timeZoneKeyNameData[UPRV_LENGTHOF(timeZoneKeyNameData) - 1] = L'\0';
-
- // Convert the UTF-16 string to UTF-8.
- UErrorCode status = U_ZERO_ERROR;
- u_strToUTF8(tzKeyName, tzKeyNamelength, NULL, reinterpret_cast<const UChar *>(timeZoneKeyNameData), -1, &status);
- if (U_ZERO_ERROR == status)
- {
- result = ERROR_SUCCESS;
- }
- }
- RegCloseKey(hkey);
- }
-
- return result;
-}
-
-/*
- This code attempts to detect the Windows time zone directly,
- as set in the Windows Date and Time control panel. It attempts
- to work on versions greater than Windows Vista and on localized
- installs. It works by directly interrogating the registry and
- comparing the data there with the data returned by the
- GetTimeZoneInformation API, along with some other strategies. The
- registry contains time zone data under this key:
-
- HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\
-
- Under this key are several subkeys, one for each time zone. For
- example these subkeys are named "Pacific Standard Time" on Vista+.
- There are some other wrinkles; see the code for
- details. The subkey name is NOT LOCALIZED, allowing us to support
- localized installs.
-
- Under the subkey are data values. We care about:
-
- Std Standard time display name, localized
- TZI Binary block of data
-
- The TZI data is of particular interest. It contains the offset, two
- more offsets for standard and daylight time, and the start and end
- rules. This is the same data returned by the GetTimeZoneInformation
- API. The API may modify the data on the way out, so we have to be
- careful, but essentially we do a binary comparison against the TZI
- blocks of various registry keys. When we find a match, we know what
- time zone Windows is set to. Since the registry key is not
- localized, we can then translate the key through a simple table
- lookup into the corresponding ICU time zone.
-
- This strategy doesn't always work because there are zones which
- share an offset and rules, so more than one TZI block will match.
- For example, both Tokyo and Seoul are at GMT+9 with no DST rules;
- their TZI blocks are identical. For these cases, we fall back to a
- name lookup. We attempt to match the display name as stored in the
- registry for the current zone to the display name stored in the
- registry for various Windows zones. By comparing the registry data
- directly we avoid conversion complications.
-
- Author: Alan Liu
- Since: ICU 2.6
- Based on original code by Carl Brown <cbrown@xnetinc.com>
+* Main Windows time zone detection function.
+* Returns the Windows time zone converted to an ICU time zone as a heap-allocated buffer, or nullptr upon failure.
+* Note: We use the Win32 API GetDynamicTimeZoneInformation to get the current time zone info.
+* This API returns a non-localized time zone name, which we can then map to an ICU time zone name.
*/
-
-/**
- * Main Windows time zone detection function. Returns the Windows
- * time zone, translated to an ICU time zone, or NULL upon failure.
- */
U_CFUNC const char* U_EXPORT2
-uprv_detectWindowsTimeZone()
+uprv_detectWindowsTimeZone()
{
UErrorCode status = U_ZERO_ERROR;
- UResourceBundle* bundle = NULL;
- char* icuid = NULL;
- char apiStdName[MAX_LENGTH_ID];
- char regStdName[MAX_LENGTH_ID];
- char tmpid[MAX_LENGTH_ID];
+ char* icuid = nullptr;
+ char dynamicTZKeyName[MAX_TIMEZONE_ID_LENGTH];
+ char tmpid[MAX_TIMEZONE_ID_LENGTH];
int32_t len;
- int id;
+ int id = GEOID_NOT_AVAILABLE;
int errorCode;
- wchar_t ISOcodeW[3]; /* 2 letter iso code in UTF-16*/
- char ISOcodeA[3]; /* 2 letter iso code in ansi */
+ wchar_t ISOcodeW[3] = {}; /* 2 letter ISO code in UTF-16 */
+ char ISOcode[3] = {}; /* 2 letter ISO code in UTF-8 */
- LONG result;
- TZI tziKey;
- TZI tziReg;
- TIME_ZONE_INFORMATION apiTZI;
+ DYNAMIC_TIME_ZONE_INFORMATION dynamicTZI;
+ uprv_memset(&dynamicTZI, 0, sizeof(dynamicTZI));
+ uprv_memset(dynamicTZKeyName, 0, sizeof(dynamicTZKeyName));
+ uprv_memset(tmpid, 0, sizeof(tmpid));
- BOOL tryPreVistaFallback;
- OSVERSIONINFO osVerInfo;
-
- /* Obtain TIME_ZONE_INFORMATION from the API, and then convert it
- to TZI. We could also interrogate the registry directly; we do
- this below if needed. */
- uprv_memset(&apiTZI, 0, sizeof(apiTZI));
- uprv_memset(&tziKey, 0, sizeof(tziKey));
- uprv_memset(&tziReg, 0, sizeof(tziReg));
- GetTimeZoneInformation(&apiTZI);
- tziKey.bias = apiTZI.Bias;
- uprv_memcpy((char *)&tziKey.standardDate, (char*)&apiTZI.StandardDate,
- sizeof(apiTZI.StandardDate));
- uprv_memcpy((char *)&tziKey.daylightDate, (char*)&apiTZI.DaylightDate,
- sizeof(apiTZI.DaylightDate));
-
- /* Convert the wchar_t* standard name to char* */
- uprv_memset(apiStdName, 0, sizeof(apiStdName));
- wcstombs(apiStdName, apiTZI.StandardName, MAX_LENGTH_ID);
-
- tmpid[0] = 0;
+ /* Obtain TIME_ZONE_INFORMATION from the API and get the non-localized time zone name. */
+ if (TIME_ZONE_ID_INVALID == GetDynamicTimeZoneInformation(&dynamicTZI)) {
+ return nullptr;
+ }
id = GetUserGeoID(GEOCLASS_NATION);
errorCode = GetGeoInfoW(id, GEO_ISO2, ISOcodeW, 3, 0);
- u_strToUTF8(ISOcodeA, 3, NULL, (const UChar *)ISOcodeW, 3, &status);
- bundle = ures_openDirect(NULL, "windowsZones", &status);
- ures_getByKey(bundle, "mapTimezones", bundle, &status);
+ // convert from wchar_t* (UTF-16 on Windows) to char* (UTF-8).
+ u_strToUTF8(ISOcode, UPRV_LENGTHOF(ISOcode), nullptr,
+ reinterpret_cast<const UChar*>(ISOcodeW), UPRV_LENGTHOF(ISOcodeW), &status);
- /*
- Windows Vista+ provides us with a "TimeZoneKeyName" that is not localized
- and can be used to directly map a name in our bundle. Try to use that first
- if we're on Vista or higher
- */
- uprv_memset(&osVerInfo, 0, sizeof(osVerInfo));
- osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
- tryPreVistaFallback = TRUE;
- result = getTZKeyName(regStdName, sizeof(regStdName));
- if(ERROR_SUCCESS == result)
- {
- UResourceBundle* winTZ = ures_getByKey(bundle, regStdName, NULL, &status);
- if(U_SUCCESS(status))
- {
- const UChar* icuTZ = NULL;
- if (errorCode != 0)
- {
- icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
+ LocalUResourceBundlePointer bundle(ures_openDirect(nullptr, "windowsZones", &status));
+ ures_getByKey(bundle.getAlias(), "mapTimezones", bundle.getAlias(), &status);
+
+ // convert from wchar_t* (UTF-16 on Windows) to char* (UTF-8).
+ u_strToUTF8(dynamicTZKeyName, UPRV_LENGTHOF(dynamicTZKeyName), nullptr,
+ reinterpret_cast<const UChar*>(dynamicTZI.TimeZoneKeyName), UPRV_LENGTHOF(dynamicTZI.TimeZoneKeyName), &status);
+
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+
+ if (dynamicTZI.TimeZoneKeyName[0] != 0) {
+ UResourceBundle winTZ;
+ ures_initStackObject(&winTZ);
+ ures_getByKey(bundle.getAlias(), dynamicTZKeyName, &winTZ, &status);
+
+ if (U_SUCCESS(status)) {
+ const UChar* icuTZ = nullptr;
+ if (errorCode != 0) {
+ icuTZ = ures_getStringByKey(&winTZ, ISOcode, &len, &status);
}
- if (errorCode==0 || icuTZ==NULL)
- {
+ if (errorCode == 0 || icuTZ == nullptr) {
/* fallback to default "001" and reset status */
status = U_ZERO_ERROR;
- icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
+ icuTZ = ures_getStringByKey(&winTZ, "001", &len, &status);
}
- if(U_SUCCESS(status))
- {
- int index=0;
- while (! (*icuTZ == '\0' || *icuTZ ==' '))
- {
- tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */
+ if (U_SUCCESS(status)) {
+ int index = 0;
+
+ while (!(*icuTZ == '\0' || *icuTZ == ' ')) {
+ // time zone IDs only contain ASCII invariant characters.
+ tmpid[index++] = (char)(*icuTZ++);
}
- tmpid[index]='\0';
- tryPreVistaFallback = FALSE;
+ tmpid[index] = '\0';
}
}
- ures_close(winTZ);
+ ures_close(&winTZ);
}
- if(tryPreVistaFallback)
- {
- /* Note: We get the winid not from static tables but from resource bundle. */
- while (U_SUCCESS(status) && ures_hasNext(bundle))
- {
- UBool idFound = FALSE;
- const char* winid;
- UResourceBundle* winTZ = ures_getNextResource(bundle, NULL, &status);
- if (U_FAILURE(status))
- {
- break;
- }
- winid = ures_getKey(winTZ);
- result = getTZI(winid, &tziReg);
-
- if (result == ERROR_SUCCESS)
- {
- /* Windows alters the DaylightBias in some situations.
- Using the bias and the rules suffices, so overwrite
- these unreliable fields. */
- tziKey.standardBias = tziReg.standardBias;
- tziKey.daylightBias = tziReg.daylightBias;
-
- if (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) == 0)
- {
- const UChar* icuTZ = NULL;
- if (errorCode != 0)
- {
- icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
- }
- if (errorCode==0 || icuTZ==NULL)
- {
- /* fallback to default "001" and reset status */
- status = U_ZERO_ERROR;
- icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
- }
-
- if (U_SUCCESS(status))
- {
- /* Get the standard name from the registry key to compare with
- the one from Windows API call. */
- uprv_memset(regStdName, 0, sizeof(regStdName));
- result = getSTDName(winid, regStdName, sizeof(regStdName));
- if (result == ERROR_SUCCESS)
- {
- if (uprv_strcmp(apiStdName, regStdName) == 0)
- {
- idFound = TRUE;
- }
- }
-
- /* tmpid buffer holds the ICU timezone ID corresponding to the timezone ID from Windows.
- * If none is found, tmpid buffer will contain a fallback ID (i.e. the time zone ID matching
- * the current time zone information)
- */
- if (idFound || tmpid[0] == 0)
- {
- /* if icuTZ has more than one city, take only the first (i.e. terminate icuTZ at first space) */
- int index=0;
- while (! (*icuTZ == '\0' || *icuTZ ==' '))
- {
- tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */
- }
- tmpid[index]='\0';
- }
- }
- }
- }
- ures_close(winTZ);
- if (idFound)
- {
- break;
- }
- }
+ // Copy the timezone ID to icuid to be returned.
+ if (tmpid[0] != 0) {
+ icuid = uprv_strdup(tmpid);
}
- /*
- * Copy the timezone ID to icuid to be returned.
- */
- if (tmpid[0] != 0)
- {
- len = uprv_strlen(tmpid);
- icuid = (char*)uprv_calloc(len + 1, sizeof(char));
- if (icuid != NULL)
- {
- uprv_strcpy(icuid, tmpid);
- }
- }
-
- ures_close(bundle);
-
return icuid;
}
-#endif /* U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0) */
+U_NAMESPACE_END
+#endif /* U_PLATFORM_HAS_WIN32_API */
diff --git a/source/common/wintz.h b/source/common/wintz.h
index 7be30eb..19b7cfe 100644
--- a/source/common/wintz.h
+++ b/source/common/wintz.h
@@ -16,9 +16,7 @@
#include "unicode/utypes.h"
-// This file contains only desktop windows behavior
-// Windows UWP calls Windows::Globalization directly, so this isn't needed there.
-#if U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0)
+#if U_PLATFORM_HAS_WIN32_API
/**
* \file
@@ -33,6 +31,6 @@
U_CFUNC const char* U_EXPORT2
uprv_detectWindowsTimeZone();
-#endif /* U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0) */
+#endif /* U_PLATFORM_HAS_WIN32_API */
#endif /* __WINTZ */