diff --git a/icu4c/source/common/common_uwp.vcxproj b/icu4c/source/common/common_uwp.vcxproj index a651d505b2f..d7f0187ceac 100644 --- a/icu4c/source/common/common_uwp.vcxproj +++ b/icu4c/source/common/common_uwp.vcxproj @@ -320,9 +320,7 @@ - - true - + diff --git a/icu4c/source/common/putil.cpp b/icu4c/source/common/putil.cpp index 2d2de524dbb..229656d640d 100644 --- a/icu4c/source/common/putil.cpp +++ b/icu4c/source/common/putil.cpp @@ -102,9 +102,8 @@ # define NOMCX # include # 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 #include @@ -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 calendar; - HRESULT hr = RoActivateInstance( - HStringReference(RuntimeClass_Windows_Globalization_Calendar).Get(), - &calendar); - if (SUCCEEDED(hr)) - { - ComPtr timezone; - hr = calendar.As(&timezone); - if (SUCCEEDED(hr)) - { - HString timezoneString; - hr = timezone->GetTimeZone(timezoneString.GetAddressOf()); - if (SUCCEEDED(hr)) - { - int32_t length = static_cast(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/icu4c/source/common/wintz.cpp b/icu4c/source/common/wintz.cpp index 1ea8c9eed6c..df4aa061670 100644 --- a/icu4c/source/common/wintz.cpp +++ b/icu4c/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" @@ -34,363 +32,97 @@ # define NOMCX #include -#define MAX_LENGTH_ID 40 - -/* 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(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(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 +* Main Windows time zone detection function. Returns the Windows +* time zone, translated to an ICU time zone, or nullptr upon failure. +* It is calling GetDynamicTimeZoneInformation to get the current time zone info. +* The API returns non-localized time zone name so it can be used for mapping 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]; + UResourceBundle* bundle = nullptr; + char* icuid = nullptr; + char dynamicTZKeyName[MAX_TIMEZONE_ID_LENGTH] = {}; + char tmpid[MAX_TIMEZONE_ID_LENGTH] = {}; int32_t len; int id; 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 ISOcodeA[3] = {}; /* 2 letter iso code in ansi */ - LONG result; - TZI tziKey; - TZI tziReg; - TIME_ZONE_INFORMATION apiTZI; + DYNAMIC_TIME_ZONE_INFORMATION dynamicTZI; - 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); + /* Obtain TIME_ZONE_INFORMATION from the API and get the non-localized time zone name. */ + uprv_memset(&dynamicTZI, 0, sizeof(dynamicTZI)); + if (TIME_ZONE_ID_INVALID == GetDynamicTimeZoneInformation(&dynamicTZI)) + { + return nullptr; + } tmpid[0] = 0; id = GetUserGeoID(GEOCLASS_NATION); errorCode = GetGeoInfoW(id, GEO_ISO2, ISOcodeW, 3, 0); - u_strToUTF8(ISOcodeA, 3, NULL, (const UChar *)ISOcodeW, 3, &status); + u_strToUTF8(ISOcodeA, 3, nullptr, (const UChar *)ISOcodeW, 3, &status); - bundle = ures_openDirect(NULL, "windowsZones", &status); + bundle = ures_openDirect(nullptr, "windowsZones", &status); ures_getByKey(bundle, "mapTimezones", bundle, &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) + /* Convert the wchar_t* standard name to char* */ + uprv_memset(dynamicTZKeyName, 0, sizeof(dynamicTZKeyName)); + u_strToUTF8(dynamicTZKeyName, MAX_TIMEZONE_ID_LENGTH, nullptr, (const UChar *)dynamicTZI.TimeZoneKeyName, MAX_TIMEZONE_ID_LENGTH, &status); + + if (dynamicTZI.TimeZoneKeyName[0] != 0) { - UResourceBundle* winTZ = ures_getByKey(bundle, regStdName, NULL, &status); - if(U_SUCCESS(status)) + UResourceBundle* winTZ = ures_getByKey(bundle, dynamicTZKeyName, nullptr, &status); + if (U_SUCCESS(status)) { - const UChar* icuTZ = NULL; - if (errorCode != 0) + const UChar* icuTZ = nullptr; + if (errorCode != 0) { icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &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)) + if (U_SUCCESS(status)) { - int index=0; - while (! (*icuTZ == '\0' || *icuTZ ==' ')) + int index = 0; + while (!(*icuTZ == '\0' || *icuTZ == ' ')) { - tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */ + tmpid[index++] = (char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */ } - tmpid[index]='\0'; - tryPreVistaFallback = FALSE; + tmpid[index] = '\0'; } } 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) + * Copy the timezone ID to icuid to be returned. + */ + if (tmpid[0] != 0) { len = static_cast(uprv_strlen(tmpid)); icuid = (char*)uprv_calloc(len + 1, sizeof(char)); - if (icuid != NULL) + if (icuid != nullptr) { uprv_strcpy(icuid, tmpid); } } ures_close(bundle); - + return icuid; } -#endif /* U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0) */ +#endif /* U_PLATFORM_HAS_WIN32_API */ diff --git a/icu4c/source/common/wintz.h b/icu4c/source/common/wintz.h index 7be30eb4470..19b7cfe921f 100644 --- a/icu4c/source/common/wintz.h +++ b/icu4c/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 */