2 ********************************************************************************
3 * Copyright (C) 2005-2007, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 ********************************************************************************
9 ********************************************************************************
12 #include "unicode/utypes.h"
21 #include "unicode/ustring.h"
23 # define WIN32_LEAN_AND_MEAN
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))
35 #define ICUID_STACK_BUFFER_SIZE 32
37 /* The layout of the Tzi value in the registry */
43 SYSTEMTIME standardDate
;
44 SYSTEMTIME daylightDate
;
59 * Various registry keys and key fragments.
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";
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.
73 static const char* const WIN_TYPE_PROBE_REGKEY
[] = {
75 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones",
78 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\GMT"
80 /* otherwise: WIN_2K_XP_TYPE */
84 * The time zone root subkeys (under HKLM) for different flavors of
87 static const char* const TZ_REGKEY
[] = {
89 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones\\",
91 /* WIN_NT_TYPE | WIN_2K_XP_TYPE */
92 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\"
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
108 * ZONE_MAP from supplementalData.txt
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"}
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.
200 static const WindowsICUMap ZONE_MAP
[] = {
201 {"Pacific/Kwajalein", "Dateline"}, /* S (GMT-12:00) International Date Line West */
202 {"Etc/GMT+12", "Dateline"}, /* S (GMT-12:00) International Date Line West */
204 {"Pacific/Apia", "Samoa"}, /* S (GMT-11:00) Midway Island, Samoa */
206 {"Pacific/Honolulu", "Hawaiian"}, /* S (GMT-10:00) Hawaii */
208 {"America/Anchorage", "Alaskan"}, /* D (GMT-09:00) Alaska */
210 {"America/Los_Angeles", "Pacific"}, /* D (GMT-08:00) Pacific Time (US & Canada) */
211 {"America/Tijuana", "Pacific Standard Time (Mexico)"}, /* S (GMT-08:00) Tijuana, Baja California */
213 {"America/Phoenix", "US Mountain"}, /* S (GMT-07:00) Arizona */
214 {"America/Denver", "Mountain"}, /* D (GMT-07:00) Mountain Time (US & Canada) */
215 {"America/Chihuahua", "Mountain Standard Time (Mexico)"}, /* D (GMT-07:00) Chihuahua, La Paz, Mazatlan */
217 {"America/Managua", "Central America"}, /* S (GMT-06:00) Central America */ /* America/Guatemala? */
218 {"America/Regina", "Canada Central"}, /* S (GMT-06:00) Saskatchewan */
219 {"America/Mexico_City", "Central Standard Time (Mexico)"}, /* D (GMT-06:00) Guadalajara, Mexico City, Monterrey */
220 {"America/Chicago", "Central"}, /* D (GMT-06:00) Central Time (US & Canada) */
222 {"America/Indianapolis", "US Eastern"}, /* S (GMT-05:00) Indiana (East) */
223 {"America/Bogota", "SA Pacific"}, /* S (GMT-05:00) Bogota, Lima, Quito */
224 {"America/New_York", "Eastern"}, /* D (GMT-05:00) Eastern Time (US & Canada) */
226 {"America/Caracas", "SA Western"}, /* S (GMT-04:00) Caracas, La Paz */
227 {"America/Santiago", "Pacific SA"}, /* D (GMT-04:00) Santiago */
228 {"America/Halifax", "Atlantic"}, /* D (GMT-04:00) Atlantic Time (Canada) */
229 {"America/Manaus", "Central Brazilian"}, /* D (GMT-04:00 Manaus */
231 {"America/St_Johns", "Newfoundland"}, /* D (GMT-03:30) Newfoundland */
233 {"America/Buenos_Aires", "SA Eastern"}, /* S (GMT-03:00) Buenos Aires, Georgetown */
234 {"America/Godthab", "Greenland"}, /* D (GMT-03:00) Greenland */
235 {"America/Sao_Paulo", "E. South America"}, /* D (GMT-03:00) Brasilia */
236 {"America/Montevideo", "Montevideo"}, /* S (GMT-03:00) Montevideo */
238 {"America/Noronha", "Mid-Atlantic"}, /* D (GMT-02:00) Mid-Atlantic */
240 {"Atlantic/Cape_Verde", "Cape Verde"}, /* S (GMT-01:00) Cape Verde Is. */
241 {"Atlantic/Azores", "Azores"}, /* D (GMT-01:00) Azores */
243 {"Africa/Casablanca", "Greenwich"}, /* S (GMT) Casablanca, Monrovia */
244 {"Europe/London", "GMT"}, /* D (GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London */
246 {"Africa/Lagos", "W. Central Africa"}, /* S (GMT+01:00) West Central Africa */
247 {"Europe/Berlin", "W. Europe"}, /* D (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna */
248 {"Europe/Paris", "Romance"}, /* D (GMT+01:00) Brussels, Copenhagen, Madrid, Paris */
249 {"Eurpoe/Warsaw", "Central European"}, /* D (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb */
250 {"Europe/Sarajevo", "Central European"}, /* D (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb */
251 {"Europe/Prague", "Central Europe"}, /* D (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague */
252 {"Europe/Belgrade", "Central Europe"}, /* D (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague */
254 {"Africa/Johannesburg", "South Africa"}, /* S (GMT+02:00) Harare, Pretoria */
255 {"Asia/Jerusalem", "Israel"}, /* S (GMT+02:00) Jerusalem */
256 {"Europe/Istanbul", "GTB"}, /* D (GMT+02:00) Athens, Istanbul, Minsk */
257 {"Europe/Helsinki", "FLE"}, /* D (GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius */
258 {"Africa/Cairo", "Egypt"}, /* D (GMT+02:00) Cairo */
259 {"Europe/Minsk", "E. Europe"}, /* D (GMT+02:00) Bucharest */
260 {"Europe/Bucharest", "E. Europe"}, /* D (GMT+02:00) Bucharest */
261 {"Africa/Windhoek", "Namibia"}, /* S (GMT+02:00) Windhoek */
262 {"Asia/Amman", "Jordan"}, /* S (GMT+02:00) Aman */
263 {"Asia/Beirut", "Middle East"}, /* S (GMT+02:00) Beirut */
265 {"Africa/Nairobi", "E. Africa"}, /* S (GMT+03:00) Nairobi */
266 {"Asia/Riyadh", "Arab"}, /* S (GMT+03:00) Kuwait, Riyadh */
267 {"Europe/Moscow", "Russian"}, /* D (GMT+03:00) Moscow, St. Petersburg, Volgograd */
268 {"Asia/Baghdad", "Arabic"}, /* D (GMT+03:00) Baghdad */
270 {"Asia/Tehran", "Iran"}, /* D (GMT+03:30) Tehran */
272 {"Asia/Muscat", "Arabian"}, /* S (GMT+04:00) Abu Dhabi, Muscat */
273 {"Asia/Tbilisi", "Georgian"}, /* D (GMT+04:00) Tbilisi */
274 {"Asia/Baku", "Azerbaijan"}, /* S (GMT+04:00) Baku */
275 {"Asia/Yerevan", "Caucasus"}, /* S (GMT+04:00) Yerevan */
276 {"Asia/Kabul", "Afghanistan"}, /* S (GMT+04:30) Kabul */
278 {"Asia/Karachi", "West Asia"}, /* S (GMT+05:00) Islamabad, Karachi, Tashkent */
279 {"Asia/Yekaterinburg", "Ekaterinburg"}, /* D (GMT+05:00) Ekaterinburg */
281 {"Asia/Calcutta", "India"}, /* S (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi */
283 {"Asia/Katmandu", "Nepal"}, /* S (GMT+05:45) Kathmandu */
285 {"Asia/Colombo", "Sri Lanka"}, /* S (GMT+06:00) Sri Jayawardenepura */
286 {"Asia/Dhaka", "Central Asia"}, /* S (GMT+06:00) Astana, Dhaka */
287 {"Asia/Novosibirsk", "N. Central Asia"}, /* D (GMT+06:00) Almaty, Novosibirsk */
289 {"Asia/Rangoon", "Myanmar"}, /* S (GMT+06:30) Rangoon */
291 {"Asia/Bangkok", "SE Asia"}, /* S (GMT+07:00) Bangkok, Hanoi, Jakarta */
292 {"Asia/Krasnoyarsk", "North Asia"}, /* D (GMT+07:00) Krasnoyarsk */
294 {"Australia/Perth", "W. Australia"}, /* S (GMT+08:00) Perth */
295 {"Asia/Taipei", "Taipei"}, /* S (GMT+08:00) Taipei */
296 {"Asia/Singapore", "Singapore"}, /* S (GMT+08:00) Kuala Lumpur, Singapore */
297 {"Asia/Shanghai", "China"}, /* S (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi */
298 {"Asia/Hong_Kong", "China"}, /* S (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi */
299 {"Asia/Ulaanbaatar", "North Asia East"}, /* D (GMT+08:00) Irkutsk, Ulaan Bataar */
300 {"Asia/Irkutsk", "North Asia East"}, /* D (GMT+08:00) Irkutsk, Ulaan Bataar */
302 {"Asia/Tokyo", "Tokyo"}, /* S (GMT+09:00) Osaka, Sapporo, Tokyo */
303 {"Asia/Seoul", "Korea"}, /* S (GMT+09:00) Seoul */
304 {"Asia/Yakutsk", "Yakutsk"}, /* D (GMT+09:00) Yakutsk */
306 {"Australia/Darwin", "AUS Central"}, /* S (GMT+09:30) Darwin */
307 {"Australia/Adelaide", "Cen. Australia"}, /* D (GMT+09:30) Adelaide */
309 {"Pacific/Guam", "West Pacific"}, /* S (GMT+10:00) Guam, Port Moresby */
310 {"Australia/Brisbane", "E. Australia"}, /* S (GMT+10:00) Brisbane */
311 {"Asia/Vladivostok", "Vladivostok"}, /* D (GMT+10:00) Vladivostok */
312 {"Australia/Hobart", "Tasmania"}, /* D (GMT+10:00) Hobart */
313 {"Australia/Sydney", "AUS Eastern"}, /* D (GMT+10:00) Canberra, Melbourne, Sydney */
315 {"Asia/Guadalcanal", "Central Pacific"}, /* S (GMT+11:00) Magadan, Solomon Is., New Caledonia */
316 {"Asia/Magadan", "Central Pacific"}, /* S (GMT+11:00) Magadan, Solomon Is., New Caledonia */
318 {"Pacific/Fiji", "Fiji"}, /* S (GMT+12:00) Fiji, Kamchatka, Marshall Is. */
319 {"Pacific/Auckland", "New Zealand"}, /* D (GMT+12:00) Auckland, Wellington */
321 {"Pacific/Tongatapu", "Tonga"}, /* S (GMT+13:00) Nuku'alofa */
326 * If a lookup fails, we attempt to remap certain Windows ids to
327 * alternate Windows ids. If the alternate listed here begins with
328 * '-', we use it as is (without the '-'). If it begins with '+', we
329 * append a " Standard Time" if appropriate.
331 static const WindowsZoneRemap ZONE_REMAP
[] = {
332 "Central European", "-Warsaw",
333 "Central Europe", "-Prague Bratislava",
338 "Arab", "+Saudi Arabia",
339 "SE Asia", "+Bangkok",
340 "AUS Eastern", "+Sydney",
341 "Mountain Standard Time (Mexico)", "-Mexico Standard Time 2",
342 "Central Standard Time (Mexico)", "+Mexico",
346 static int32_t fWinType
= -1;
348 static int32_t detectWindowsType()
354 /* Detect the version of windows by trying to open a sequence of
355 probe keys. We don't use the OS version API because what we
356 really want to know is how the registry is laid out.
357 Specifically, is it 9x/Me or not, and is it "GMT" or "GMT
359 for (winType
= 0; winType
< 2; winType
+= 1) {
360 result
= RegOpenKeyExA(HKEY_LOCAL_MACHINE
,
361 WIN_TYPE_PROBE_REGKEY
[winType
],
367 if (result
== ERROR_SUCCESS
) {
376 * TODO: Binary search sorted ZONE_MAP...
377 * (u_detectWindowsTimeZone() needs them sorted by offset...)
379 static const char *findWindowsZoneID(const UChar
*icuid
, int32_t length
)
381 char stackBuffer
[ICUID_STACK_BUFFER_SIZE
];
382 char *buffer
= stackBuffer
;
383 const char *result
= NULL
;
387 * NOTE: >= because length doesn't include
390 if (length
>= ICUID_STACK_BUFFER_SIZE
) {
391 buffer
= NEW_ARRAY(char, length
+ 1);
394 u_UCharsToChars(icuid
, buffer
, length
);
395 buffer
[length
] = '\0';
397 for (i
= 0; ZONE_MAP
[i
].icuid
!= NULL
; i
+= 1) {
398 if (uprv_strcmp(buffer
, ZONE_MAP
[i
].icuid
) == 0) {
399 result
= ZONE_MAP
[i
].winid
;
404 if (buffer
!= stackBuffer
) {
405 DELETE_ARRAY(buffer
);
411 static LONG
openTZRegKey(HKEY
*hkey
, const char *winid
)
413 char subKeyName
[96]; /* TODO: why 96?? */
417 /* TODO: This isn't thread safe, but it's probably good enough. */
419 fWinType
= detectWindowsType();
422 uprv_strcpy(subKeyName
, TZ_REGKEY
[(fWinType
== WIN_9X_ME_TYPE
) ? 0 : 1]);
423 name
= &subKeyName
[strlen(subKeyName
)];
424 uprv_strcat(subKeyName
, winid
);
426 if (fWinType
!= WIN_9X_ME_TYPE
&&
427 (winid
[strlen(winid
) - 1] != '2') &&
428 (winid
[strlen(winid
) - 1] != ')') &&
429 !(fWinType
== WIN_NT_TYPE
&& strcmp(winid
, "GMT") == 0)) {
430 uprv_strcat(subKeyName
, STANDARD_TIME_REGKEY
);
433 result
= RegOpenKeyExA(HKEY_LOCAL_MACHINE
,
439 if (result
!= ERROR_SUCCESS
) {
442 /* If the primary lookup fails, try to remap the Windows zone
443 ID, according to the remapping table. */
444 for (i
=0; ZONE_REMAP
[i
].winid
; i
++) {
445 if (uprv_strcmp(winid
, ZONE_REMAP
[i
].winid
) == 0) {
446 uprv_strcpy(name
, ZONE_REMAP
[i
].altwinid
+ 1);
447 if (*(ZONE_REMAP
[i
].altwinid
) == '+' && fWinType
!= WIN_9X_ME_TYPE
) {
448 uprv_strcat(subKeyName
, STANDARD_TIME_REGKEY
);
450 return RegOpenKeyExA(HKEY_LOCAL_MACHINE
,
462 static LONG
getTZI(const char *winid
, TZI
*tzi
)
464 DWORD cbData
= sizeof(TZI
);
468 result
= openTZRegKey(&hkey
, winid
);
470 if (result
== ERROR_SUCCESS
) {
471 result
= RegQueryValueExA(hkey
,
485 U_CAPI UBool U_EXPORT2
486 uprv_getWindowsTimeZoneInfo(TIME_ZONE_INFORMATION
*zoneInfo
, const UChar
*icuid
, int32_t length
)
492 winid
= findWindowsZoneID(icuid
, length
);
495 result
= getTZI(winid
, &tzi
);
497 if (result
== ERROR_SUCCESS
) {
498 zoneInfo
->Bias
= tzi
.bias
;
499 zoneInfo
->DaylightBias
= tzi
.daylightBias
;
500 zoneInfo
->StandardBias
= tzi
.standardBias
;
501 zoneInfo
->DaylightDate
= tzi
.daylightDate
;
502 zoneInfo
->StandardDate
= tzi
.standardDate
;
512 This code attempts to detect the Windows time zone, as set in the
513 Windows Date and Time control panel. It attempts to work on
514 multiple flavors of Windows (9x, Me, NT, 2000, XP) and on localized
515 installs. It works by directly interrogating the registry and
516 comparing the data there with the data returned by the
517 GetTimeZoneInformation API, along with some other strategies. The
518 registry contains time zone data under one of two keys (depending on
519 the flavor of Windows):
521 HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones\
522 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\
524 Under this key are several subkeys, one for each time zone. These
525 subkeys are named "Pacific" on Win9x/Me and "Pacific Standard Time"
526 on WinNT/2k/XP. There are some other wrinkles; see the code for
527 details. The subkey name is NOT LOCALIZED, allowing us to support
530 Under the subkey are data values. We care about:
532 Std Standard time display name, localized
533 TZI Binary block of data
535 The TZI data is of particular interest. It contains the offset, two
536 more offsets for standard and daylight time, and the start and end
537 rules. This is the same data returned by the GetTimeZoneInformation
538 API. The API may modify the data on the way out, so we have to be
539 careful, but essentially we do a binary comparison against the TZI
540 blocks of various registry keys. When we find a match, we know what
541 time zone Windows is set to. Since the registry key is not
542 localized, we can then translate the key through a simple table
543 lookup into the corresponding ICU time zone.
545 This strategy doesn't always work because there are zones which
546 share an offset and rules, so more than one TZI block will match.
547 For example, both Tokyo and Seoul are at GMT+9 with no DST rules;
548 their TZI blocks are identical. For these cases, we fall back to a
549 name lookup. We attempt to match the display name as stored in the
550 registry for the current zone to the display name stored in the
551 registry for various Windows zones. By comparing the registry data
552 directly we avoid conversion complications.
556 Based on original code by Carl Brown <cbrown@xnetinc.com>
560 * Main Windows time zone detection function. Returns the Windows
561 * time zone, translated to an ICU time zone, or NULL upon failure.
563 U_CFUNC
const char* U_EXPORT2
564 uprv_detectWindowsTimeZone() {
569 TIME_ZONE_INFORMATION apiTZI
;
570 int firstMatch
, lastMatch
;
573 /* Obtain TIME_ZONE_INFORMATION from the API, and then convert it
574 to TZI. We could also interrogate the registry directly; we do
575 this below if needed. */
576 uprv_memset(&apiTZI
, 0, sizeof(apiTZI
));
577 uprv_memset(&tziKey
, 0, sizeof(tziKey
));
578 uprv_memset(&tziReg
, 0, sizeof(tziReg
));
579 GetTimeZoneInformation(&apiTZI
);
580 tziKey
.bias
= apiTZI
.Bias
;
581 uprv_memcpy((char *)&tziKey
.standardDate
, (char*)&apiTZI
.StandardDate
,
582 sizeof(apiTZI
.StandardDate
));
583 uprv_memcpy((char *)&tziKey
.daylightDate
, (char*)&apiTZI
.DaylightDate
,
584 sizeof(apiTZI
.DaylightDate
));
586 /* For each zone that can be identified by Offset+Rules, see if we
587 have a match. Continue scanning after finding a match,
588 recording the index of the first and the last match. We have
589 to do this because some zones are not unique under
593 for (j
=0; ZONE_MAP
[j
].icuid
; j
++) {
594 result
= getTZI(ZONE_MAP
[j
].winid
, &tziReg
);
596 if (result
== ERROR_SUCCESS
) {
597 /* Assume that offsets are grouped together, and bail out
598 when we've scanned everything with a matching
600 if (firstMatch
>= 0 && tziKey
.bias
!= tziReg
.bias
) {
604 /* Windows alters the DaylightBias in some situations.
605 Using the bias and the rules suffices, so overwrite
606 these unreliable fields. */
607 tziKey
.standardBias
= tziReg
.standardBias
;
608 tziKey
.daylightBias
= tziReg
.daylightBias
;
610 if (uprv_memcmp((char *)&tziKey
, (char*)&tziReg
, sizeof(tziKey
)) == 0) {
611 if (firstMatch
< 0) {
620 /* This should never happen; if it does it means our table doesn't
621 match Windows AT ALL, perhaps because this is post-XP? */
622 if (firstMatch
< 0) {
626 if (firstMatch
!= lastMatch
) {
630 DWORD stdRegNameSize
;
632 /* Offset+Rules lookup yielded >= 2 matches. Try to match the
633 localized display name. Get the name from the registry
634 (not the API). This avoids conversion issues. Use the
635 standard name, since Windows modifies the daylight name to
636 match the standard name if there is no DST. */
637 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE
,
641 &hkey
) == ERROR_SUCCESS
)
643 stdNameSize
= sizeof(stdName
);
644 result
= RegQueryValueExA(hkey
,
645 STANDARD_NAME_REGKEY
,
653 * Scan through the Windows time zone data in the registry
654 * again (just the range of zones with matching TZIs) and
655 * look for a standard display name match.
657 for (j
= firstMatch
; j
<= lastMatch
; j
+= 1) {
658 stdRegNameSize
= sizeof(stdRegName
);
659 result
= openTZRegKey(&hkey
, ZONE_MAP
[j
].winid
);
661 if (result
== ERROR_SUCCESS
) {
662 result
= RegQueryValueExA(hkey
,
672 if (result
== ERROR_SUCCESS
&&
673 stdRegNameSize
== stdNameSize
&&
674 uprv_memcmp(stdName
, stdRegName
, stdNameSize
) == 0)
676 firstMatch
= j
; /* record the match */
681 RegCloseKey(hkey
); /* should never get here */
685 return ZONE_MAP
[firstMatch
].icuid
;
688 #endif /* #ifdef U_WINDOWS */