OLD | NEW |
(Empty) | |
| 1 /* |
| 2 ******************************************************************************** |
| 3 * Copyright (C) 2005-2010, International Business Machines |
| 4 * Corporation and others. All Rights Reserved. |
| 5 ******************************************************************************** |
| 6 * |
| 7 * File WINTZ.CPP |
| 8 * |
| 9 ******************************************************************************** |
| 10 */ |
| 11 |
| 12 #include "unicode/utypes.h" |
| 13 |
| 14 #ifdef U_WINDOWS |
| 15 |
| 16 #include "wintz.h" |
| 17 |
| 18 #include "cmemory.h" |
| 19 #include "cstring.h" |
| 20 |
| 21 #include "unicode/ustring.h" |
| 22 #include "unicode/ures.h" |
| 23 |
| 24 # define WIN32_LEAN_AND_MEAN |
| 25 # define VC_EXTRALEAN |
| 26 # define NOUSER |
| 27 # define NOSERVICE |
| 28 # define NOIME |
| 29 # define NOMCX |
| 30 #include <windows.h> |
| 31 |
| 32 /* The layout of the Tzi value in the registry */ |
| 33 typedef struct |
| 34 { |
| 35 int32_t bias; |
| 36 int32_t standardBias; |
| 37 int32_t daylightBias; |
| 38 SYSTEMTIME standardDate; |
| 39 SYSTEMTIME daylightDate; |
| 40 } TZI; |
| 41 |
| 42 /** |
| 43 * Various registry keys and key fragments. |
| 44 */ |
| 45 static const char CURRENT_ZONE_REGKEY[] = "SYSTEM\\CurrentControlSet\\Control\\T
imeZoneInformation\\"; |
| 46 static const char STANDARD_NAME_REGKEY[] = "StandardName"; |
| 47 static const char STANDARD_TIME_REGKEY[] = " Standard Time"; |
| 48 static const char TZI_REGKEY[] = "TZI"; |
| 49 static const char STD_REGKEY[] = "Std"; |
| 50 |
| 51 /** |
| 52 * HKLM subkeys used to probe for the flavor of Windows. Note that we |
| 53 * specifically check for the "GMT" zone subkey; this is present on |
| 54 * NT, but on XP has become "GMT Standard Time". We need to |
| 55 * discriminate between these cases. |
| 56 */ |
| 57 static const char* const WIN_TYPE_PROBE_REGKEY[] = { |
| 58 /* WIN_9X_ME_TYPE */ |
| 59 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones", |
| 60 |
| 61 /* WIN_NT_TYPE */ |
| 62 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\GMT" |
| 63 |
| 64 /* otherwise: WIN_2K_XP_TYPE */ |
| 65 }; |
| 66 |
| 67 /** |
| 68 * The time zone root subkeys (under HKLM) for different flavors of |
| 69 * Windows. |
| 70 */ |
| 71 static const char* const TZ_REGKEY[] = { |
| 72 /* WIN_9X_ME_TYPE */ |
| 73 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones\\", |
| 74 |
| 75 /* WIN_NT_TYPE | WIN_2K_XP_TYPE */ |
| 76 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\" |
| 77 }; |
| 78 |
| 79 /** |
| 80 * Flavor of Windows, from our perspective. Not a real OS version, |
| 81 * but rather the flavor of the layout of the time zone information in |
| 82 * the registry. |
| 83 */ |
| 84 enum { |
| 85 WIN_9X_ME_TYPE = 1, |
| 86 WIN_NT_TYPE = 2, |
| 87 WIN_2K_XP_TYPE = 3 |
| 88 }; |
| 89 |
| 90 static int32_t gWinType = 0; |
| 91 |
| 92 static int32_t detectWindowsType() |
| 93 { |
| 94 int32_t winType; |
| 95 LONG result; |
| 96 HKEY hkey; |
| 97 |
| 98 /* Detect the version of windows by trying to open a sequence of |
| 99 probe keys. We don't use the OS version API because what we |
| 100 really want to know is how the registry is laid out. |
| 101 Specifically, is it 9x/Me or not, and is it "GMT" or "GMT |
| 102 Standard Time". */ |
| 103 for (winType = 0; winType < 2; winType++) { |
| 104 result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, |
| 105 WIN_TYPE_PROBE_REGKEY[winType], |
| 106 0, |
| 107 KEY_QUERY_VALUE, |
| 108 &hkey); |
| 109 RegCloseKey(hkey); |
| 110 |
| 111 if (result == ERROR_SUCCESS) { |
| 112 break; |
| 113 } |
| 114 } |
| 115 |
| 116 return winType+1; // +1 to bring it inline with the enum |
| 117 } |
| 118 |
| 119 static LONG openTZRegKey(HKEY *hkey, const char *winid) |
| 120 { |
| 121 char subKeyName[110]; /* TODO: why 96?? */ |
| 122 char *name; |
| 123 LONG result; |
| 124 |
| 125 /* This isn't thread safe, but it's good enough because the result should be
constant per system. */ |
| 126 if (gWinType <= 0) { |
| 127 gWinType = detectWindowsType(); |
| 128 } |
| 129 |
| 130 uprv_strcpy(subKeyName, TZ_REGKEY[(gWinType != WIN_9X_ME_TYPE)]); |
| 131 name = &subKeyName[strlen(subKeyName)]; |
| 132 uprv_strcat(subKeyName, winid); |
| 133 |
| 134 if (gWinType == WIN_9X_ME_TYPE) { |
| 135 /* Remove " Standard Time" */ |
| 136 char *pStd = uprv_strstr(subKeyName, STANDARD_TIME_REGKEY); |
| 137 if (pStd) { |
| 138 *pStd = 0; |
| 139 } |
| 140 } |
| 141 |
| 142 result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, |
| 143 subKeyName, |
| 144 0, |
| 145 KEY_QUERY_VALUE, |
| 146 hkey); |
| 147 return result; |
| 148 } |
| 149 |
| 150 static LONG getTZI(const char *winid, TZI *tzi) |
| 151 { |
| 152 DWORD cbData = sizeof(TZI); |
| 153 LONG result; |
| 154 HKEY hkey; |
| 155 |
| 156 result = openTZRegKey(&hkey, winid); |
| 157 |
| 158 if (result == ERROR_SUCCESS) { |
| 159 result = RegQueryValueExA(hkey, |
| 160 TZI_REGKEY, |
| 161 NULL, |
| 162 NULL, |
| 163 (LPBYTE)tzi, |
| 164 &cbData); |
| 165 |
| 166 } |
| 167 |
| 168 RegCloseKey(hkey); |
| 169 |
| 170 return result; |
| 171 } |
| 172 |
| 173 /* |
| 174 This code attempts to detect the Windows time zone, as set in the |
| 175 Windows Date and Time control panel. It attempts to work on |
| 176 multiple flavors of Windows (9x, Me, NT, 2000, XP) and on localized |
| 177 installs. It works by directly interrogating the registry and |
| 178 comparing the data there with the data returned by the |
| 179 GetTimeZoneInformation API, along with some other strategies. The |
| 180 registry contains time zone data under one of two keys (depending on |
| 181 the flavor of Windows): |
| 182 |
| 183 HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones\ |
| 184 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\ |
| 185 |
| 186 Under this key are several subkeys, one for each time zone. These |
| 187 subkeys are named "Pacific" on Win9x/Me and "Pacific Standard Time" |
| 188 on WinNT/2k/XP. There are some other wrinkles; see the code for |
| 189 details. The subkey name is NOT LOCALIZED, allowing us to support |
| 190 localized installs. |
| 191 |
| 192 Under the subkey are data values. We care about: |
| 193 |
| 194 Std Standard time display name, localized |
| 195 TZI Binary block of data |
| 196 |
| 197 The TZI data is of particular interest. It contains the offset, two |
| 198 more offsets for standard and daylight time, and the start and end |
| 199 rules. This is the same data returned by the GetTimeZoneInformation |
| 200 API. The API may modify the data on the way out, so we have to be |
| 201 careful, but essentially we do a binary comparison against the TZI |
| 202 blocks of various registry keys. When we find a match, we know what |
| 203 time zone Windows is set to. Since the registry key is not |
| 204 localized, we can then translate the key through a simple table |
| 205 lookup into the corresponding ICU time zone. |
| 206 |
| 207 This strategy doesn't always work because there are zones which |
| 208 share an offset and rules, so more than one TZI block will match. |
| 209 For example, both Tokyo and Seoul are at GMT+9 with no DST rules; |
| 210 their TZI blocks are identical. For these cases, we fall back to a |
| 211 name lookup. We attempt to match the display name as stored in the |
| 212 registry for the current zone to the display name stored in the |
| 213 registry for various Windows zones. By comparing the registry data |
| 214 directly we avoid conversion complications. |
| 215 |
| 216 Author: Alan Liu |
| 217 Since: ICU 2.6 |
| 218 Based on original code by Carl Brown <cbrown@xnetinc.com> |
| 219 */ |
| 220 |
| 221 /** |
| 222 * Main Windows time zone detection function. Returns the Windows |
| 223 * time zone, translated to an ICU time zone, or NULL upon failure. |
| 224 */ |
| 225 U_CFUNC const char* U_EXPORT2 |
| 226 uprv_detectWindowsTimeZone() { |
| 227 UErrorCode status = U_ZERO_ERROR; |
| 228 UResourceBundle* bundle = NULL; |
| 229 char* icuid = NULL; |
| 230 |
| 231 LONG result; |
| 232 TZI tziKey; |
| 233 TZI tziReg; |
| 234 TIME_ZONE_INFORMATION apiTZI; |
| 235 |
| 236 /* Obtain TIME_ZONE_INFORMATION from the API, and then convert it |
| 237 to TZI. We could also interrogate the registry directly; we do |
| 238 this below if needed. */ |
| 239 uprv_memset(&apiTZI, 0, sizeof(apiTZI)); |
| 240 uprv_memset(&tziKey, 0, sizeof(tziKey)); |
| 241 uprv_memset(&tziReg, 0, sizeof(tziReg)); |
| 242 GetTimeZoneInformation(&apiTZI); |
| 243 tziKey.bias = apiTZI.Bias; |
| 244 uprv_memcpy((char *)&tziKey.standardDate, (char*)&apiTZI.StandardDate, |
| 245 sizeof(apiTZI.StandardDate)); |
| 246 uprv_memcpy((char *)&tziKey.daylightDate, (char*)&apiTZI.DaylightDate, |
| 247 sizeof(apiTZI.DaylightDate)); |
| 248 |
| 249 bundle = ures_openDirect(NULL, "windowsZones", &status); |
| 250 ures_getByKey(bundle, "mapTimezones", bundle, &status); |
| 251 |
| 252 /* Note: We get the winid not from static tables but from resource bundle. *
/ |
| 253 while (U_SUCCESS(status) && ures_hasNext(bundle)) { |
| 254 const char* winid; |
| 255 int32_t len; |
| 256 UResourceBundle* winTZ = ures_getNextResource(bundle, NULL, &status); |
| 257 if (U_FAILURE(status)) { |
| 258 break; |
| 259 } |
| 260 winid = ures_getKey(winTZ); |
| 261 result = getTZI(winid, &tziReg); |
| 262 |
| 263 if (result == ERROR_SUCCESS) { |
| 264 /* Windows alters the DaylightBias in some situations. |
| 265 Using the bias and the rules suffices, so overwrite |
| 266 these unreliable fields. */ |
| 267 tziKey.standardBias = tziReg.standardBias; |
| 268 tziKey.daylightBias = tziReg.daylightBias; |
| 269 |
| 270 if (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) ==
0) { |
| 271 const UChar* icuTZ = ures_getStringByKey(winTZ, "001", &len, &st
atus); |
| 272 if (U_SUCCESS(status)) { |
| 273 icuid = (char*)uprv_malloc(sizeof(char) * (len + 1)); |
| 274 uprv_memset(icuid, 0, len + 1); |
| 275 u_austrncpy(icuid, icuTZ, len); |
| 276 } |
| 277 } |
| 278 } |
| 279 ures_close(winTZ); |
| 280 if (icuid != NULL) { |
| 281 break; |
| 282 } |
| 283 } |
| 284 |
| 285 ures_close(bundle); |
| 286 |
| 287 return icuid; |
| 288 } |
| 289 |
| 290 #endif /* #ifdef U_WINDOWS */ |
OLD | NEW |