summaryrefslogtreecommitdiff
path: root/Build/source/libs/icu/icu-xetex/common/wintz.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/icu/icu-xetex/common/wintz.c')
-rw-r--r--Build/source/libs/icu/icu-xetex/common/wintz.c688
1 files changed, 688 insertions, 0 deletions
diff --git a/Build/source/libs/icu/icu-xetex/common/wintz.c b/Build/source/libs/icu/icu-xetex/common/wintz.c
new file mode 100644
index 00000000000..f64a4ad09ce
--- /dev/null
+++ b/Build/source/libs/icu/icu-xetex/common/wintz.c
@@ -0,0 +1,688 @@
+/*
+********************************************************************************
+* Copyright (C) 2005-2007, International Business Machines
+* Corporation and others. All Rights Reserved.
+********************************************************************************
+*
+* File WINTZ.CPP
+*
+********************************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#ifdef U_WINDOWS
+
+#include "wintz.h"
+
+#include "cmemory.h"
+#include "cstring.h"
+
+#include "unicode/ustring.h"
+
+# define WIN32_LEAN_AND_MEAN
+# define VC_EXTRALEAN
+# define NOUSER
+# define NOSERVICE
+# define NOIME
+# define NOMCX
+#include <windows.h>
+
+#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
+#define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
+#define DELETE_ARRAY(array) uprv_free((void *) (array))
+
+#define ICUID_STACK_BUFFER_SIZE 32
+
+/* The layout of the Tzi value in the registry */
+typedef struct
+{
+ int32_t bias;
+ int32_t standardBias;
+ int32_t daylightBias;
+ SYSTEMTIME standardDate;
+ SYSTEMTIME daylightDate;
+} TZI;
+
+typedef struct
+{
+ const char *icuid;
+ const char *winid;
+} WindowsICUMap;
+
+typedef struct {
+ const char* winid;
+ const char* altwinid;
+} WindowsZoneRemap;
+
+/**
+ * Various registry keys and key fragments.
+ */
+static const char CURRENT_ZONE_REGKEY[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\";
+static const char STANDARD_NAME_REGKEY[] = "StandardName";
+static const char STANDARD_TIME_REGKEY[] = " Standard Time";
+static const char TZI_REGKEY[] = "TZI";
+static const char STD_REGKEY[] = "Std";
+
+/**
+ * HKLM subkeys used to probe for the flavor of Windows. Note that we
+ * specifically check for the "GMT" zone subkey; this is present on
+ * NT, but on XP has become "GMT Standard Time". We need to
+ * discriminate between these cases.
+ */
+static const char* const WIN_TYPE_PROBE_REGKEY[] = {
+ /* WIN_9X_ME_TYPE */
+ "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones",
+
+ /* WIN_NT_TYPE */
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\GMT"
+
+ /* otherwise: WIN_2K_XP_TYPE */
+};
+
+/**
+ * The time zone root subkeys (under HKLM) for different flavors of
+ * Windows.
+ */
+static const char* const TZ_REGKEY[] = {
+ /* WIN_9X_ME_TYPE */
+ "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones\\",
+
+ /* WIN_NT_TYPE | WIN_2K_XP_TYPE */
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\"
+};
+
+/**
+ * Flavor of Windows, from our perspective. Not a real OS version,
+ * but rather the flavor of the layout of the time zone information in
+ * the registry.
+ */
+enum {
+ WIN_9X_ME_TYPE = 0,
+ WIN_NT_TYPE = 1,
+ WIN_2K_XP_TYPE = 2
+};
+
+# if 0
+/*
+ * ZONE_MAP from supplementalData.txt
+ */
+static const WindowsICUMap NEW_ZONE_MAP[] = {
+ {"Africa/Cairo", "Egypt"},
+ {"Africa/Casablanca", "Greenwich"},
+ {"Africa/Johannesburg", "South Africa"},
+ {"Africa/Lagos", "W. Central Africa"},
+ {"Africa/Nairobi", "E. Africa"},
+ {"Africa/Windhoek", "Namibia"},
+ {"America/Anchorage", "Alaskan"},
+ {"America/Bogota", "SA Pacific"},
+ {"America/Buenos_Aires", "SA Eastern"},
+ {"America/Caracas", "SA Western"},
+ {"America/Chicago", "Central"},
+ {"America/Chihuahua", "Mountain Standard Time (Mexico)"},
+ {"America/Denver", "Mountain"},
+ {"America/Godthab", "Greenland"},
+ {"America/Guatemala", "Central America"},
+ {"America/Halifax", "Atlantic"},
+ {"America/Indianapolis", "US Eastern"},
+ {"America/Los_Angeles", "Pacific"},
+ {"America/Manaus", "Central Brazilian"},
+ {"America/Mexico_City", "Central Standard Time (Mexico)"},
+ {"America/Montevideo", "Montevideo"},
+ {"America/New_York", "Eastern"},
+ {"America/Noronha", "Mid-Atlantic"},
+ {"America/Phoenix", "US Mountain"},
+ {"America/Regina", "Canada Central"},
+ {"America/Santiago", "Pacific SA"},
+ {"America/Sao_Paulo", "E. South America"},
+ {"America/St_Johns", "Newfoundland"},
+ {"America/Tijuana", "Pacific Standard Time (Mexico)"},
+ {"Asia/Amman", "Jordan"},
+ {"Asia/Baghdad", "Arabic"},
+ {"Asia/Baku", "Azerbaijan"},
+ {"Asia/Bangkok", "SE Asia"},
+ {"Asia/Beirut", "Middle East"},
+ {"Asia/Calcutta", "India"},
+ {"Asia/Colombo", "Sri Lanka"},
+ {"Asia/Dhaka", "Central Asia"},
+ {"Asia/Jerusalem", "Israel"},
+ {"Asia/Kabul", "Afghanistan"},
+ {"Asia/Karachi", "West Asia"},
+ {"Asia/Katmandu", "Nepal"},
+ {"Asia/Krasnoyarsk", "North Asia"},
+ {"Asia/Muscat", "Arabian"},
+ {"Asia/Novosibirsk", "N. Central Asia"},
+ {"Asia/Rangoon", "Myanmar"},
+ {"Asia/Riyadh", "Arab"},
+ {"Asia/Seoul", "Korea"},
+ {"Asia/Shanghai", "China"},
+ {"Asia/Singapore", "Singapore"},
+ {"Asia/Taipei", "Taipei"},
+ {"Asia/Tbilisi", "Georgian"},
+ {"Asia/Tehran", "Iran"},
+ {"Asia/Tokyo", "Tokyo"},
+ {"Asia/Ulaanbaatar", "North Asia East"},
+ {"Asia/Vladivostok", "Vladivostok"},
+ {"Asia/Yakutsk", "Yakutsk"},
+ {"Asia/Yekaterinburg", "Ekaterinburg"},
+ {"Asia/Yerevan", "Caucasus"},
+ {"Atlantic/Azores", "Azores"},
+ {"Atlantic/Cape_Verde", "Cape Verde"},
+ {"Australia/Adelaide", "Cen. Australia"},
+ {"Australia/Brisbane", "E. Australia"},
+ {"Australia/Darwin", "AUS Central"},
+ {"Australia/Hobart", "Tasmania"},
+ {"Australia/Perth", "W. Australia"},
+ {"Australia/Sydney", "AUS Eastern"},
+ {"Europe/Berlin", "W. Europe"},
+ {"Europe/Helsinki", "FLE"},
+ {"Europe/Istanbul", "GTB"},
+ {"Europe/London", "GMT"},
+ {"Europe/Minsk", "E. Europe"},
+ {"Europe/Moscow", "Russian"},
+ {"Europe/Paris", "Romance"},
+ {"Europe/Prague", "Central Europe"},
+ {"Europe/Warsaw", "Central European"},
+ {"Pacific/Apia", "Samoa"},
+ {"Pacific/Auckland", "New Zealand"},
+ {"Pacific/Fiji", "Fiji"},
+ {"Pacific/Guadalcanal", "Central Pacific"},
+ {"Pacific/Guam", "West Pacific"},
+ {"Pacific/Honolulu", "Hawaiian"},
+ {"Pacific/Kwajalein", "Dateline"},
+ {"Pacific/Tongatapu", "Tonga"}
+};
+#endif
+
+/* NOTE: Some Windows zone ids appear more than once. In such cases the
+ * ICU zone id from the first one is the preferred match.
+ */
+static const WindowsICUMap ZONE_MAP[] = {
+ {"Pacific/Kwajalein", "Dateline"}, /* S (GMT-12:00) International Date Line West */
+ {"Etc/GMT+12", "Dateline"}, /* S (GMT-12:00) International Date Line West */
+
+ {"Pacific/Apia", "Samoa"}, /* S (GMT-11:00) Midway Island, Samoa */
+
+ {"Pacific/Honolulu", "Hawaiian"}, /* S (GMT-10:00) Hawaii */
+
+ {"America/Anchorage", "Alaskan"}, /* D (GMT-09:00) Alaska */
+
+ {"America/Los_Angeles", "Pacific"}, /* D (GMT-08:00) Pacific Time (US & Canada) */
+ {"America/Tijuana", "Pacific Standard Time (Mexico)"}, /* S (GMT-08:00) Tijuana, Baja California */
+
+ {"America/Phoenix", "US Mountain"}, /* S (GMT-07:00) Arizona */
+ {"America/Denver", "Mountain"}, /* D (GMT-07:00) Mountain Time (US & Canada) */
+ {"America/Chihuahua", "Mountain Standard Time (Mexico)"}, /* D (GMT-07:00) Chihuahua, La Paz, Mazatlan */
+
+ {"America/Managua", "Central America"}, /* S (GMT-06:00) Central America */ /* America/Guatemala? */
+ {"America/Regina", "Canada Central"}, /* S (GMT-06:00) Saskatchewan */
+ {"America/Mexico_City", "Central Standard Time (Mexico)"}, /* D (GMT-06:00) Guadalajara, Mexico City, Monterrey */
+ {"America/Chicago", "Central"}, /* D (GMT-06:00) Central Time (US & Canada) */
+
+ {"America/Indianapolis", "US Eastern"}, /* S (GMT-05:00) Indiana (East) */
+ {"America/Bogota", "SA Pacific"}, /* S (GMT-05:00) Bogota, Lima, Quito */
+ {"America/New_York", "Eastern"}, /* D (GMT-05:00) Eastern Time (US & Canada) */
+
+ {"America/Caracas", "SA Western"}, /* S (GMT-04:00) Caracas, La Paz */
+ {"America/Santiago", "Pacific SA"}, /* D (GMT-04:00) Santiago */
+ {"America/Halifax", "Atlantic"}, /* D (GMT-04:00) Atlantic Time (Canada) */
+ {"America/Manaus", "Central Brazilian"}, /* D (GMT-04:00 Manaus */
+
+ {"America/St_Johns", "Newfoundland"}, /* D (GMT-03:30) Newfoundland */
+
+ {"America/Buenos_Aires", "SA Eastern"}, /* S (GMT-03:00) Buenos Aires, Georgetown */
+ {"America/Godthab", "Greenland"}, /* D (GMT-03:00) Greenland */
+ {"America/Sao_Paulo", "E. South America"}, /* D (GMT-03:00) Brasilia */
+ {"America/Montevideo", "Montevideo"}, /* S (GMT-03:00) Montevideo */
+
+ {"America/Noronha", "Mid-Atlantic"}, /* D (GMT-02:00) Mid-Atlantic */
+
+ {"Atlantic/Cape_Verde", "Cape Verde"}, /* S (GMT-01:00) Cape Verde Is. */
+ {"Atlantic/Azores", "Azores"}, /* D (GMT-01:00) Azores */
+
+ {"Africa/Casablanca", "Greenwich"}, /* S (GMT) Casablanca, Monrovia */
+ {"Europe/London", "GMT"}, /* D (GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London */
+
+ {"Africa/Lagos", "W. Central Africa"}, /* S (GMT+01:00) West Central Africa */
+ {"Europe/Berlin", "W. Europe"}, /* D (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna */
+ {"Europe/Paris", "Romance"}, /* D (GMT+01:00) Brussels, Copenhagen, Madrid, Paris */
+ {"Eurpoe/Warsaw", "Central European"}, /* D (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb */
+ {"Europe/Sarajevo", "Central European"}, /* D (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb */
+ {"Europe/Prague", "Central Europe"}, /* D (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague */
+ {"Europe/Belgrade", "Central Europe"}, /* D (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague */
+
+ {"Africa/Johannesburg", "South Africa"}, /* S (GMT+02:00) Harare, Pretoria */
+ {"Asia/Jerusalem", "Israel"}, /* S (GMT+02:00) Jerusalem */
+ {"Europe/Istanbul", "GTB"}, /* D (GMT+02:00) Athens, Istanbul, Minsk */
+ {"Europe/Helsinki", "FLE"}, /* D (GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius */
+ {"Africa/Cairo", "Egypt"}, /* D (GMT+02:00) Cairo */
+ {"Europe/Minsk", "E. Europe"}, /* D (GMT+02:00) Bucharest */
+ {"Europe/Bucharest", "E. Europe"}, /* D (GMT+02:00) Bucharest */
+ {"Africa/Windhoek", "Namibia"}, /* S (GMT+02:00) Windhoek */
+ {"Asia/Amman", "Jordan"}, /* S (GMT+02:00) Aman */
+ {"Asia/Beirut", "Middle East"}, /* S (GMT+02:00) Beirut */
+
+ {"Africa/Nairobi", "E. Africa"}, /* S (GMT+03:00) Nairobi */
+ {"Asia/Riyadh", "Arab"}, /* S (GMT+03:00) Kuwait, Riyadh */
+ {"Europe/Moscow", "Russian"}, /* D (GMT+03:00) Moscow, St. Petersburg, Volgograd */
+ {"Asia/Baghdad", "Arabic"}, /* D (GMT+03:00) Baghdad */
+
+ {"Asia/Tehran", "Iran"}, /* D (GMT+03:30) Tehran */
+
+ {"Asia/Muscat", "Arabian"}, /* S (GMT+04:00) Abu Dhabi, Muscat */
+ {"Asia/Tbilisi", "Georgian"}, /* D (GMT+04:00) Tbilisi */
+ {"Asia/Baku", "Azerbaijan"}, /* S (GMT+04:00) Baku */
+ {"Asia/Yerevan", "Caucasus"}, /* S (GMT+04:00) Yerevan */
+ {"Asia/Kabul", "Afghanistan"}, /* S (GMT+04:30) Kabul */
+
+ {"Asia/Karachi", "West Asia"}, /* S (GMT+05:00) Islamabad, Karachi, Tashkent */
+ {"Asia/Yekaterinburg", "Ekaterinburg"}, /* D (GMT+05:00) Ekaterinburg */
+
+ {"Asia/Calcutta", "India"}, /* S (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi */
+
+ {"Asia/Katmandu", "Nepal"}, /* S (GMT+05:45) Kathmandu */
+
+ {"Asia/Colombo", "Sri Lanka"}, /* S (GMT+06:00) Sri Jayawardenepura */
+ {"Asia/Dhaka", "Central Asia"}, /* S (GMT+06:00) Astana, Dhaka */
+ {"Asia/Novosibirsk", "N. Central Asia"}, /* D (GMT+06:00) Almaty, Novosibirsk */
+
+ {"Asia/Rangoon", "Myanmar"}, /* S (GMT+06:30) Rangoon */
+
+ {"Asia/Bangkok", "SE Asia"}, /* S (GMT+07:00) Bangkok, Hanoi, Jakarta */
+ {"Asia/Krasnoyarsk", "North Asia"}, /* D (GMT+07:00) Krasnoyarsk */
+
+ {"Australia/Perth", "W. Australia"}, /* S (GMT+08:00) Perth */
+ {"Asia/Taipei", "Taipei"}, /* S (GMT+08:00) Taipei */
+ {"Asia/Singapore", "Singapore"}, /* S (GMT+08:00) Kuala Lumpur, Singapore */
+ {"Asia/Shanghai", "China"}, /* S (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi */
+ {"Asia/Hong_Kong", "China"}, /* S (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi */
+ {"Asia/Ulaanbaatar", "North Asia East"}, /* D (GMT+08:00) Irkutsk, Ulaan Bataar */
+ {"Asia/Irkutsk", "North Asia East"}, /* D (GMT+08:00) Irkutsk, Ulaan Bataar */
+
+ {"Asia/Tokyo", "Tokyo"}, /* S (GMT+09:00) Osaka, Sapporo, Tokyo */
+ {"Asia/Seoul", "Korea"}, /* S (GMT+09:00) Seoul */
+ {"Asia/Yakutsk", "Yakutsk"}, /* D (GMT+09:00) Yakutsk */
+
+ {"Australia/Darwin", "AUS Central"}, /* S (GMT+09:30) Darwin */
+ {"Australia/Adelaide", "Cen. Australia"}, /* D (GMT+09:30) Adelaide */
+
+ {"Pacific/Guam", "West Pacific"}, /* S (GMT+10:00) Guam, Port Moresby */
+ {"Australia/Brisbane", "E. Australia"}, /* S (GMT+10:00) Brisbane */
+ {"Asia/Vladivostok", "Vladivostok"}, /* D (GMT+10:00) Vladivostok */
+ {"Australia/Hobart", "Tasmania"}, /* D (GMT+10:00) Hobart */
+ {"Australia/Sydney", "AUS Eastern"}, /* D (GMT+10:00) Canberra, Melbourne, Sydney */
+
+ {"Asia/Guadalcanal", "Central Pacific"}, /* S (GMT+11:00) Magadan, Solomon Is., New Caledonia */
+ {"Asia/Magadan", "Central Pacific"}, /* S (GMT+11:00) Magadan, Solomon Is., New Caledonia */
+
+ {"Pacific/Fiji", "Fiji"}, /* S (GMT+12:00) Fiji, Kamchatka, Marshall Is. */
+ {"Pacific/Auckland", "New Zealand"}, /* D (GMT+12:00) Auckland, Wellington */
+
+ {"Pacific/Tongatapu", "Tonga"}, /* S (GMT+13:00) Nuku'alofa */
+ NULL, NULL
+};
+
+/**
+ * If a lookup fails, we attempt to remap certain Windows ids to
+ * alternate Windows ids. If the alternate listed here begins with
+ * '-', we use it as is (without the '-'). If it begins with '+', we
+ * append a " Standard Time" if appropriate.
+ */
+static const WindowsZoneRemap ZONE_REMAP[] = {
+ "Central European", "-Warsaw",
+ "Central Europe", "-Prague Bratislava",
+ "China", "-Beijing",
+
+ "Greenwich", "+GMT",
+ "GTB", "+GFT",
+ "Arab", "+Saudi Arabia",
+ "SE Asia", "+Bangkok",
+ "AUS Eastern", "+Sydney",
+ "Mountain Standard Time (Mexico)", "-Mexico Standard Time 2",
+ "Central Standard Time (Mexico)", "+Mexico",
+ NULL, NULL,
+};
+
+static int32_t fWinType = -1;
+
+static int32_t detectWindowsType()
+{
+ int32_t winType;
+ LONG result;
+ HKEY hkey;
+
+ /* Detect the version of windows by trying to open a sequence of
+ probe keys. We don't use the OS version API because what we
+ really want to know is how the registry is laid out.
+ Specifically, is it 9x/Me or not, and is it "GMT" or "GMT
+ Standard Time". */
+ for (winType = 0; winType < 2; winType += 1) {
+ result = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+ WIN_TYPE_PROBE_REGKEY[winType],
+ 0,
+ KEY_QUERY_VALUE,
+ &hkey);
+ RegCloseKey(hkey);
+
+ if (result == ERROR_SUCCESS) {
+ break;
+ }
+ }
+
+ return winType;
+}
+
+/*
+ * TODO: Binary search sorted ZONE_MAP...
+ * (u_detectWindowsTimeZone() needs them sorted by offset...)
+ */
+static const char *findWindowsZoneID(const UChar *icuid, int32_t length)
+{
+ char stackBuffer[ICUID_STACK_BUFFER_SIZE];
+ char *buffer = stackBuffer;
+ const char *result = NULL;
+ int i;
+
+ /*
+ * NOTE: >= because length doesn't include
+ * trailing null.
+ */
+ if (length >= ICUID_STACK_BUFFER_SIZE) {
+ buffer = NEW_ARRAY(char, length + 1);
+ }
+
+ u_UCharsToChars(icuid, buffer, length);
+ buffer[length] = '\0';
+
+ for (i = 0; ZONE_MAP[i].icuid != NULL; i += 1) {
+ if (uprv_strcmp(buffer, ZONE_MAP[i].icuid) == 0) {
+ result = ZONE_MAP[i].winid;
+ break;
+ }
+ }
+
+ if (buffer != stackBuffer) {
+ DELETE_ARRAY(buffer);
+ }
+
+ return result;
+}
+
+static LONG openTZRegKey(HKEY *hkey, const char *winid)
+{
+ char subKeyName[96]; /* TODO: why 96?? */
+ char *name;
+ LONG result;
+
+ /* TODO: This isn't thread safe, but it's probably good enough. */
+ if (fWinType < 0) {
+ fWinType = detectWindowsType();
+ }
+
+ uprv_strcpy(subKeyName, TZ_REGKEY[(fWinType == WIN_9X_ME_TYPE) ? 0 : 1]);
+ name = &subKeyName[strlen(subKeyName)];
+ uprv_strcat(subKeyName, winid);
+
+ if (fWinType != WIN_9X_ME_TYPE &&
+ (winid[strlen(winid) - 1] != '2') &&
+ (winid[strlen(winid) - 1] != ')') &&
+ !(fWinType == WIN_NT_TYPE && strcmp(winid, "GMT") == 0)) {
+ uprv_strcat(subKeyName, STANDARD_TIME_REGKEY);
+ }
+
+ result = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+ subKeyName,
+ 0,
+ KEY_QUERY_VALUE,
+ hkey);
+
+ if (result != ERROR_SUCCESS) {
+ int i;
+
+ /* If the primary lookup fails, try to remap the Windows zone
+ ID, according to the remapping table. */
+ for (i=0; ZONE_REMAP[i].winid; i++) {
+ if (uprv_strcmp(winid, ZONE_REMAP[i].winid) == 0) {
+ uprv_strcpy(name, ZONE_REMAP[i].altwinid + 1);
+ if (*(ZONE_REMAP[i].altwinid) == '+' && fWinType != WIN_9X_ME_TYPE) {
+ uprv_strcat(subKeyName, STANDARD_TIME_REGKEY);
+ }
+ return RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+ subKeyName,
+ 0,
+ KEY_QUERY_VALUE,
+ hkey);
+ }
+ }
+ }
+
+ return result;
+}
+
+static LONG getTZI(const char *winid, TZI *tzi)
+{
+ DWORD cbData = sizeof(TZI);
+ LONG result;
+ HKEY hkey;
+
+ result = openTZRegKey(&hkey, winid);
+
+ if (result == ERROR_SUCCESS) {
+ result = RegQueryValueExA(hkey,
+ TZI_REGKEY,
+ NULL,
+ NULL,
+ (LPBYTE)tzi,
+ &cbData);
+
+ }
+
+ RegCloseKey(hkey);
+
+ return result;
+}
+
+U_CAPI UBool U_EXPORT2
+uprv_getWindowsTimeZoneInfo(TIME_ZONE_INFORMATION *zoneInfo, const UChar *icuid, int32_t length)
+{
+ const char *winid;
+ TZI tzi;
+ LONG result;
+
+ winid = findWindowsZoneID(icuid, length);
+
+ if (winid != NULL) {
+ result = getTZI(winid, &tzi);
+
+ if (result == ERROR_SUCCESS) {
+ zoneInfo->Bias = tzi.bias;
+ zoneInfo->DaylightBias = tzi.daylightBias;
+ zoneInfo->StandardBias = tzi.standardBias;
+ zoneInfo->DaylightDate = tzi.daylightDate;
+ zoneInfo->StandardDate = tzi.standardDate;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ This code attempts to detect the Windows time zone, as set in the
+ Windows Date and Time control panel. It attempts to work on
+ multiple flavors of Windows (9x, Me, NT, 2000, XP) and on localized
+ installs. It works by directly interrogating the registry and
+ comparing the data there with the data returned by the
+ GetTimeZoneInformation API, along with some other strategies. The
+ registry contains time zone data under one of two keys (depending on
+ the flavor of Windows):
+
+ HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones\
+ HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\
+
+ Under this key are several subkeys, one for each time zone. These
+ subkeys are named "Pacific" on Win9x/Me and "Pacific Standard Time"
+ on WinNT/2k/XP. There are some other wrinkles; see the code for
+ details. The subkey name is NOT LOCALIZED, allowing us to support
+ localized installs.
+
+ Under the subkey are data values. We care about:
+
+ Std Standard time display name, localized
+ TZI Binary block of data
+
+ The TZI data is of particular interest. It contains the offset, two
+ more offsets for standard and daylight time, and the start and end
+ rules. This is the same data returned by the GetTimeZoneInformation
+ API. The API may modify the data on the way out, so we have to be
+ careful, but essentially we do a binary comparison against the TZI
+ blocks of various registry keys. When we find a match, we know what
+ time zone Windows is set to. Since the registry key is not
+ localized, we can then translate the key through a simple table
+ lookup into the corresponding ICU time zone.
+
+ This strategy doesn't always work because there are zones which
+ share an offset and rules, so more than one TZI block will match.
+ For example, both Tokyo and Seoul are at GMT+9 with no DST rules;
+ their TZI blocks are identical. For these cases, we fall back to a
+ name lookup. We attempt to match the display name as stored in the
+ registry for the current zone to the display name stored in the
+ registry for various Windows zones. By comparing the registry data
+ directly we avoid conversion complications.
+
+ Author: Alan Liu
+ Since: ICU 2.6
+ Based on original code by Carl Brown <cbrown@xnetinc.com>
+*/
+
+/**
+ * Main Windows time zone detection function. Returns the Windows
+ * time zone, translated to an ICU time zone, or NULL upon failure.
+ */
+U_CFUNC const char* U_EXPORT2
+uprv_detectWindowsTimeZone() {
+ LONG result;
+ HKEY hkey;
+ TZI tziKey;
+ TZI tziReg;
+ TIME_ZONE_INFORMATION apiTZI;
+ int firstMatch, lastMatch;
+ int j;
+
+ /* Obtain TIME_ZONE_INFORMATION from the API, and then convert it
+ to TZI. We could also interrogate the registry directly; we do
+ this below if needed. */
+ uprv_memset(&apiTZI, 0, sizeof(apiTZI));
+ uprv_memset(&tziKey, 0, sizeof(tziKey));
+ uprv_memset(&tziReg, 0, sizeof(tziReg));
+ GetTimeZoneInformation(&apiTZI);
+ tziKey.bias = apiTZI.Bias;
+ uprv_memcpy((char *)&tziKey.standardDate, (char*)&apiTZI.StandardDate,
+ sizeof(apiTZI.StandardDate));
+ uprv_memcpy((char *)&tziKey.daylightDate, (char*)&apiTZI.DaylightDate,
+ sizeof(apiTZI.DaylightDate));
+
+ /* For each zone that can be identified by Offset+Rules, see if we
+ have a match. Continue scanning after finding a match,
+ recording the index of the first and the last match. We have
+ to do this because some zones are not unique under
+ Offset+Rules. */
+ firstMatch = -1;
+ lastMatch = -1;
+ for (j=0; ZONE_MAP[j].icuid; j++) {
+ result = getTZI(ZONE_MAP[j].winid, &tziReg);
+
+ if (result == ERROR_SUCCESS) {
+ /* Assume that offsets are grouped together, and bail out
+ when we've scanned everything with a matching
+ offset. */
+ if (firstMatch >= 0 && tziKey.bias != tziReg.bias) {
+ break;
+ }
+
+ /* Windows alters the DaylightBias in some situations.
+ Using the bias and the rules suffices, so overwrite
+ these unreliable fields. */
+ tziKey.standardBias = tziReg.standardBias;
+ tziKey.daylightBias = tziReg.daylightBias;
+
+ if (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) == 0) {
+ if (firstMatch < 0) {
+ firstMatch = j;
+ }
+
+ lastMatch = j;
+ }
+ }
+ }
+
+ /* This should never happen; if it does it means our table doesn't
+ match Windows AT ALL, perhaps because this is post-XP? */
+ if (firstMatch < 0) {
+ return NULL;
+ }
+
+ if (firstMatch != lastMatch) {
+ char stdName[32];
+ DWORD stdNameSize;
+ char stdRegName[64];
+ DWORD stdRegNameSize;
+
+ /* Offset+Rules lookup yielded >= 2 matches. Try to match the
+ localized display name. Get the name from the registry
+ (not the API). This avoids conversion issues. Use the
+ standard name, since Windows modifies the daylight name to
+ match the standard name if there is no DST. */
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+ CURRENT_ZONE_REGKEY,
+ 0,
+ KEY_QUERY_VALUE,
+ &hkey) == ERROR_SUCCESS)
+ {
+ stdNameSize = sizeof(stdName);
+ result = RegQueryValueExA(hkey,
+ STANDARD_NAME_REGKEY,
+ NULL,
+ NULL,
+ (LPBYTE)stdName,
+ &stdNameSize);
+ RegCloseKey(hkey);
+
+ /*
+ * Scan through the Windows time zone data in the registry
+ * again (just the range of zones with matching TZIs) and
+ * look for a standard display name match.
+ */
+ for (j = firstMatch; j <= lastMatch; j += 1) {
+ stdRegNameSize = sizeof(stdRegName);
+ result = openTZRegKey(&hkey, ZONE_MAP[j].winid);
+
+ if (result == ERROR_SUCCESS) {
+ result = RegQueryValueExA(hkey,
+ STD_REGKEY,
+ NULL,
+ NULL,
+ (LPBYTE)stdRegName,
+ &stdRegNameSize);
+ }
+
+ RegCloseKey(hkey);
+
+ if (result == ERROR_SUCCESS &&
+ stdRegNameSize == stdNameSize &&
+ uprv_memcmp(stdName, stdRegName, stdNameSize) == 0)
+ {
+ firstMatch = j; /* record the match */
+ break;
+ }
+ }
+ } else {
+ RegCloseKey(hkey); /* should never get here */
+ }
+ }
+
+ return ZONE_MAP[firstMatch].icuid;
+}
+
+#endif /* #ifdef U_WINDOWS */