]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/wintz.c
ICU-57166.0.1.tar.gz
[apple/icu.git] / icuSources / common / wintz.c
index 136eba9e28774e950c1f208e2930cdff17a8aaf4..89fc35061ea39345114099b382dfcea6d1a58608 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ********************************************************************************
-*   Copyright (C) 2005-2006, International Business Machines
+*   Copyright (C) 2005-2015, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 ********************************************************************************
 *
 
 #include "unicode/utypes.h"
 
-#ifdef U_WINDOWS
+#if U_PLATFORM_HAS_WIN32_API
 
 #include "wintz.h"
-
 #include "cmemory.h"
 #include "cstring.h"
 
+#include "unicode/ures.h"
 #include "unicode/ustring.h"
 
 #   define WIN32_LEAN_AND_MEAN
 #   define NOMCX
 #include <windows.h>
 
-#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
-#define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
-#define DELETE_ARRAY(array) uprv_free((void *) (array))
-
-#define ICUID_STACK_BUFFER_SIZE 32
+#define MAX_LENGTH_ID 40
 
 /* The layout of the Tzi value in the registry */
 typedef struct
@@ -44,22 +40,11 @@ typedef struct
     SYSTEMTIME daylightDate;
 } TZI;
 
-typedef struct
-{
-    const char *icuid;
-    const char *winid;
-} WindowsICUMap;
-
-typedef struct {
-    const char* winid;
-    const char* altwinid;
-} WindowsZoneRemap;
-
 /**
  * Various registry keys and key fragments.
  */
 static const char CURRENT_ZONE_REGKEY[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\";
-static const char STANDARD_NAME_REGKEY[] = "StandardName";
+/* static const char STANDARD_NAME_REGKEY[] = "StandardName"; Currently unused constant */
 static const char STANDARD_TIME_REGKEY[] = " Standard Time";
 static const char TZI_REGKEY[] = "TZI";
 static const char STD_REGKEY[] = "Std";
@@ -98,146 +83,12 @@ static const char* const TZ_REGKEY[] = {
  * the registry.
  */
 enum {
-    WIN_9X_ME_TYPE = 0,
-    WIN_NT_TYPE = 1,
-    WIN_2K_XP_TYPE = 2
+    WIN_9X_ME_TYPE = 1,
+    WIN_NT_TYPE = 2,
+    WIN_2K_XP_TYPE = 3
 };
 
-/*
- * TODO: Sort on ICU ID?
- * TODO: This data should come from ICU/CLDR...
- */
-static const WindowsICUMap ZONE_MAP[] = {
-    {"Etc/GMT+12",           "Dateline"}, /* S (GMT-12:00) International Date Line West */
-
-    {"Pacific/Apia",         "Samoa"}, /* S (GMT-11:00) Midway Island, Samoa */
-
-    {"Pacific/Honolulu",     "Hawaiian"}, /* S (GMT-10:00) Hawaii */
-
-    {"America/Anchorage",    "Alaskan"}, /* D (GMT-09:00) Alaska */
-
-    {"America/Los_Angeles",  "Pacific"}, /* D (GMT-08:00) Pacific Time (US & Canada); Tijuana */
-
-    {"America/Phoenix",      "US Mountain"}, /* S (GMT-07:00) Arizona */
-    {"America/Denver",       "Mountain"}, /* D (GMT-07:00) Mountain Time (US & Canada) */
-    {"America/Chihuahua",    "Mexico Standard Time 2"}, /* D (GMT-07:00) Chihuahua, La Paz, Mazatlan */
-
-    {"America/Managua",      "Central America"}, /* S (GMT-06:00) Central America */
-    {"America/Regina",       "Canada Central"}, /* S (GMT-06:00) Saskatchewan */
-    {"America/Mexico_City",  "Mexico"}, /* D (GMT-06:00) Guadalajara, Mexico City, Monterrey */
-    {"America/Chicago",      "Central"}, /* D (GMT-06:00) Central Time (US & Canada) */
-
-    {"America/Indianapolis", "US Eastern"}, /* S (GMT-05:00) Indiana (East) */
-    {"America/Bogota",       "SA Pacific"}, /* S (GMT-05:00) Bogota, Lima, Quito */
-    {"America/New_York",     "Eastern"}, /* D (GMT-05:00) Eastern Time (US & Canada) */
-
-    {"America/Caracas",      "SA Western"}, /* S (GMT-04:00) Caracas, La Paz */
-    {"America/Santiago",     "Pacific SA"}, /* D (GMT-04:00) Santiago */
-    {"America/Halifax",      "Atlantic"}, /* D (GMT-04:00) Atlantic Time (Canada) */
-
-    {"America/St_Johns",     "Newfoundland"}, /* D (GMT-03:30) Newfoundland */
-
-    {"America/Buenos_Aires", "SA Eastern"}, /* S (GMT-03:00) Buenos Aires, Georgetown */
-    {"America/Godthab",      "Greenland"}, /* D (GMT-03:00) Greenland */
-    {"America/Sao_Paulo",    "E. South America"}, /* D (GMT-03:00) Brasilia */
-
-    {"America/Noronha",      "Mid-Atlantic"}, /* D (GMT-02:00) Mid-Atlantic */
-
-    {"Atlantic/Cape_Verde",  "Cape Verde"}, /* S (GMT-01:00) Cape Verde Is. */
-    {"Atlantic/Azores",      "Azores"}, /* D (GMT-01:00) Azores */
-
-    {"Africa/Casablanca",    "Greenwich"}, /* S (GMT) Casablanca, Monrovia */
-    {"Europe/London",        "GMT"}, /* D (GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London */
-
-    {"Africa/Lagos",         "W. Central Africa"}, /* S (GMT+01:00) West Central Africa */
-    {"Europe/Berlin",        "W. Europe"}, /* D (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna */
-    {"Europe/Paris",         "Romance"}, /* D (GMT+01:00) Brussels, Copenhagen, Madrid, Paris */
-    {"Europe/Sarajevo",      "Central European"}, /* D (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb */
-    {"Europe/Belgrade",      "Central Europe"}, /* D (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague */
-
-    {"Africa/Johannesburg",  "South Africa"}, /* S (GMT+02:00) Harare, Pretoria */
-    {"Asia/Jerusalem",       "Israel"}, /* S (GMT+02:00) Jerusalem */
-    {"Europe/Istanbul",      "GTB"}, /* D (GMT+02:00) Athens, Istanbul, Minsk */
-    {"Europe/Helsinki",      "FLE"}, /* D (GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius */
-    {"Africa/Cairo",         "Egypt"}, /* D (GMT+02:00) Cairo */
-    {"Europe/Bucharest",     "E. Europe"}, /* D (GMT+02:00) Bucharest */
-
-    {"Africa/Nairobi",       "E. Africa"}, /* S (GMT+03:00) Nairobi */
-    {"Asia/Riyadh",          "Arab"}, /* S (GMT+03:00) Kuwait, Riyadh */
-    {"Europe/Moscow",        "Russian"}, /* D (GMT+03:00) Moscow, St. Petersburg, Volgograd */
-    {"Asia/Baghdad",         "Arabic"}, /* D (GMT+03:00) Baghdad */
-
-    {"Asia/Tehran",          "Iran"}, /* D (GMT+03:30) Tehran */
-
-    {"Asia/Muscat",          "Arabian"}, /* S (GMT+04:00) Abu Dhabi, Muscat */
-    {"Asia/Tbilisi",         "Caucasus"}, /* D (GMT+04:00) Baku, Tbilisi, Yerevan */
-
-    {"Asia/Kabul",           "Afghanistan"}, /* S (GMT+04:30) Kabul */
-
-    {"Asia/Karachi",         "West Asia"}, /* S (GMT+05:00) Islamabad, Karachi, Tashkent */
-    {"Asia/Yekaterinburg",   "Ekaterinburg"}, /* D (GMT+05:00) Ekaterinburg */
-
-    {"Asia/Calcutta",        "India"}, /* S (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi */
-
-    {"Asia/Katmandu",        "Nepal"}, /* S (GMT+05:45) Kathmandu */
-
-    {"Asia/Colombo",         "Sri Lanka"}, /* S (GMT+06:00) Sri Jayawardenepura */
-    {"Asia/Dhaka",           "Central Asia"}, /* S (GMT+06:00) Astana, Dhaka */
-    {"Asia/Novosibirsk",     "N. Central Asia"}, /* D (GMT+06:00) Almaty, Novosibirsk */
-
-    {"Asia/Rangoon",         "Myanmar"}, /* S (GMT+06:30) Rangoon */
-
-    {"Asia/Bangkok",         "SE Asia"}, /* S (GMT+07:00) Bangkok, Hanoi, Jakarta */
-    {"Asia/Krasnoyarsk",     "North Asia"}, /* D (GMT+07:00) Krasnoyarsk */
-
-    {"Australia/Perth",      "W. Australia"}, /* S (GMT+08:00) Perth */
-    {"Asia/Taipei",          "Taipei"}, /* S (GMT+08:00) Taipei */
-    {"Asia/Singapore",       "Singapore"}, /* S (GMT+08:00) Kuala Lumpur, Singapore */
-    {"Asia/Hong_Kong",       "China"}, /* S (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi */
-    {"Asia/Irkutsk",         "North Asia East"}, /* D (GMT+08:00) Irkutsk, Ulaan Bataar */
-
-    {"Asia/Tokyo",           "Tokyo"}, /* S (GMT+09:00) Osaka, Sapporo, Tokyo */
-    {"Asia/Seoul",           "Korea"}, /* S (GMT+09:00) Seoul */
-    {"Asia/Yakutsk",         "Yakutsk"}, /* D (GMT+09:00) Yakutsk */
-
-    {"Australia/Darwin",     "AUS Central"}, /* S (GMT+09:30) Darwin */
-    {"Australia/Adelaide",   "Cen. Australia"}, /* D (GMT+09:30) Adelaide */
-
-    {"Pacific/Guam",         "West Pacific"}, /* S (GMT+10:00) Guam, Port Moresby */
-    {"Australia/Brisbane",   "E. Australia"}, /* S (GMT+10:00) Brisbane */
-    {"Asia/Vladivostok",     "Vladivostok"}, /* D (GMT+10:00) Vladivostok */
-    {"Australia/Hobart",     "Tasmania"}, /* D (GMT+10:00) Hobart */
-    {"Australia/Sydney",     "AUS Eastern"}, /* D (GMT+10:00) Canberra, Melbourne, Sydney */
-
-    {"Asia/Magadan",         "Central Pacific"}, /* S (GMT+11:00) Magadan, Solomon Is., New Caledonia */
-
-    {"Pacific/Fiji",         "Fiji"}, /* S (GMT+12:00) Fiji, Kamchatka, Marshall Is. */
-    {"Pacific/Auckland",     "New Zealand"}, /* D (GMT+12:00) Auckland, Wellington */
-
-    {"Pacific/Tongatapu",    "Tonga"}, /* S (GMT+13:00) Nuku'alofa */
-    NULL,                    NULL
-};
-
-/**
- * If a lookup fails, we attempt to remap certain Windows ids to
- * alternate Windows ids.  If the alternate listed here begins with
- * '-', we use it as is (without the '-').  If it begins with '+', we
- * append a " Standard Time" if appropriate.
- */
-static const WindowsZoneRemap ZONE_REMAP[] = {
-    "Central European",     "-Warsaw",
-    "Central Europe",       "-Prague Bratislava",
-    "China",                "-Beijing",
-                                               
-    "Greenwich",            "+GMT",
-    "GTB",                  "+GFT",
-    "Arab",                 "+Saudi Arabia",
-    "SE Asia",              "+Bangkok",
-    "AUS Eastern",          "+Sydney",
-    NULL,                   NULL,
-};
-
-static int32_t fWinType = -1;
+static int32_t gWinType = 0;
 
 static int32_t detectWindowsType()
 {
@@ -250,8 +101,8 @@ static int32_t detectWindowsType()
         really want to know is how the registry is laid out.
         Specifically, is it 9x/Me or not, and is it "GMT" or "GMT
         Standard Time". */
-    for (winType = 0; winType < 2; winType += 1) {
-        result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+    for (winType = 0; winType < 2; winType++) {
+        result = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
                               WIN_TYPE_PROBE_REGKEY[winType],
                               0,
                               KEY_QUERY_VALUE,
@@ -263,92 +114,40 @@ static int32_t detectWindowsType()
         }
     }
 
-    return winType;
-}
-
-/*
- * TODO: Binary search sorted ZONE_MAP...
- * (u_detectWindowsTimeZone() needs them sorted by offset...)
- */
-static const char *findWindowsZoneID(const UChar *icuid, int32_t length)
-{
-    char stackBuffer[ICUID_STACK_BUFFER_SIZE];
-    char *buffer = stackBuffer;
-    const char *result = NULL;
-    int i;
-
-    /*
-     * NOTE: >= because length doesn't include
-     * trailing null.
-     */
-    if (length >= ICUID_STACK_BUFFER_SIZE) {
-        buffer = NEW_ARRAY(char, length + 1);
-    }
-
-    u_UCharsToChars(icuid, buffer, length);
-    buffer[length] = '\0';
-
-    for (i = 0; ZONE_MAP[i].icuid != NULL; i += 1) {
-        if (uprv_strcmp(buffer, ZONE_MAP[i].icuid) == 0) {
-            result = ZONE_MAP[i].winid;
-            break;
-        }
-    }
-
-    if (buffer != stackBuffer) {
-        DELETE_ARRAY(buffer);
-    }
-
-    return result;
+    return winType+1; /* +1 to bring it inline with the enum */
 }
 
 static LONG openTZRegKey(HKEY *hkey, const char *winid)
 {
-    char subKeyName[96]; /* TODO: why 96?? */
+    /* subKeyName needs to be long enough for the longest TZ_REGKEY, plus the longest Windows
+     * tzid (current or obsolete), plus an appended STANDARD_TIME_REGKEY, plus a 0 terminator.
+     * At its max point this was 111, but the code had 110. Make it 128 for some wiggle room. */
+    char subKeyName[128];
     char *name;
     LONG result;
 
-    /* TODO: This isn't thread safe, but it's probably good enough. */
-    if (fWinType < 0) {
-        fWinType = detectWindowsType();
+    /* This isn't thread safe, but it's good enough because the result should be constant per system. */
+    if (gWinType <= 0) {
+        gWinType = detectWindowsType();
     }
 
-    uprv_strcpy(subKeyName, TZ_REGKEY[(fWinType == WIN_9X_ME_TYPE) ? 0 : 1]);
+    uprv_strcpy(subKeyName, TZ_REGKEY[(gWinType != WIN_9X_ME_TYPE)]);
     name = &subKeyName[strlen(subKeyName)];
     uprv_strcat(subKeyName, winid);
 
-    if (fWinType != WIN_9X_ME_TYPE &&
-        (winid[strlen(winid) - 1] != '2') &&
-        !(fWinType == WIN_NT_TYPE && strcmp(winid, "GMT") == 0)) {
-        uprv_strcat(subKeyName, STANDARD_TIME_REGKEY);
+    if (gWinType == WIN_9X_ME_TYPE) {
+        /* Remove " Standard Time" */
+        char *pStd = uprv_strstr(subKeyName, STANDARD_TIME_REGKEY);
+        if (pStd) {
+            *pStd = 0;
+        }
     }
 
-    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+    result = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
                             subKeyName,
                             0,
                             KEY_QUERY_VALUE,
                             hkey);
-
-    if (result != ERROR_SUCCESS) {
-        int i;
-
-        /* If the primary lookup fails, try to remap the Windows zone
-           ID, according to the remapping table. */
-        for (i=0; ZONE_REMAP[i].winid; i++) {
-            if (uprv_strcmp(winid, ZONE_REMAP[i].winid) == 0) {
-                uprv_strcpy(name, ZONE_REMAP[i].altwinid + 1);
-                if (*(ZONE_REMAP[i].altwinid) == '+' && fWinType != WIN_9X_ME_TYPE) {
-                    uprv_strcat(subKeyName, STANDARD_TIME_REGKEY);                
-                }
-                return RegOpenKeyEx(HKEY_LOCAL_MACHINE,
-                                      subKeyName,
-                                      0,
-                                      KEY_QUERY_VALUE,
-                                      hkey);
-            }
-        }
-    }
-
     return result;
 }
 
@@ -361,7 +160,7 @@ static LONG getTZI(const char *winid, TZI *tzi)
     result = openTZRegKey(&hkey, winid);
 
     if (result == ERROR_SUCCESS) {
-        result = RegQueryValueEx(hkey,
+        result = RegQueryValueExA(hkey,
                                     TZI_REGKEY,
                                     NULL,
                                     NULL,
@@ -375,30 +174,50 @@ static LONG getTZI(const char *winid, TZI *tzi)
     return result;
 }
 
-U_CAPI UBool U_EXPORT2
-uprv_getWindowsTimeZoneInfo(TIME_ZONE_INFORMATION *zoneInfo, const UChar *icuid, int32_t length)
-{
-    const char *winid;
-    TZI tzi;
+static LONG getSTDName(const char *winid, char *regStdName, int32_t length) {
+    DWORD cbData = length;
     LONG result;
-    
-    winid = findWindowsZoneID(icuid, length);
+    HKEY hkey;
 
-    if (winid != NULL) {
-        result = getTZI(winid, &tzi);
+    result = openTZRegKey(&hkey, winid);
 
-        if (result == ERROR_SUCCESS) {
-            zoneInfo->Bias         = tzi.bias;
-            zoneInfo->DaylightBias = tzi.daylightBias;
-            zoneInfo->StandardBias = tzi.standardBias;
-            zoneInfo->DaylightDate = tzi.daylightDate;
-            zoneInfo->StandardDate = tzi.standardDate;
+    if (result == ERROR_SUCCESS) {
+        result = RegQueryValueExA(hkey,
+                                    STD_REGKEY,
+                                    NULL,
+                                    NULL,
+                                    (LPBYTE)regStdName,
+                                    &cbData);
 
-            return TRUE;
-        }
     }
 
-    return FALSE;
+    RegCloseKey(hkey);
+
+    return result;
+}
+
+static LONG getTZKeyName(char* tzKeyName, int32_t length) {
+    HKEY hkey;
+    LONG result = FALSE;
+    DWORD cbData = length;
+
+    if(ERROR_SUCCESS == RegOpenKeyExA(
+        HKEY_LOCAL_MACHINE,
+        CURRENT_ZONE_REGKEY,
+        0, 
+        KEY_QUERY_VALUE,
+        &hkey))
+    {
+         result = RegQueryValueExA(
+             hkey,
+             "TimeZoneKeyName",
+             NULL,
+             NULL,
+             (LPBYTE)tzKeyName,
+             &cbData);
+    }
+
+    return result;
 }
 
 /*
@@ -453,15 +272,28 @@ uprv_getWindowsTimeZoneInfo(TIME_ZONE_INFORMATION *zoneInfo, const UChar *icuid,
  * Main Windows time zone detection function.  Returns the Windows
  * time zone, translated to an ICU time zone, or NULL upon failure.
  */
-U_CAPI const char* U_EXPORT2
+U_CFUNC const char* U_EXPORT2
 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];
+    int32_t len;
+    int id;
+    int errorCode;
+    UChar ISOcodeW[3]; /* 2 letter iso code in UTF-16*/
+    char  ISOcodeA[3]; /* 2 letter iso code in ansi */
+
     LONG result;
-    HKEY hkey;
     TZI tziKey;
     TZI tziReg;
     TIME_ZONE_INFORMATION apiTZI;
-    int firstMatch, lastMatch;
-    int j;
+
+    BOOL isVistaOrHigher;
+    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
@@ -476,106 +308,135 @@ uprv_detectWindowsTimeZone() {
     uprv_memcpy((char *)&tziKey.daylightDate, (char*)&apiTZI.DaylightDate,
            sizeof(apiTZI.DaylightDate));
 
-    /* For each zone that can be identified by Offset+Rules, see if we
-       have a match.  Continue scanning after finding a match,
-       recording the index of the first and the last match.  We have
-       to do this because some zones are not unique under
-       Offset+Rules. */
-    firstMatch = -1;
-    lastMatch = -1;
-    for (j=0; ZONE_MAP[j].icuid; j++) {
-        result = getTZI(ZONE_MAP[j].winid, &tziReg);
+    /* Convert the wchar_t* standard name to char* */
+    uprv_memset(apiStdName, 0, sizeof(apiStdName));
+    wcstombs(apiStdName, apiTZI.StandardName, MAX_LENGTH_ID);
 
-        if (result == ERROR_SUCCESS) {
-            /* Assume that offsets are grouped together, and bail out
-               when we've scanned everything with a matching
-               offset. */
-            if (firstMatch >= 0 && tziKey.bias != tziReg.bias) {
-                break;
-            }
+    tmpid[0] = 0;
+
+    id = GetUserGeoID(GEOCLASS_NATION);
+    errorCode = GetGeoInfoW(id,GEO_ISO2,ISOcodeW,3,0);
+    u_strToUTF8(ISOcodeA, 3, NULL, ISOcodeW, 3, &status);
 
-            /* 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;
+    bundle = ures_openDirect(NULL, "windowsZones", &status);
+    ures_getByKey(bundle, "mapTimezones", bundle, &status);
 
-            if (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) == 0) {
-                if (firstMatch < 0) {
-                    firstMatch = j;
+    /*
+        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);
+    GetVersionEx(&osVerInfo);
+    isVistaOrHigher = osVerInfo.dwMajorVersion >= 6;   /* actually includes Windows Server 2008 as well, but don't worry about it */
+    tryPreVistaFallback = TRUE;
+    if(isVistaOrHigher) {
+        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 (errorCode==0 || icuTZ==NULL) {
+                    /* fallback to default "001" and reset status */
+                    status = U_ZERO_ERROR;
+                    icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
                 }
 
-                lastMatch = j;
+                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);
         }
     }
 
-    /* This should never happen; if it does it means our table doesn't
-       match Windows AT ALL, perhaps because this is post-XP? */
-    if (firstMatch < 0) {
-        return NULL;
-    }
-    
-    if (firstMatch != lastMatch) {
-        char stdName[32];
-        DWORD stdNameSize;
-        char stdRegName[64];
-        DWORD stdRegNameSize;
-
-        /* Offset+Rules lookup yielded >= 2 matches.  Try to match the
-           localized display name.  Get the name from the registry
-           (not the API). This avoids conversion issues.  Use the
-           standard name, since Windows modifies the daylight name to
-           match the standard name if there is no DST. */
-        if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
-                              CURRENT_ZONE_REGKEY,
-                              0,
-                              KEY_QUERY_VALUE,
-                              &hkey) == ERROR_SUCCESS)
-        {
-            stdNameSize = sizeof(stdName);
-            result = RegQueryValueEx(hkey,
-                                     (LPTSTR)STANDARD_NAME_REGKEY,
-                                     NULL,
-                                     NULL,
-                                     (LPBYTE)stdName,
-                                     &stdNameSize);
-            RegCloseKey(hkey);
-
-            /*
-             * Scan through the Windows time zone data in the registry
-             * again (just the range of zones with matching TZIs) and
-             * look for a standard display name match.
-             */
-            for (j = firstMatch; j <= lastMatch; j += 1) {
-                stdRegNameSize = sizeof(stdRegName);
-                result = openTZRegKey(&hkey, ZONE_MAP[j].winid);
-
-                if (result == ERROR_SUCCESS) {
-                    result = RegQueryValueEx(hkey,
-                                             (LPTSTR)STD_REGKEY,
-                                             NULL,
-                                             NULL,
-                                             (LPBYTE)stdRegName,
-                                             &stdRegNameSize);
-                }
-
-                RegCloseKey(hkey);
+    if(tryPreVistaFallback) {
 
-                if (result == ERROR_SUCCESS &&
-                    stdRegNameSize == stdNameSize &&
-                    uprv_memcmp(stdName, stdRegName, stdNameSize) == 0)
-                {
-                    firstMatch = j; /* record the match */
-                    break;
+        /* 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';
+                        }
+                    }
                 }
             }
-        } else {
-            RegCloseKey(hkey); /* should never get here */
+            ures_close(winTZ);
+            if (idFound) {
+                break;
+            }
         }
     }
 
-    return ZONE_MAP[firstMatch].icuid;
+    /*
+     * 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 /* #ifdef U_WINDOWS */
+#endif /* U_PLATFORM_HAS_WIN32_API */