#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
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;
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";
static const char* const WIN_TYPE_PROBE_REGKEY[] = {
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones",
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\GMT"
};
static const char* const TZ_REGKEY[] = {
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones\\",
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\"
};
enum {
WIN_9X_ME_TYPE = 1,
WIN_NT_TYPE = 2,
WIN_2K_XP_TYPE = 3
};
# if 0
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
static const WindowsICUMap ZONE_MAP[] = {
{"Etc/GMT+12", "Dateline"},
{"Pacific/Apia", "Samoa"},
{"Pacific/Midway", "Samoa"},
{"Pacific/Honolulu", "Hawaiian"},
{"America/Anchorage", "Alaskan"},
{"America/Juneau", "Alaskan"},
{"America/Yakutat", "Alaskan"},
{"America/Nome", "Alaskan"},
{"America/Los_Angeles", "Pacific"},
{"America/Dawson", "Pacific"},
{"America/Vancouver", "Pacific"},
{"America/Whitehorse", "Pacific"},
{"America/Tijuana", "Pacific Standard Time (Mexico)"},
{"America/Denver", "Mountain"},
{"America/Boise", "Mountain"},
{"America/Cambridge_Bay", "Mountain"},
{"America/Edmonton", "Mountain"},
{"America/Inuvik", "Mountain"},
{"America/Shiprock", "Mountain"},
{"America/Yellowknife", "Mountain"},
{"America/Phoenix", "US Mountain"},
{"America/Chihuahua", "Mountain Standard Time (Mexico)"},
{"America/Mazatlan", "Mountain Standard Time (Mexico)"},
{"America/Chicago", "Central"},
{"America/Indiana/Knox", "Central"},
{"America/Indiana/Tell_City", "Central"},
{"America/Menominee", "Central"},
{"America/North_Dakota/Center", "Central"},
{"America/North_Dakota/New_Salem", "Central"},
{"America/Rainy_River", "Central"},
{"America/Rankin_Inlet", "Central"},
{"America/Winnipeg", "Central"},
{"America/Mexico_City", "Central Standard Time (Mexico)"},
{"America/Monterrey", "Central Standard Time (Mexico)"},
{"America/Guatemala", "Central America"},
{"America/Belize", "Central America"},
{"America/Costa_Rica", "Central America"},
{"America/El_Salvador", "Central America"},
{"America/Managua", "Central America"},
{"America/Tegucigalpa", "Central America"},
{"Pacific/Galapagos", "Central America"},
{"America/Regina", "Canada Central"},
{"America/Swift_Current", "Canada Central"},
{"America/New_York", "Eastern"},
{"America/Detroit", "Eastern"},
{"America/Grand_Turk", "Eastern"},
{"America/Indiana/Marengo", "Eastern"},
{"America/Indiana/Petersburg", "Eastern"},
{"America/Indiana/Vevay", "Eastern"},
{"America/Indiana/Vincennes", "Eastern"},
{"America/Indiana/Winamac", "Eastern"},
{"America/Indianapolis", "Eastern"},
{"America/Iqaluit", "Eastern"},
{"America/Kentucky/Monticello", "Eastern"},
{"America/Louisville", "Eastern"},
{"America/Montreal", "Eastern"},
{"America/Nassau", "Eastern"},
{"America/Nipigon", "Eastern"},
{"America/Pangnirtung", "Eastern"},
{"America/Thunder_Bay", "Eastern"},
{"America/Toronto", "Eastern"},
{"America/Bogota", "SA Pacific"},
{"America/Lima", "SA Pacific"},
{"America/Guayaquil", "SA Pacific"},
{"America/Rio_Branco", "SA Pacific"},
{"Etc/GMT+5", "US Eastern"},
{"America/Caracas", "Venezuela"},
{"America/Halifax", "Atlantic"},
{"America/Glace_Bay", "Atlantic"},
{"America/Moncton", "Atlantic"},
{"America/Santiago", "Pacific SA"},
{"America/Manaus", "Central Brazilian"},
{"America/La_Paz", "SA Western"},
{"America/St_Johns", "Newfoundland"},
{"America/Sao_Paulo", "E. South America"},
{"America/Buenos_Aires", "Argentina"},
{"America/Godthab", "Greenland"},
{"America/Montevideo", "Montevideo"},
{"Etc/GMT+3", "SA Eastern"},
{"America/South_Georgia", "Mid-Atlantic"},
{"America/Noronha", "Mid-Atlantic"},
{"Atlantic/Azores", "Azores"},
{"Atlantic/Cape_Verde", "Cape Verde"},
{"Europe/London", "GMT"},
{"Europe/Dublin", "GMT"},
{"Europe/Lisbon", "GMT"},
{"Africa/Casablanca", "Greenwich"},
{"Africa/Monrovia", "Greenwich"},
{"Atlantic/Reykjavik", "Greenwich"},
{"Europe/Paris", "Romance"},
{"Europe/Brussels", "Romance"},
{"Europe/Copenhagen", "Romance"},
{"Europe/Madrid", "Romance"},
{"Europe/Berlin", "W. Europe"},
{"Europe/Amsterdam", "W. Europe"},
{"Europe/Zurich", "W. Europe"},
{"Europe/Rome", "W. Europe"},
{"Europe/Stockholm", "W. Europe"},
{"Europe/Vienna", "W. Europe"},
{"Europe/Budapest", "Central Europe"},
{"Europe/Belgrade", "Central Europe"},
{"Europe/Bratislava", "Central Europe"},
{"Europe/Ljubljana", "Central Europe"},
{"Europe/Prague", "Central Europe"},
{"Eurpoe/Warsaw", "Central European"},
{"Eurpoe/Sarajevo", "Central European"},
{"Eurpoe/Skopje", "Central European"},
{"Eurpoe/Zagreb", "Central European"},
{"Africa/Lagos", "W. Central Africa"},
{"Africa/Luanda", "W. Central Africa"},
{"Africa/Porto-Novo", "W. Central Africa"},
{"Africa/Douala", "W. Central Africa"},
{"Africa/Bangui", "W. Central Africa"},
{"Africa/Ndjamena", "W. Central Africa"},
{"Africa/Kinshasa", "W. Central Africa"},
{"Africa/Brazzaville", "W. Central Africa"},
{"Africa/Malabo", "W. Central Africa"},
{"Africa/Libreville", "W. Central Africa"},
{"Africa/Niamey", "W. Central Africa"},
{"Europe/Istanbul", "GTB"},
{"Europe/Athens", "GTB"},
{"Europe/Bucharest", "GTB"},
{"Europe/Kiev", "FLE"},
{"Europe/Helsinki", "FLE"},
{"Europe/Riga", "FLE"},
{"Europe/Sofia", "FLE"},
{"Europe/Tallinn", "FLE"},
{"Europe/Vilnius", "FLE"},
{"Asia/Jerusalem", "Israel"},
{"Europe/Minsk", "E. Europe"},
{"Africa/Cairo", "Egypt"},
{"Asia/Amman", "Jordan"},
{"Asia/Beirut", "Middle East"},
{"Africa/Windhoek", "Namibia"},
{"Africa/Johannesburg", "South Africa"},
{"Africa/Harare", "South Africa"},
{"Europe/Moscow", "Russian"},
{"Europe/Volgograd", "Russian"},
{"Asia/Baghdad", "Arabic"},
{"Asia/Riyadh", "Arab"},
{"Asia/Kuwait", "Arab"},
{"Africa/Nairobi", "E. Africa"},
{"Etc/GMT-3", "Georgian"},
{"Asia/Tehran", "Iran"},
{"Asia/Yerevan", "Armenian"},
{"Asia/Baku", "Azerbaijan"},
{"Asia/Dubai", "Arabian"},
{"Asia/Muscat", "Arabian"},
{"Asia/Tbilisi", "Caucasus"},
{"Asia/Kabul", "Afghanistan"},
{"Asia/Yekaterinburg", "Ekaterinburg"},
{"Asia/Karachi", "West Asia"},
{"Asia/Tashkent", "West Asia"},
{"Asia/Calcutta", "India"},
{"Asia/Colombo", "Sri Lanka"},
{"Asia/Katmandu", "Nepal"},
{"Asia/Novosibirsk", "N. Central Asia"},
{"Asia/Dhaka", "Central Asia"},
{"Asia/Almaty", "Central Asia"},
{"Asia/Qyzylorda", "Central Asia"},
{"Asia/Rangoon", "Myanmar"},
{"Asia/Krasnoyarsk", "North Asia"},
{"Asia/Bangkok", "SE Asia"},
{"Asia/Saigon", "SE Asia"},
{"Asia/Jakarta", "SE Asia"},
{"Asia/Irkutsk", "North Asia East"},
{"Australia/Perth", "W. Australia"},
{"Asia/Shanghai", "China"},
{"Asia/Chongqing", "China"},
{"Asia/Hong_Kong", "China"},
{"Asia/Urumqi", "China"},
{"Asia/Taipei", "Taipei"},
{"Asia/Singapore", "Singapore"},
{"Asia/Kuala_Lumpur", "Singapore"},
{"Asia/Yakutsk", "Yakutsk"},
{"Asia/Tokyo", "Tokyo"},
{"Asia/Seoul", "Korea"},
{"Australia/Adelaide", "Cen. Australia"},
{"Australia/Darwin", "AUS Central"},
{"Australia/Sydney", "AUS Eastern"},
{"Australia/Melbourne", "AUS Eastern"},
{"Australia/Hobart", "Tasmania"},
{"Asia/Vladivostok", "Vladivostok"},
{"Australia/Brisbane", "E. Australia"},
{"Pacific/Port_Moresby", "West Pacific"},
{"Pacific/Guam", "West Pacific"},
{"Pacific/Guadalcanal", "Central Pacific"},
{"Pacific/Noumea", "Central Pacific"},
{"Pacific/Auckland", "New Zealand"},
{"Pacific/Fiji", "Fiji"},
{"Pacific/Majuro", "Fiji"},
{"Pacific/Kwajalein", "Fiji"},
{"Pacific/Tongatapu", "Tonga"},
NULL, NULL
};
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 gWinType = 0;
static int32_t detectWindowsType()
{
int32_t winType;
LONG result;
HKEY hkey;
for (winType = 0; winType < 2; winType++) {
result = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
WIN_TYPE_PROBE_REGKEY[winType],
0,
KEY_QUERY_VALUE,
&hkey);
RegCloseKey(hkey);
if (result == ERROR_SUCCESS) {
break;
}
}
return winType+1; }
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;
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];
char *name;
LONG result;
if (gWinType <= 0) {
gWinType = detectWindowsType();
}
uprv_strcpy(subKeyName, TZ_REGKEY[(gWinType != WIN_9X_ME_TYPE)]);
name = &subKeyName[strlen(subKeyName)];
uprv_strcat(subKeyName, winid);
if (gWinType != WIN_9X_ME_TYPE &&
(winid[strlen(winid) - 1] != '2') &&
(winid[strlen(winid) - 1] != ')') &&
!(gWinType == 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;
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) == '+' && gWinType != 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;
}
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;
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));
firstMatch = -1;
lastMatch = -1;
for (j=0; ZONE_MAP[j].icuid; j++) {
result = getTZI(ZONE_MAP[j].winid, &tziReg);
if (result == ERROR_SUCCESS) {
if (firstMatch >= 0 && tziKey.bias != tziReg.bias) {
break;
}
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;
}
}
}
if (firstMatch < 0) {
return NULL;
}
if (firstMatch != lastMatch) {
char stdName[32];
DWORD stdNameSize;
char stdRegName[64];
DWORD stdRegNameSize;
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);
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;
break;
}
}
} else {
RegCloseKey(hkey);
}
}
return ZONE_MAP[firstMatch].icuid;
}
#endif