]> git.saurik.com Git - apple/icu.git/blame - icuSources/common/wintz.c
ICU-400.42.tar.gz
[apple/icu.git] / icuSources / common / wintz.c
CommitLineData
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 */
38typedef struct
39{
40 int32_t bias;
41 int32_t standardBias;
42 int32_t daylightBias;
43 SYSTEMTIME standardDate;
44 SYSTEMTIME daylightDate;
45} TZI;
46
47typedef struct
48{
49 const char *icuid;
50 const char *winid;
51} WindowsICUMap;
52
53typedef struct {
54 const char* winid;
55 const char* altwinid;
56} WindowsZoneRemap;
57
58/**
59 * Various registry keys and key fragments.
60 */
61static const char CURRENT_ZONE_REGKEY[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\";
62static const char STANDARD_NAME_REGKEY[] = "StandardName";
63static const char STANDARD_TIME_REGKEY[] = " Standard Time";
64static const char TZI_REGKEY[] = "TZI";
65static 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 */
73static 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 */
87static 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 */
100enum {
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 */
110static 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 */
200static 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 */
575static 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 590static int32_t gWinType = 0;
73c04bcf
A
591
592static 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 */
623static 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
655static 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
707static 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
730U_CAPI UBool U_EXPORT2
731uprv_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 808U_CFUNC const char* U_EXPORT2
73c04bcf
A
809uprv_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 */