]>
Commit | Line | Data |
---|---|---|
73c04bcf A |
1 | /* |
2 | ******************************************************************************** | |
46f4442e | 3 | * Copyright (C) 2005-2008, International Business Machines |
73c04bcf A |
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 | ||
23 | # define WIN32_LEAN_AND_MEAN | |
24 | # define VC_EXTRALEAN | |
25 | # define NOUSER | |
26 | # define NOSERVICE | |
27 | # define NOIME | |
28 | # define NOMCX | |
29 | #include <windows.h> | |
30 | ||
31 | #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) | |
32 | #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type)) | |
33 | #define DELETE_ARRAY(array) uprv_free((void *) (array)) | |
34 | ||
35 | #define ICUID_STACK_BUFFER_SIZE 32 | |
36 | ||
37 | /* The layout of the Tzi value in the registry */ | |
38 | typedef struct | |
39 | { | |
40 | int32_t bias; | |
41 | int32_t standardBias; | |
42 | int32_t daylightBias; | |
43 | SYSTEMTIME standardDate; | |
44 | SYSTEMTIME daylightDate; | |
45 | } TZI; | |
46 | ||
47 | typedef struct | |
48 | { | |
49 | const char *icuid; | |
50 | const char *winid; | |
51 | } WindowsICUMap; | |
52 | ||
53 | typedef struct { | |
54 | const char* winid; | |
55 | const char* altwinid; | |
56 | } WindowsZoneRemap; | |
57 | ||
58 | /** | |
59 | * Various registry keys and key fragments. | |
60 | */ | |
61 | static const char CURRENT_ZONE_REGKEY[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\"; | |
62 | static const char STANDARD_NAME_REGKEY[] = "StandardName"; | |
63 | static const char STANDARD_TIME_REGKEY[] = " Standard Time"; | |
64 | static const char TZI_REGKEY[] = "TZI"; | |
65 | static const char STD_REGKEY[] = "Std"; | |
66 | ||
67 | /** | |
68 | * HKLM subkeys used to probe for the flavor of Windows. Note that we | |
69 | * specifically check for the "GMT" zone subkey; this is present on | |
70 | * NT, but on XP has become "GMT Standard Time". We need to | |
71 | * discriminate between these cases. | |
72 | */ | |
73 | static const char* const WIN_TYPE_PROBE_REGKEY[] = { | |
74 | /* WIN_9X_ME_TYPE */ | |
75 | "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones", | |
76 | ||
77 | /* WIN_NT_TYPE */ | |
78 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\GMT" | |
79 | ||
80 | /* otherwise: WIN_2K_XP_TYPE */ | |
81 | }; | |
82 | ||
83 | /** | |
84 | * The time zone root subkeys (under HKLM) for different flavors of | |
85 | * Windows. | |
86 | */ | |
87 | static const char* const TZ_REGKEY[] = { | |
88 | /* WIN_9X_ME_TYPE */ | |
89 | "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones\\", | |
90 | ||
91 | /* WIN_NT_TYPE | WIN_2K_XP_TYPE */ | |
92 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\" | |
93 | }; | |
94 | ||
95 | /** | |
96 | * Flavor of Windows, from our perspective. Not a real OS version, | |
97 | * but rather the flavor of the layout of the time zone information in | |
98 | * the registry. | |
99 | */ | |
100 | enum { | |
46f4442e A |
101 | WIN_9X_ME_TYPE = 1, |
102 | WIN_NT_TYPE = 2, | |
103 | WIN_2K_XP_TYPE = 3 | |
73c04bcf A |
104 | }; |
105 | ||
46f4442e | 106 | # if 0 |
73c04bcf | 107 | /* |
46f4442e A |
108 | * ZONE_MAP from supplementalData.txt |
109 | */ | |
110 | static const WindowsICUMap NEW_ZONE_MAP[] = { | |
111 | {"Africa/Cairo", "Egypt"}, | |
112 | {"Africa/Casablanca", "Greenwich"}, | |
113 | {"Africa/Johannesburg", "South Africa"}, | |
114 | {"Africa/Lagos", "W. Central Africa"}, | |
115 | {"Africa/Nairobi", "E. Africa"}, | |
116 | {"Africa/Windhoek", "Namibia"}, | |
117 | {"America/Anchorage", "Alaskan"}, | |
118 | {"America/Bogota", "SA Pacific"}, | |
119 | {"America/Buenos_Aires", "SA Eastern"}, | |
120 | {"America/Caracas", "SA Western"}, | |
121 | {"America/Chicago", "Central"}, | |
122 | {"America/Chihuahua", "Mountain Standard Time (Mexico)"}, | |
123 | {"America/Denver", "Mountain"}, | |
124 | {"America/Godthab", "Greenland"}, | |
125 | {"America/Guatemala", "Central America"}, | |
126 | {"America/Halifax", "Atlantic"}, | |
127 | {"America/Indianapolis", "US Eastern"}, | |
128 | {"America/Los_Angeles", "Pacific"}, | |
129 | {"America/Manaus", "Central Brazilian"}, | |
130 | {"America/Mexico_City", "Central Standard Time (Mexico)"}, | |
131 | {"America/Montevideo", "Montevideo"}, | |
132 | {"America/New_York", "Eastern"}, | |
133 | {"America/Noronha", "Mid-Atlantic"}, | |
134 | {"America/Phoenix", "US Mountain"}, | |
135 | {"America/Regina", "Canada Central"}, | |
136 | {"America/Santiago", "Pacific SA"}, | |
137 | {"America/Sao_Paulo", "E. South America"}, | |
138 | {"America/St_Johns", "Newfoundland"}, | |
139 | {"America/Tijuana", "Pacific Standard Time (Mexico)"}, | |
140 | {"Asia/Amman", "Jordan"}, | |
141 | {"Asia/Baghdad", "Arabic"}, | |
142 | {"Asia/Baku", "Azerbaijan"}, | |
143 | {"Asia/Bangkok", "SE Asia"}, | |
144 | {"Asia/Beirut", "Middle East"}, | |
145 | {"Asia/Calcutta", "India"}, | |
146 | {"Asia/Colombo", "Sri Lanka"}, | |
147 | {"Asia/Dhaka", "Central Asia"}, | |
148 | {"Asia/Jerusalem", "Israel"}, | |
149 | {"Asia/Kabul", "Afghanistan"}, | |
150 | {"Asia/Karachi", "West Asia"}, | |
151 | {"Asia/Katmandu", "Nepal"}, | |
152 | {"Asia/Krasnoyarsk", "North Asia"}, | |
153 | {"Asia/Muscat", "Arabian"}, | |
154 | {"Asia/Novosibirsk", "N. Central Asia"}, | |
155 | {"Asia/Rangoon", "Myanmar"}, | |
156 | {"Asia/Riyadh", "Arab"}, | |
157 | {"Asia/Seoul", "Korea"}, | |
158 | {"Asia/Shanghai", "China"}, | |
159 | {"Asia/Singapore", "Singapore"}, | |
160 | {"Asia/Taipei", "Taipei"}, | |
161 | {"Asia/Tbilisi", "Georgian"}, | |
162 | {"Asia/Tehran", "Iran"}, | |
163 | {"Asia/Tokyo", "Tokyo"}, | |
164 | {"Asia/Ulaanbaatar", "North Asia East"}, | |
165 | {"Asia/Vladivostok", "Vladivostok"}, | |
166 | {"Asia/Yakutsk", "Yakutsk"}, | |
167 | {"Asia/Yekaterinburg", "Ekaterinburg"}, | |
168 | {"Asia/Yerevan", "Caucasus"}, | |
169 | {"Atlantic/Azores", "Azores"}, | |
170 | {"Atlantic/Cape_Verde", "Cape Verde"}, | |
171 | {"Australia/Adelaide", "Cen. Australia"}, | |
172 | {"Australia/Brisbane", "E. Australia"}, | |
173 | {"Australia/Darwin", "AUS Central"}, | |
174 | {"Australia/Hobart", "Tasmania"}, | |
175 | {"Australia/Perth", "W. Australia"}, | |
176 | {"Australia/Sydney", "AUS Eastern"}, | |
177 | {"Europe/Berlin", "W. Europe"}, | |
178 | {"Europe/Helsinki", "FLE"}, | |
179 | {"Europe/Istanbul", "GTB"}, | |
180 | {"Europe/London", "GMT"}, | |
181 | {"Europe/Minsk", "E. Europe"}, | |
182 | {"Europe/Moscow", "Russian"}, | |
183 | {"Europe/Paris", "Romance"}, | |
184 | {"Europe/Prague", "Central Europe"}, | |
185 | {"Europe/Warsaw", "Central European"}, | |
186 | {"Pacific/Apia", "Samoa"}, | |
187 | {"Pacific/Auckland", "New Zealand"}, | |
188 | {"Pacific/Fiji", "Fiji"}, | |
189 | {"Pacific/Guadalcanal", "Central Pacific"}, | |
190 | {"Pacific/Guam", "West Pacific"}, | |
191 | {"Pacific/Honolulu", "Hawaiian"}, | |
192 | {"Pacific/Kwajalein", "Dateline"}, | |
193 | {"Pacific/Tongatapu", "Tonga"} | |
194 | }; | |
195 | #endif | |
196 | ||
197 | /* NOTE: Some Windows zone ids appear more than once. In such cases the | |
198 | * ICU zone id from the first one is the preferred match. | |
73c04bcf A |
199 | */ |
200 | static const WindowsICUMap ZONE_MAP[] = { | |
46f4442e A |
201 | /* S (GMT-12:00) International Date Line West */ |
202 | {"Etc/GMT+12", "Dateline"}, | |
203 | ||
204 | /* S (GMT-11:00) Midway Island, Samoa */ | |
205 | {"Pacific/Apia", "Samoa"}, | |
206 | {"Pacific/Midway", "Samoa"}, | |
207 | ||
208 | /* S (GMT-10:00) Hawaii */ | |
209 | {"Pacific/Honolulu", "Hawaiian"}, | |
210 | ||
211 | /* D (GMT-09:00) Alaska */ | |
212 | {"America/Anchorage", "Alaskan"}, | |
213 | {"America/Juneau", "Alaskan"}, | |
214 | {"America/Yakutat", "Alaskan"}, | |
215 | {"America/Nome", "Alaskan"}, | |
216 | ||
217 | /* D (GMT-08:00) Pacific Time (US & Canada) */ | |
218 | {"America/Los_Angeles", "Pacific"}, | |
219 | {"America/Dawson", "Pacific"}, | |
220 | {"America/Vancouver", "Pacific"}, | |
221 | {"America/Whitehorse", "Pacific"}, | |
222 | ||
223 | /* D (GMT-08:00) Tijuana, Baja California */ | |
224 | {"America/Tijuana", "Pacific Standard Time (Mexico)"}, | |
225 | ||
226 | /* D (GMT-07:00) Mountain Time (US & Canada) */ | |
227 | {"America/Denver", "Mountain"}, | |
228 | {"America/Boise", "Mountain"}, | |
229 | {"America/Cambridge_Bay", "Mountain"}, | |
230 | {"America/Edmonton", "Mountain"}, | |
231 | {"America/Inuvik", "Mountain"}, | |
232 | {"America/Shiprock", "Mountain"}, | |
233 | {"America/Yellowknife", "Mountain"}, | |
234 | ||
235 | /* S (GMT-07:00) Arizona */ | |
236 | {"America/Phoenix", "US Mountain"}, | |
237 | ||
238 | /* D (GMT-07:00) Chihuahua, La Paz, Mazatlan */ | |
239 | {"America/Chihuahua", "Mountain Standard Time (Mexico)"}, | |
240 | {"America/Mazatlan", "Mountain Standard Time (Mexico)"}, | |
241 | ||
242 | /* D (GMT-06:00) Central Time (US & Canada) */ | |
243 | {"America/Chicago", "Central"}, | |
244 | {"America/Indiana/Knox", "Central"}, | |
245 | {"America/Indiana/Tell_City", "Central"}, | |
246 | {"America/Menominee", "Central"}, | |
247 | {"America/North_Dakota/Center", "Central"}, | |
248 | {"America/North_Dakota/New_Salem", "Central"}, | |
249 | {"America/Rainy_River", "Central"}, | |
250 | {"America/Rankin_Inlet", "Central"}, | |
251 | {"America/Winnipeg", "Central"}, | |
252 | ||
253 | /* D (GMT-06:00) Guadalajara, Mexico City, Monterrey */ | |
254 | {"America/Mexico_City", "Central Standard Time (Mexico)"}, | |
255 | {"America/Monterrey", "Central Standard Time (Mexico)"}, | |
256 | ||
257 | /* S (GMT-06:00) Central America */ | |
258 | {"America/Guatemala", "Central America"}, | |
259 | {"America/Belize", "Central America"}, | |
260 | {"America/Costa_Rica", "Central America"}, | |
261 | {"America/El_Salvador", "Central America"}, | |
262 | {"America/Managua", "Central America"}, | |
263 | {"America/Tegucigalpa", "Central America"}, | |
264 | {"Pacific/Galapagos", "Central America"}, | |
265 | ||
266 | /* S (GMT-06:00) Saskatchewan */ | |
267 | {"America/Regina", "Canada Central"}, | |
268 | {"America/Swift_Current", "Canada Central"}, | |
269 | ||
270 | /* D (GMT-05:00) Eastern Time (US & Canada) */ | |
271 | {"America/New_York", "Eastern"}, | |
272 | {"America/Detroit", "Eastern"}, | |
273 | {"America/Grand_Turk", "Eastern"}, | |
274 | {"America/Indiana/Marengo", "Eastern"}, | |
275 | {"America/Indiana/Petersburg", "Eastern"}, | |
276 | {"America/Indiana/Vevay", "Eastern"}, | |
277 | {"America/Indiana/Vincennes", "Eastern"}, | |
278 | {"America/Indiana/Winamac", "Eastern"}, | |
279 | {"America/Indianapolis", "Eastern"}, | |
280 | {"America/Iqaluit", "Eastern"}, | |
281 | {"America/Kentucky/Monticello", "Eastern"}, | |
282 | {"America/Louisville", "Eastern"}, | |
283 | {"America/Montreal", "Eastern"}, | |
284 | {"America/Nassau", "Eastern"}, | |
285 | {"America/Nipigon", "Eastern"}, | |
286 | {"America/Pangnirtung", "Eastern"}, | |
287 | {"America/Thunder_Bay", "Eastern"}, | |
288 | {"America/Toronto", "Eastern"}, | |
289 | ||
290 | /* S (GMT-05:00) Bogota, Lima, Quito, Rio Branco */ | |
291 | {"America/Bogota", "SA Pacific"}, | |
292 | {"America/Lima", "SA Pacific"}, | |
293 | {"America/Guayaquil", "SA Pacific"}, | |
294 | {"America/Rio_Branco", "SA Pacific"}, | |
295 | ||
296 | /* S (GMT-05:00) Indiana (East) */ | |
297 | {"Etc/GMT+5", "US Eastern"}, | |
298 | ||
299 | /* S (GMT-04:30) Caracas */ | |
300 | {"America/Caracas", "Venezuela"}, | |
301 | ||
302 | /* D (GMT-04:00) Atlantic Time (Canada) */ | |
303 | {"America/Halifax", "Atlantic"}, | |
304 | {"America/Glace_Bay", "Atlantic"}, | |
305 | {"America/Moncton", "Atlantic"}, | |
306 | ||
307 | /* D (GMT-04:00) Santiago */ | |
308 | {"America/Santiago", "Pacific SA"}, | |
309 | ||
310 | /* D (GMT-04:00) Manaus */ /* MS bug - DST is not used */ | |
311 | {"America/Manaus", "Central Brazilian"}, | |
312 | ||
313 | /* S (GMT-04:00) La Paz */ | |
314 | {"America/La_Paz", "SA Western"}, | |
315 | ||
316 | /* D (GMT-03:30) Newfoundland */ | |
317 | {"America/St_Johns", "Newfoundland"}, | |
318 | ||
319 | /* D (GMT-03:00) Brasilia */ | |
320 | {"America/Sao_Paulo", "E. South America"}, | |
321 | ||
322 | /* D (GMT-03:00) Buenos Aires */ | |
323 | {"America/Buenos_Aires", "Argentina"}, | |
324 | ||
325 | /* D (GMT-03:00) Greenland */ | |
326 | {"America/Godthab", "Greenland"}, | |
327 | ||
328 | /* D (GMT-03:00) Montevideo */ | |
329 | {"America/Montevideo", "Montevideo"}, | |
330 | ||
331 | /* S (GMT-03:00) Georgetown */ /* MS bug - Georgetown uses GMT-04:00 */ | |
332 | {"Etc/GMT+3", "SA Eastern"}, | |
333 | ||
334 | /* D (GMT-02:00) Mid-Atlantic */ /* MS bug - There is no such zone using GMT-02:00 with DST */ | |
335 | {"America/South_Georgia", "Mid-Atlantic"}, | |
336 | {"America/Noronha", "Mid-Atlantic"}, | |
337 | ||
338 | /* D (GMT-01:00) Azores */ | |
339 | {"Atlantic/Azores", "Azores"}, | |
340 | ||
341 | /* S (GMT-01:00) Cape Verde Is. */ | |
342 | {"Atlantic/Cape_Verde", "Cape Verde"}, | |
343 | ||
344 | /* D (GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London */ | |
345 | {"Europe/London", "GMT"}, | |
346 | {"Europe/Dublin", "GMT"}, | |
347 | {"Europe/Lisbon", "GMT"}, | |
348 | ||
349 | /* S (GMT) Casablanca, Monrovia, Reykjavik */ | |
350 | {"Africa/Casablanca", "Greenwich"}, | |
351 | {"Africa/Monrovia", "Greenwich"}, | |
352 | {"Atlantic/Reykjavik", "Greenwich"}, | |
353 | ||
354 | /* D (GMT+01:00) Brussels, Copenhagen, Madrid, Paris */ | |
355 | {"Europe/Paris", "Romance"}, | |
356 | {"Europe/Brussels", "Romance"}, | |
357 | {"Europe/Copenhagen", "Romance"}, | |
358 | {"Europe/Madrid", "Romance"}, | |
359 | ||
360 | /* D (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna */ | |
361 | {"Europe/Berlin", "W. Europe"}, | |
362 | {"Europe/Amsterdam", "W. Europe"}, | |
363 | {"Europe/Zurich", "W. Europe"}, | |
364 | {"Europe/Rome", "W. Europe"}, | |
365 | {"Europe/Stockholm", "W. Europe"}, | |
366 | {"Europe/Vienna", "W. Europe"}, | |
367 | ||
368 | /* D (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague */ | |
369 | {"Europe/Budapest", "Central Europe"}, | |
370 | {"Europe/Belgrade", "Central Europe"}, | |
371 | {"Europe/Bratislava", "Central Europe"}, | |
372 | {"Europe/Ljubljana", "Central Europe"}, | |
373 | {"Europe/Prague", "Central Europe"}, | |
374 | ||
375 | /* D (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb */ | |
376 | {"Eurpoe/Warsaw", "Central European"}, | |
377 | {"Eurpoe/Sarajevo", "Central European"}, | |
378 | {"Eurpoe/Skopje", "Central European"}, | |
379 | {"Eurpoe/Zagreb", "Central European"}, | |
380 | ||
381 | /* S (GMT+01:00) West Central Africa */ | |
382 | {"Africa/Lagos", "W. Central Africa"}, | |
383 | {"Africa/Luanda", "W. Central Africa"}, | |
384 | {"Africa/Porto-Novo", "W. Central Africa"}, | |
385 | {"Africa/Douala", "W. Central Africa"}, | |
386 | {"Africa/Bangui", "W. Central Africa"}, | |
387 | {"Africa/Ndjamena", "W. Central Africa"}, | |
388 | {"Africa/Kinshasa", "W. Central Africa"}, | |
389 | {"Africa/Brazzaville", "W. Central Africa"}, | |
390 | {"Africa/Malabo", "W. Central Africa"}, | |
391 | {"Africa/Libreville", "W. Central Africa"}, | |
392 | {"Africa/Niamey", "W. Central Africa"}, | |
393 | ||
394 | /* D (GMT+02:00) Athens, Bucharest, Istanbul */ | |
395 | {"Europe/Istanbul", "GTB"}, | |
396 | {"Europe/Athens", "GTB"}, | |
397 | {"Europe/Bucharest", "GTB"}, | |
398 | ||
399 | /* D (GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius */ | |
400 | {"Europe/Kiev", "FLE"}, | |
401 | {"Europe/Helsinki", "FLE"}, | |
402 | {"Europe/Riga", "FLE"}, | |
403 | {"Europe/Sofia", "FLE"}, | |
404 | {"Europe/Tallinn", "FLE"}, | |
405 | {"Europe/Vilnius", "FLE"}, | |
406 | ||
407 | /* D (GMT+02:00) Jerusalem */ | |
408 | {"Asia/Jerusalem", "Israel"}, | |
409 | ||
410 | /* D (GMT+02:00) Minsk */ | |
411 | {"Europe/Minsk", "E. Europe"}, | |
412 | ||
413 | /* D (GMT+02:00) Cairo */ | |
414 | {"Africa/Cairo", "Egypt"}, | |
415 | ||
416 | /* D (GMT+02:00) Amman */ | |
417 | {"Asia/Amman", "Jordan"}, | |
418 | ||
419 | /* D (GMT+02:00) Beirut */ | |
420 | {"Asia/Beirut", "Middle East"}, | |
421 | ||
422 | /* D (GMT+02:00) Windhoek */ | |
423 | {"Africa/Windhoek", "Namibia"}, | |
424 | ||
425 | /* S (GMT+02:00) Harare, Pretoria */ | |
426 | {"Africa/Johannesburg", "South Africa"}, | |
427 | {"Africa/Harare", "South Africa"}, | |
73c04bcf | 428 | |
46f4442e A |
429 | /* D (GMT+03:00) Moscow, St. Petersburg, Volgograd */ |
430 | {"Europe/Moscow", "Russian"}, | |
431 | {"Europe/Volgograd", "Russian"}, | |
73c04bcf | 432 | |
46f4442e A |
433 | /* D (GMT+03:00) Baghdad */ |
434 | {"Asia/Baghdad", "Arabic"}, | |
73c04bcf | 435 | |
46f4442e A |
436 | /* S (GMT+03:00) Kuwait, Riyadh */ |
437 | {"Asia/Riyadh", "Arab"}, | |
438 | {"Asia/Kuwait", "Arab"}, | |
73c04bcf | 439 | |
46f4442e A |
440 | /* S (GMT+03:00) Nairobi */ |
441 | {"Africa/Nairobi", "E. Africa"}, | |
73c04bcf | 442 | |
46f4442e A |
443 | /* S (GMT+03:00) Tbilisi */ /* MS bug - Tbilisi uses GMT+04:00 */ |
444 | {"Etc/GMT-3", "Georgian"}, | |
73c04bcf | 445 | |
46f4442e A |
446 | /* D (GMT+03:30) Tehran */ |
447 | {"Asia/Tehran", "Iran"}, | |
73c04bcf | 448 | |
46f4442e A |
449 | /* D (GMT+04:00) Yerevan */ |
450 | {"Asia/Yerevan", "Armenian"}, | |
73c04bcf | 451 | |
46f4442e A |
452 | /* D (GMT+04:00) Baku */ |
453 | {"Asia/Baku", "Azerbaijan"}, | |
73c04bcf | 454 | |
46f4442e A |
455 | /* S (GMT+04:00) Abu Dhabi, Muscat */ |
456 | {"Asia/Dubai", "Arabian"}, | |
457 | {"Asia/Muscat", "Arabian"}, | |
73c04bcf | 458 | |
46f4442e A |
459 | /* S (GMT+04:00) Caucasus Standard Time */ |
460 | {"Asia/Tbilisi", "Caucasus"}, | |
73c04bcf | 461 | |
46f4442e A |
462 | /* S (GMT+04:30) Kabul */ |
463 | {"Asia/Kabul", "Afghanistan"}, | |
73c04bcf | 464 | |
46f4442e A |
465 | /* D (GMT+05:00) Ekaterinburg */ |
466 | {"Asia/Yekaterinburg", "Ekaterinburg"}, | |
73c04bcf | 467 | |
46f4442e A |
468 | /* S (GMT+05:00) Islamabad, Karachi, Tashkent */ |
469 | {"Asia/Karachi", "West Asia"}, | |
470 | {"Asia/Tashkent", "West Asia"}, | |
73c04bcf | 471 | |
46f4442e A |
472 | /* S (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi */ |
473 | {"Asia/Calcutta", "India"}, | |
73c04bcf | 474 | |
46f4442e A |
475 | /* S (GMT+05:30) Sri Jayawardenepura */ |
476 | {"Asia/Colombo", "Sri Lanka"}, | |
73c04bcf | 477 | |
46f4442e A |
478 | /* S (GMT+05:45) Kathmandu */ |
479 | {"Asia/Katmandu", "Nepal"}, | |
73c04bcf | 480 | |
46f4442e A |
481 | /* D (GMT+06:00) Almaty, Novosibirsk */ /* No DST in Almaty */ |
482 | {"Asia/Novosibirsk", "N. Central Asia"}, | |
73c04bcf | 483 | |
46f4442e A |
484 | /* S (GMT+06:00) Astana, Dhaka */ |
485 | {"Asia/Dhaka", "Central Asia"}, | |
486 | {"Asia/Almaty", "Central Asia"}, | |
487 | {"Asia/Qyzylorda", "Central Asia"}, | |
73c04bcf | 488 | |
46f4442e A |
489 | /* S (GMT+06:30) Yangon (Rangoon) */ |
490 | {"Asia/Rangoon", "Myanmar"}, | |
73c04bcf | 491 | |
46f4442e A |
492 | /* D (GMT+07:00) Krasnoyarsk */ |
493 | {"Asia/Krasnoyarsk", "North Asia"}, | |
73c04bcf | 494 | |
46f4442e A |
495 | /* S (GMT+07:00) Bangkok, Hanoi, Jakarta */ |
496 | {"Asia/Bangkok", "SE Asia"}, | |
497 | {"Asia/Saigon", "SE Asia"}, | |
498 | {"Asia/Jakarta", "SE Asia"}, | |
73c04bcf | 499 | |
46f4442e A |
500 | /* D (GMT+08:00) Irkutsk, Ulaan Bataar */ /* Ulaan Bataar does not use DST */ |
501 | {"Asia/Irkutsk", "North Asia East"}, | |
73c04bcf | 502 | |
46f4442e A |
503 | /* D (GMT+08:00) Perth */ |
504 | {"Australia/Perth", "W. Australia"}, | |
73c04bcf | 505 | |
46f4442e A |
506 | /* S (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi */ |
507 | {"Asia/Shanghai", "China"}, | |
508 | {"Asia/Chongqing", "China"}, | |
509 | {"Asia/Hong_Kong", "China"}, | |
510 | {"Asia/Urumqi", "China"}, | |
73c04bcf | 511 | |
46f4442e A |
512 | /* S (GMT+08:00) Taipei */ |
513 | {"Asia/Taipei", "Taipei"}, | |
73c04bcf | 514 | |
46f4442e A |
515 | /* S (GMT+08:00) Kuala Lumpur, Singapore */ |
516 | {"Asia/Singapore", "Singapore"}, | |
517 | {"Asia/Kuala_Lumpur", "Singapore"}, | |
73c04bcf | 518 | |
46f4442e A |
519 | /* D (GMT+09:00) Yakutsk */ |
520 | {"Asia/Yakutsk", "Yakutsk"}, | |
73c04bcf | 521 | |
46f4442e A |
522 | /* S (GMT+09:00) Osaka, Sapporo, Tokyo */ |
523 | {"Asia/Tokyo", "Tokyo"}, | |
73c04bcf | 524 | |
46f4442e A |
525 | /* S (GMT+09:00) Seoul */ |
526 | {"Asia/Seoul", "Korea"}, | |
73c04bcf | 527 | |
46f4442e A |
528 | /* D (GMT+09:30) Adelaide */ |
529 | {"Australia/Adelaide", "Cen. Australia"}, | |
73c04bcf | 530 | |
46f4442e A |
531 | /* S (GMT+09:30) Darwin */ |
532 | {"Australia/Darwin", "AUS Central"}, | |
73c04bcf | 533 | |
46f4442e A |
534 | /* D (GMT+10:00) Canberra, Melbourne, Sydney */ |
535 | {"Australia/Sydney", "AUS Eastern"}, | |
536 | {"Australia/Melbourne", "AUS Eastern"}, | |
537 | ||
538 | /* D (GMT+10:00) Hobart */ | |
539 | {"Australia/Hobart", "Tasmania"}, | |
540 | ||
541 | /* D (GMT+10:00) Vladivostok */ | |
542 | {"Asia/Vladivostok", "Vladivostok"}, | |
543 | ||
544 | /* S (GMT+10:00) Brisbane */ | |
545 | {"Australia/Brisbane", "E. Australia"}, | |
546 | ||
547 | /* S (GMT+10:00) Guam, Port Moresby */ | |
548 | {"Pacific/Port_Moresby", "West Pacific"}, | |
549 | {"Pacific/Guam", "West Pacific"}, | |
550 | ||
551 | /* S (GMT+11:00) Magadan, Solomon Is., New Caledonia */ /* Magadan uses DST */ | |
552 | {"Pacific/Guadalcanal", "Central Pacific"}, | |
553 | {"Pacific/Noumea", "Central Pacific"}, | |
554 | ||
555 | /* D (GMT+12:00) Auckland, Wellington */ | |
556 | {"Pacific/Auckland", "New Zealand"}, | |
557 | ||
558 | /* S (GMT+12:00) Fiji, Kamchatka, Marshall Is. */ | |
559 | {"Pacific/Fiji", "Fiji"}, | |
560 | {"Pacific/Majuro", "Fiji"}, | |
561 | {"Pacific/Kwajalein", "Fiji"}, | |
562 | ||
563 | /* S (GMT+13:00) Nuku'alofa */ | |
564 | {"Pacific/Tongatapu", "Tonga"}, | |
565 | ||
566 | NULL, NULL | |
73c04bcf A |
567 | }; |
568 | ||
569 | /** | |
570 | * If a lookup fails, we attempt to remap certain Windows ids to | |
571 | * alternate Windows ids. If the alternate listed here begins with | |
572 | * '-', we use it as is (without the '-'). If it begins with '+', we | |
573 | * append a " Standard Time" if appropriate. | |
574 | */ | |
575 | static const WindowsZoneRemap ZONE_REMAP[] = { | |
46f4442e A |
576 | "Central European", "-Warsaw", |
577 | "Central Europe", "-Prague Bratislava", | |
578 | "China", "-Beijing", | |
579 | ||
580 | "Greenwich", "+GMT", | |
581 | "GTB", "+GFT", | |
582 | "Arab", "+Saudi Arabia", | |
583 | "SE Asia", "+Bangkok", | |
584 | "AUS Eastern", "+Sydney", | |
585 | "Mountain Standard Time (Mexico)", "-Mexico Standard Time 2", | |
586 | "Central Standard Time (Mexico)", "+Mexico", | |
73c04bcf A |
587 | NULL, NULL, |
588 | }; | |
589 | ||
46f4442e | 590 | static int32_t gWinType = 0; |
73c04bcf A |
591 | |
592 | static int32_t detectWindowsType() | |
593 | { | |
594 | int32_t winType; | |
595 | LONG result; | |
596 | HKEY hkey; | |
597 | ||
598 | /* Detect the version of windows by trying to open a sequence of | |
599 | probe keys. We don't use the OS version API because what we | |
600 | really want to know is how the registry is laid out. | |
601 | Specifically, is it 9x/Me or not, and is it "GMT" or "GMT | |
602 | Standard Time". */ | |
46f4442e A |
603 | for (winType = 0; winType < 2; winType++) { |
604 | result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, | |
73c04bcf A |
605 | WIN_TYPE_PROBE_REGKEY[winType], |
606 | 0, | |
607 | KEY_QUERY_VALUE, | |
608 | &hkey); | |
609 | RegCloseKey(hkey); | |
610 | ||
611 | if (result == ERROR_SUCCESS) { | |
612 | break; | |
613 | } | |
614 | } | |
615 | ||
46f4442e | 616 | return winType+1; // +1 to bring it inline with the enum |
73c04bcf A |
617 | } |
618 | ||
619 | /* | |
620 | * TODO: Binary search sorted ZONE_MAP... | |
621 | * (u_detectWindowsTimeZone() needs them sorted by offset...) | |
622 | */ | |
623 | static const char *findWindowsZoneID(const UChar *icuid, int32_t length) | |
624 | { | |
625 | char stackBuffer[ICUID_STACK_BUFFER_SIZE]; | |
626 | char *buffer = stackBuffer; | |
627 | const char *result = NULL; | |
628 | int i; | |
629 | ||
630 | /* | |
631 | * NOTE: >= because length doesn't include | |
632 | * trailing null. | |
633 | */ | |
634 | if (length >= ICUID_STACK_BUFFER_SIZE) { | |
635 | buffer = NEW_ARRAY(char, length + 1); | |
636 | } | |
637 | ||
638 | u_UCharsToChars(icuid, buffer, length); | |
639 | buffer[length] = '\0'; | |
640 | ||
641 | for (i = 0; ZONE_MAP[i].icuid != NULL; i += 1) { | |
642 | if (uprv_strcmp(buffer, ZONE_MAP[i].icuid) == 0) { | |
643 | result = ZONE_MAP[i].winid; | |
644 | break; | |
645 | } | |
646 | } | |
647 | ||
648 | if (buffer != stackBuffer) { | |
649 | DELETE_ARRAY(buffer); | |
650 | } | |
651 | ||
652 | return result; | |
653 | } | |
654 | ||
655 | static LONG openTZRegKey(HKEY *hkey, const char *winid) | |
656 | { | |
657 | char subKeyName[96]; /* TODO: why 96?? */ | |
658 | char *name; | |
659 | LONG result; | |
660 | ||
46f4442e A |
661 | /* This isn't thread safe, but it's good enough because the result should be constant per system. */ |
662 | if (gWinType <= 0) { | |
663 | gWinType = detectWindowsType(); | |
73c04bcf A |
664 | } |
665 | ||
46f4442e | 666 | uprv_strcpy(subKeyName, TZ_REGKEY[(gWinType != WIN_9X_ME_TYPE)]); |
73c04bcf A |
667 | name = &subKeyName[strlen(subKeyName)]; |
668 | uprv_strcat(subKeyName, winid); | |
669 | ||
46f4442e | 670 | if (gWinType != WIN_9X_ME_TYPE && |
73c04bcf | 671 | (winid[strlen(winid) - 1] != '2') && |
46f4442e A |
672 | (winid[strlen(winid) - 1] != ')') && |
673 | !(gWinType == WIN_NT_TYPE && strcmp(winid, "GMT") == 0)) | |
674 | { | |
73c04bcf A |
675 | uprv_strcat(subKeyName, STANDARD_TIME_REGKEY); |
676 | } | |
677 | ||
46f4442e | 678 | result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, |
73c04bcf A |
679 | subKeyName, |
680 | 0, | |
681 | KEY_QUERY_VALUE, | |
682 | hkey); | |
683 | ||
684 | if (result != ERROR_SUCCESS) { | |
685 | int i; | |
686 | ||
687 | /* If the primary lookup fails, try to remap the Windows zone | |
688 | ID, according to the remapping table. */ | |
689 | for (i=0; ZONE_REMAP[i].winid; i++) { | |
690 | if (uprv_strcmp(winid, ZONE_REMAP[i].winid) == 0) { | |
691 | uprv_strcpy(name, ZONE_REMAP[i].altwinid + 1); | |
46f4442e A |
692 | if (*(ZONE_REMAP[i].altwinid) == '+' && gWinType != WIN_9X_ME_TYPE) { |
693 | uprv_strcat(subKeyName, STANDARD_TIME_REGKEY); | |
73c04bcf | 694 | } |
46f4442e | 695 | return RegOpenKeyExA(HKEY_LOCAL_MACHINE, |
73c04bcf A |
696 | subKeyName, |
697 | 0, | |
698 | KEY_QUERY_VALUE, | |
699 | hkey); | |
700 | } | |
701 | } | |
702 | } | |
703 | ||
704 | return result; | |
705 | } | |
706 | ||
707 | static LONG getTZI(const char *winid, TZI *tzi) | |
708 | { | |
709 | DWORD cbData = sizeof(TZI); | |
710 | LONG result; | |
711 | HKEY hkey; | |
712 | ||
713 | result = openTZRegKey(&hkey, winid); | |
714 | ||
715 | if (result == ERROR_SUCCESS) { | |
46f4442e | 716 | result = RegQueryValueExA(hkey, |
73c04bcf A |
717 | TZI_REGKEY, |
718 | NULL, | |
719 | NULL, | |
720 | (LPBYTE)tzi, | |
721 | &cbData); | |
722 | ||
723 | } | |
724 | ||
725 | RegCloseKey(hkey); | |
726 | ||
727 | return result; | |
728 | } | |
729 | ||
730 | U_CAPI UBool U_EXPORT2 | |
731 | uprv_getWindowsTimeZoneInfo(TIME_ZONE_INFORMATION *zoneInfo, const UChar *icuid, int32_t length) | |
732 | { | |
733 | const char *winid; | |
734 | TZI tzi; | |
735 | LONG result; | |
46f4442e | 736 | |
73c04bcf A |
737 | winid = findWindowsZoneID(icuid, length); |
738 | ||
739 | if (winid != NULL) { | |
740 | result = getTZI(winid, &tzi); | |
741 | ||
742 | if (result == ERROR_SUCCESS) { | |
743 | zoneInfo->Bias = tzi.bias; | |
744 | zoneInfo->DaylightBias = tzi.daylightBias; | |
745 | zoneInfo->StandardBias = tzi.standardBias; | |
746 | zoneInfo->DaylightDate = tzi.daylightDate; | |
747 | zoneInfo->StandardDate = tzi.standardDate; | |
748 | ||
749 | return TRUE; | |
750 | } | |
751 | } | |
752 | ||
753 | return FALSE; | |
754 | } | |
755 | ||
756 | /* | |
757 | This code attempts to detect the Windows time zone, as set in the | |
758 | Windows Date and Time control panel. It attempts to work on | |
759 | multiple flavors of Windows (9x, Me, NT, 2000, XP) and on localized | |
760 | installs. It works by directly interrogating the registry and | |
761 | comparing the data there with the data returned by the | |
762 | GetTimeZoneInformation API, along with some other strategies. The | |
763 | registry contains time zone data under one of two keys (depending on | |
764 | the flavor of Windows): | |
765 | ||
766 | HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones\ | |
767 | HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\ | |
768 | ||
769 | Under this key are several subkeys, one for each time zone. These | |
770 | subkeys are named "Pacific" on Win9x/Me and "Pacific Standard Time" | |
771 | on WinNT/2k/XP. There are some other wrinkles; see the code for | |
772 | details. The subkey name is NOT LOCALIZED, allowing us to support | |
773 | localized installs. | |
774 | ||
775 | Under the subkey are data values. We care about: | |
776 | ||
777 | Std Standard time display name, localized | |
778 | TZI Binary block of data | |
779 | ||
780 | The TZI data is of particular interest. It contains the offset, two | |
781 | more offsets for standard and daylight time, and the start and end | |
782 | rules. This is the same data returned by the GetTimeZoneInformation | |
783 | API. The API may modify the data on the way out, so we have to be | |
784 | careful, but essentially we do a binary comparison against the TZI | |
785 | blocks of various registry keys. When we find a match, we know what | |
786 | time zone Windows is set to. Since the registry key is not | |
787 | localized, we can then translate the key through a simple table | |
788 | lookup into the corresponding ICU time zone. | |
789 | ||
790 | This strategy doesn't always work because there are zones which | |
791 | share an offset and rules, so more than one TZI block will match. | |
792 | For example, both Tokyo and Seoul are at GMT+9 with no DST rules; | |
793 | their TZI blocks are identical. For these cases, we fall back to a | |
794 | name lookup. We attempt to match the display name as stored in the | |
795 | registry for the current zone to the display name stored in the | |
796 | registry for various Windows zones. By comparing the registry data | |
797 | directly we avoid conversion complications. | |
798 | ||
799 | Author: Alan Liu | |
800 | Since: ICU 2.6 | |
801 | Based on original code by Carl Brown <cbrown@xnetinc.com> | |
802 | */ | |
803 | ||
804 | /** | |
805 | * Main Windows time zone detection function. Returns the Windows | |
806 | * time zone, translated to an ICU time zone, or NULL upon failure. | |
807 | */ | |
46f4442e | 808 | U_CFUNC const char* U_EXPORT2 |
73c04bcf A |
809 | uprv_detectWindowsTimeZone() { |
810 | LONG result; | |
811 | HKEY hkey; | |
812 | TZI tziKey; | |
813 | TZI tziReg; | |
814 | TIME_ZONE_INFORMATION apiTZI; | |
815 | int firstMatch, lastMatch; | |
816 | int j; | |
817 | ||
818 | /* Obtain TIME_ZONE_INFORMATION from the API, and then convert it | |
819 | to TZI. We could also interrogate the registry directly; we do | |
820 | this below if needed. */ | |
821 | uprv_memset(&apiTZI, 0, sizeof(apiTZI)); | |
822 | uprv_memset(&tziKey, 0, sizeof(tziKey)); | |
823 | uprv_memset(&tziReg, 0, sizeof(tziReg)); | |
824 | GetTimeZoneInformation(&apiTZI); | |
825 | tziKey.bias = apiTZI.Bias; | |
826 | uprv_memcpy((char *)&tziKey.standardDate, (char*)&apiTZI.StandardDate, | |
827 | sizeof(apiTZI.StandardDate)); | |
828 | uprv_memcpy((char *)&tziKey.daylightDate, (char*)&apiTZI.DaylightDate, | |
829 | sizeof(apiTZI.DaylightDate)); | |
830 | ||
831 | /* For each zone that can be identified by Offset+Rules, see if we | |
832 | have a match. Continue scanning after finding a match, | |
833 | recording the index of the first and the last match. We have | |
834 | to do this because some zones are not unique under | |
835 | Offset+Rules. */ | |
836 | firstMatch = -1; | |
837 | lastMatch = -1; | |
838 | for (j=0; ZONE_MAP[j].icuid; j++) { | |
839 | result = getTZI(ZONE_MAP[j].winid, &tziReg); | |
840 | ||
841 | if (result == ERROR_SUCCESS) { | |
842 | /* Assume that offsets are grouped together, and bail out | |
843 | when we've scanned everything with a matching | |
844 | offset. */ | |
845 | if (firstMatch >= 0 && tziKey.bias != tziReg.bias) { | |
846 | break; | |
847 | } | |
848 | ||
849 | /* Windows alters the DaylightBias in some situations. | |
850 | Using the bias and the rules suffices, so overwrite | |
851 | these unreliable fields. */ | |
852 | tziKey.standardBias = tziReg.standardBias; | |
853 | tziKey.daylightBias = tziReg.daylightBias; | |
854 | ||
855 | if (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) == 0) { | |
856 | if (firstMatch < 0) { | |
857 | firstMatch = j; | |
858 | } | |
859 | ||
860 | lastMatch = j; | |
861 | } | |
862 | } | |
863 | } | |
864 | ||
865 | /* This should never happen; if it does it means our table doesn't | |
866 | match Windows AT ALL, perhaps because this is post-XP? */ | |
867 | if (firstMatch < 0) { | |
868 | return NULL; | |
869 | } | |
46f4442e | 870 | |
73c04bcf A |
871 | if (firstMatch != lastMatch) { |
872 | char stdName[32]; | |
873 | DWORD stdNameSize; | |
874 | char stdRegName[64]; | |
875 | DWORD stdRegNameSize; | |
876 | ||
877 | /* Offset+Rules lookup yielded >= 2 matches. Try to match the | |
878 | localized display name. Get the name from the registry | |
879 | (not the API). This avoids conversion issues. Use the | |
880 | standard name, since Windows modifies the daylight name to | |
881 | match the standard name if there is no DST. */ | |
46f4442e | 882 | if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, |
73c04bcf A |
883 | CURRENT_ZONE_REGKEY, |
884 | 0, | |
885 | KEY_QUERY_VALUE, | |
886 | &hkey) == ERROR_SUCCESS) | |
887 | { | |
888 | stdNameSize = sizeof(stdName); | |
46f4442e A |
889 | result = RegQueryValueExA(hkey, |
890 | STANDARD_NAME_REGKEY, | |
73c04bcf A |
891 | NULL, |
892 | NULL, | |
893 | (LPBYTE)stdName, | |
894 | &stdNameSize); | |
895 | RegCloseKey(hkey); | |
896 | ||
897 | /* | |
898 | * Scan through the Windows time zone data in the registry | |
899 | * again (just the range of zones with matching TZIs) and | |
900 | * look for a standard display name match. | |
901 | */ | |
902 | for (j = firstMatch; j <= lastMatch; j += 1) { | |
903 | stdRegNameSize = sizeof(stdRegName); | |
904 | result = openTZRegKey(&hkey, ZONE_MAP[j].winid); | |
905 | ||
906 | if (result == ERROR_SUCCESS) { | |
46f4442e A |
907 | result = RegQueryValueExA(hkey, |
908 | STD_REGKEY, | |
73c04bcf A |
909 | NULL, |
910 | NULL, | |
911 | (LPBYTE)stdRegName, | |
912 | &stdRegNameSize); | |
913 | } | |
914 | ||
915 | RegCloseKey(hkey); | |
916 | ||
917 | if (result == ERROR_SUCCESS && | |
918 | stdRegNameSize == stdNameSize && | |
919 | uprv_memcmp(stdName, stdRegName, stdNameSize) == 0) | |
920 | { | |
921 | firstMatch = j; /* record the match */ | |
922 | break; | |
923 | } | |
924 | } | |
925 | } else { | |
926 | RegCloseKey(hkey); /* should never get here */ | |
927 | } | |
928 | } | |
929 | ||
930 | return ZONE_MAP[firstMatch].icuid; | |
931 | } | |
932 | ||
933 | #endif /* #ifdef U_WINDOWS */ |