X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/73c04bcfe1096173b00431f0cdc742894b15eef0..f59164e3d128c7675a4d3934206346a3384e53a5:/icuSources/common/wintz.c diff --git a/icuSources/common/wintz.c b/icuSources/common/wintz.c index 136eba9e..89fc3506 100644 --- a/icuSources/common/wintz.c +++ b/icuSources/common/wintz.c @@ -1,6 +1,6 @@ /* ******************************************************************************** -* Copyright (C) 2005-2006, International Business Machines +* Copyright (C) 2005-2015, International Business Machines * Corporation and others. All Rights Reserved. ******************************************************************************** * @@ -11,13 +11,13 @@ #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 @@ -28,11 +28,7 @@ # define NOMCX #include -#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 */