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 */