#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 = 0,
WIN_NT_TYPE = 1,
WIN_2K_XP_TYPE = 2
};
static const WindowsICUMap ZONE_MAP[] = {
{"Etc/GMT+12", "Dateline"},
{"Pacific/Apia", "Samoa"},
{"Pacific/Honolulu", "Hawaiian"},
{"America/Anchorage", "Alaskan"},
{"America/Los_Angeles", "Pacific"},
{"America/Phoenix", "US Mountain"},
{"America/Denver", "Mountain"},
{"America/Chihuahua", "Mexico Standard Time 2"},
{"America/Managua", "Central America"},
{"America/Regina", "Canada Central"},
{"America/Mexico_City", "Mexico"},
{"America/Chicago", "Central"},
{"America/Indianapolis", "US Eastern"},
{"America/Bogota", "SA Pacific"},
{"America/New_York", "Eastern"},
{"America/Caracas", "SA Western"},
{"America/Santiago", "Pacific SA"},
{"America/Halifax", "Atlantic"},
{"America/St_Johns", "Newfoundland"},
{"America/Buenos_Aires", "SA Eastern"},
{"America/Godthab", "Greenland"},
{"America/Sao_Paulo", "E. South America"},
{"America/Noronha", "Mid-Atlantic"},
{"Atlantic/Cape_Verde", "Cape Verde"},
{"Atlantic/Azores", "Azores"},
{"Africa/Casablanca", "Greenwich"},
{"Europe/London", "GMT"},
{"Africa/Lagos", "W. Central Africa"},
{"Europe/Berlin", "W. Europe"},
{"Europe/Paris", "Romance"},
{"Europe/Sarajevo", "Central European"},
{"Europe/Belgrade", "Central Europe"},
{"Africa/Johannesburg", "South Africa"},
{"Asia/Jerusalem", "Israel"},
{"Europe/Istanbul", "GTB"},
{"Europe/Helsinki", "FLE"},
{"Africa/Cairo", "Egypt"},
{"Europe/Bucharest", "E. Europe"},
{"Africa/Nairobi", "E. Africa"},
{"Asia/Riyadh", "Arab"},
{"Europe/Moscow", "Russian"},
{"Asia/Baghdad", "Arabic"},
{"Asia/Tehran", "Iran"},
{"Asia/Muscat", "Arabian"},
{"Asia/Tbilisi", "Caucasus"},
{"Asia/Kabul", "Afghanistan"},
{"Asia/Karachi", "West Asia"},
{"Asia/Yekaterinburg", "Ekaterinburg"},
{"Asia/Calcutta", "India"},
{"Asia/Katmandu", "Nepal"},
{"Asia/Colombo", "Sri Lanka"},
{"Asia/Dhaka", "Central Asia"},
{"Asia/Novosibirsk", "N. Central Asia"},
{"Asia/Rangoon", "Myanmar"},
{"Asia/Bangkok", "SE Asia"},
{"Asia/Krasnoyarsk", "North Asia"},
{"Australia/Perth", "W. Australia"},
{"Asia/Taipei", "Taipei"},
{"Asia/Singapore", "Singapore"},
{"Asia/Hong_Kong", "China"},
{"Asia/Irkutsk", "North Asia East"},
{"Asia/Tokyo", "Tokyo"},
{"Asia/Seoul", "Korea"},
{"Asia/Yakutsk", "Yakutsk"},
{"Australia/Darwin", "AUS Central"},
{"Australia/Adelaide", "Cen. Australia"},
{"Pacific/Guam", "West Pacific"},
{"Australia/Brisbane", "E. Australia"},
{"Asia/Vladivostok", "Vladivostok"},
{"Australia/Hobart", "Tasmania"},
{"Australia/Sydney", "AUS Eastern"},
{"Asia/Magadan", "Central Pacific"},
{"Pacific/Fiji", "Fiji"},
{"Pacific/Auckland", "New Zealand"},
{"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",
NULL, NULL,
};
static int32_t fWinType = -1;
static int32_t detectWindowsType()
{
int32_t winType;
LONG result;
HKEY hkey;
for (winType = 0; winType < 2; winType += 1) {
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
WIN_TYPE_PROBE_REGKEY[winType],
0,
KEY_QUERY_VALUE,
&hkey);
RegCloseKey(hkey);
if (result == ERROR_SUCCESS) {
break;
}
}
return winType;
}
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 (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') &&
!(fWinType == WIN_NT_TYPE && strcmp(winid, "GMT") == 0)) {
uprv_strcat(subKeyName, STANDARD_TIME_REGKEY);
}
result = RegOpenKeyEx(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) == '+' && fWinType != WIN_9X_ME_TYPE) {
uprv_strcat(subKeyName, STANDARD_TIME_REGKEY);
}
return RegOpenKeyEx(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 = RegQueryValueEx(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_CAPI 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 (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
CURRENT_ZONE_REGKEY,
0,
KEY_QUERY_VALUE,
&hkey) == ERROR_SUCCESS)
{
stdNameSize = sizeof(stdName);
result = RegQueryValueEx(hkey,
(LPTSTR)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 = RegQueryValueEx(hkey,
(LPTSTR)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