/** * 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() { UErrorCode status = U_ZERO_ERROR; UResourceBundle* bundle = NULL; char* icuid = NULL; char apiStdName[MAX_LENGTH_ID]; #if U_PLATFORM != U_PF_DURANGO char regStdName[MAX_LENGTH_ID]; #endif // U_PLATFORM != U_PF_DURANGO char tmpid[MAX_LENGTH_ID]; int32_t len; int id; int errorCode; #if U_PLATFORM == U_PF_DURANGO wchar_t ISOcodeW[3]; /* 2 letter iso code */ #endif char ISOcode[3]; /* 2 letter iso code */ LONG result; #if U_PLATFORM != U_PF_DURANGO TZI tziKey; TZI tziReg; #endif // U_PLATFORM != U_PF_DURANGO TIME_ZONE_INFORMATION apiTZI; /* 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)); #if U_PLATFORM != U_PF_DURANGO uprv_memset(&tziKey, 0, sizeof(tziKey)); uprv_memset(&tziReg, 0, sizeof(tziReg)); #endif // U_PLATFORM != U_PF_DURANGO GetTimeZoneInformation(&apiTZI); #if U_PLATFORM != U_PF_DURANGO 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)); #endif // U_PLATFORM != U_PF_DURANGO /* Convert the wchar_t* standard name to char* */ uprv_memset(apiStdName, 0, sizeof(apiStdName)); wcstombs(apiStdName, apiTZI.StandardName, MAX_LENGTH_ID); tmpid[0] = 0; id = GetUserGeoID(GEOCLASS_NATION); #if U_PLATFORM == U_PF_DURANGO errorCode = GetGeoInfoW(id,GEO_ISO2,ISOcodeW,3,0); wcstombs(ISOcode, ISOcodeW, 3); #else errorCode = GetGeoInfoA(id,GEO_ISO2,ISOcode,3,0); #endif bundle = ures_openDirect(NULL, "windowsZones", &status); ures_getByKey(bundle, "mapTimezones", bundle, &status); /* 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); #if U_PLATFORM != U_PF_DURANGO result = getTZI(winid, &tziReg); if (result == ERROR_SUCCESS) #endif // U_PLATFORM != U_PF_DURANGO { #if U_PLATFORM != U_PF_DURANGO /* 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; #endif // U_PLATFORM != U_PF_DURANGO #if U_PLATFORM != U_PF_DURANGO if (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) == 0) #endif // U_PLATFORM != U_PF_DURANGO { const UChar* icuTZ = NULL; if (errorCode != 0) { icuTZ = ures_getStringByKey(winTZ, ISOcode, &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_PLATFORM != U_PF_DURANGO 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) { #endif // U_PLATFORM != U_PF_DURANGO idFound = TRUE; #if U_PLATFORM != U_PF_DURANGO } } #endif // U_PLATFORM != U_PF_DURANGO /* 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'; } #if U_PLATFORM != U_PF_DURANGO } #endif // U_PLATFORM != U_PF_DURANGO } } ures_close(winTZ); if (idFound) { break; } } /* * 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; }
/** * 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() { 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; TZI tziKey; TZI tziReg; TIME_ZONE_INFORMATION apiTZI; 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 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; id = GetUserGeoID(GEOCLASS_NATION); errorCode = GetGeoInfoW(id,GEO_ISO2,ISOcodeW,3,0); u_strToUTF8(ISOcodeA, 3, NULL, ISOcodeW, 3, &status); bundle = ures_openDirect(NULL, "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); 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); } 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; } 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) { len = uprv_strlen(tmpid); icuid = (char*)uprv_calloc(len + 1, sizeof(char)); if (icuid != NULL) { uprv_strcpy(icuid, tmpid); } } ures_close(bundle); return icuid; }
int main (int argc, char **argv) { BOOL ret; HKEY hkey, skey; WCHAR keyname[256], stdname[256], std2name[256], country[10], *spc; GEOID geo; int opt, idx, gotit = -1; setlocale (LC_ALL, ""); while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) switch (opt) { case 'h': usage (stdout); return 0; case 'V': print_version (); return 0; default: fprintf (stderr, "Try `%s --help' for more information.\n", program_invocation_short_name); return 1; } if (optind < argc) { usage (stderr); return 1; } /* First fetch current timezone information from registry. */ hkey = reg_open (HKEY_LOCAL_MACHINE, REG_TZINFO, "timezone information"); if (!hkey) return 1; /* Vista introduced the TimeZoneKeyName value, which simplifies the job a lot. */ if (!reg_query (hkey, L"TimeZoneKeyName", keyname, sizeof keyname, NULL)) { /* Pre-Vista we have a lot more to do. First fetch the name of the Standard (non-DST) timezone. If we can't get that, give up. */ if (!reg_query (hkey, L"StandardName", stdname, sizeof stdname, "timezone information")) { reg_close (hkey); return 1; } reg_close (hkey); /* Now open the timezone database registry key. Every subkey is a timezone. The key name is what we're after, but to find the right one, we have to compare the name of the previously fetched "StandardName" with the "Std" value in the timezone info... */ hkey = reg_open (HKEY_LOCAL_MACHINE, REG_TZDB, "timezone database"); if (!hkey) return 1; for (idx = 0; reg_enum (hkey, idx, keyname, sizeof keyname); ++idx) { skey = reg_open (hkey, keyname, NULL); if (skey) { /* ...however, on MUI-enabled machines, the names are not stored directly in the above StandardName, rather it is a resource pointer into tzres.dll. This is stored in MUI_Std. Fortunately it's easy to recognize this situation: If StandardName starts with @, it's a resource pointer, otherwise it's the cleartext value. */ ret = reg_query (skey, stdname[0] == L'@' ? L"MUI_Std" : L"Std", std2name, sizeof std2name, NULL); reg_close (skey); if (ret && !wcscmp (stdname, std2name)) break; } } } reg_close (hkey); /* We fetch the current Geo-location of the user and convert it to an ISO 3166-1 compatible nation code. */ *country = L'\0'; geo = GetUserGeoID (GEOCLASS_NATION); if (geo != GEOID_NOT_AVAILABLE) GetGeoInfoW (geo, GEO_ISO2, country, sizeof country, 0); /* If, for some reason, the Geo-location isn't available, we use the locale setting instead. */ if (!*country) GetLocaleInfoW (LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, sizeof country); /* Now iterate over the mapping table and find the right entry. */ for (idx = 0; idx < TZMAP_SIZE; ++idx) { if (!wcscmp (keyname, tzmap[idx].win_tzkey)) { if (gotit < 0) gotit = idx; if (!wcscmp (country, tzmap[idx].country)) break; } else if (gotit >= 0) { idx = gotit; break; } } if (idx >= TZMAP_SIZE) { if (gotit < 0) { fprintf (stderr, "%s: can't find matching POSIX timezone for " "Windows timezone \"%ls\"\n", program_invocation_short_name, keyname); return 1; } idx = gotit; } /* Got one. Print it. Note: The tzmap array is in the R/O data section on x86_64. Don't try to overwrite the space, as the code did originally. */ spc = wcschr (tzmap[idx].posix_tzid, L' '); if (!spc) spc = wcschr (tzmap[idx].posix_tzid, L'\0'); printf ("%.*ls\n", (int) (spc - tzmap[idx].posix_tzid), tzmap[idx].posix_tzid); return 0; }