]> granicus.if.org Git - icu/commitdiff
ICU-13842 Use GetDynamicTimeZoneInformation instead of Windows registry access in...
authorWilliam Zhao <39807811+mingyzha@users.noreply.github.com>
Thu, 23 Aug 2018 21:27:17 +0000 (14:27 -0700)
committerShane Carr <shane@unicode.org>
Thu, 27 Sep 2018 21:27:38 +0000 (14:27 -0700)
Use GetDynamicTimeZoneInformation instead of accessing the Windows registries so that it does not rely on COM in wintz. So that UWP apps could also use this code.

icu4c/source/common/common_uwp.vcxproj
icu4c/source/common/putil.cpp
icu4c/source/common/wintz.cpp
icu4c/source/common/wintz.h

index a651d505b2fafd886fae305fadfc2a9821ab4b7f..d7f0187ceac9f66cbba8bf27d14a00d109821a54 100644 (file)
     <ClCompile Include="umutex.cpp" />
     <ClCompile Include="utrace.cpp" />
     <ClCompile Include="utypes.cpp" />
-    <ClCompile Include="wintz.cpp">
-      <ExcludedFromBuild>true</ExcludedFromBuild>
-    </ClCompile>
+    <ClCompile Include="wintz.cpp" />
     <ClCompile Include="ucnv.cpp" />
     <ClCompile Include="ucnv2022.cpp" />
     <ClCompile Include="ucnv_bld.cpp" />
index 2d2de524dbbbf84527570fd302f40d0defb9a4d5..229656d640d51071d150a4713622b3edca37d5f5 100644 (file)
 #   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;
index 1ea8c9eed6c8ab7013678390a223ff249b39e689..df4aa061670defd7c67c6a8add91eb154d0c7b53 100644 (file)
@@ -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"
 #   define NOMCX
 #include <windows.h>
 
-#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<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, 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<int32_t>(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  */
index 7be30eb44702c25c31437549e097d47cda95d5d9..19b7cfe921fb22cf65c25e60f36a38da69592856 100644 (file)
@@ -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 */