mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-13 08:53:20 +00:00
ICU-12912 - Windows timezone detection - Get rid of deprecated version checking for old OS behavior (ICU supports Win7+, the updated code supports Vista+ (and even some older version)
X-SVN-Rev: 39782
This commit is contained in:
parent
05c591d7ef
commit
72c8be3fb6
1 changed files with 79 additions and 137 deletions
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
// This file contains only desktop windows behavior
|
||||
#if U_PLATFORM_HAS_WIN32_API
|
||||
|
||||
#include "wintz.h"
|
||||
|
@ -46,102 +47,25 @@ typedef struct
|
|||
* Various registry keys and key fragments.
|
||||
*/
|
||||
static const char CURRENT_ZONE_REGKEY[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\";
|
||||
/* static const char STANDARD_NAME_REGKEY[] = "StandardName"; Currently unused constant */
|
||||
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.
|
||||
* The time zone root keys (under HKLM) for Win7+
|
||||
*/
|
||||
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 = 1,
|
||||
WIN_NT_TYPE = 2,
|
||||
WIN_2K_XP_TYPE = 3
|
||||
};
|
||||
|
||||
static int32_t gWinType = 0;
|
||||
|
||||
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++) {
|
||||
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; /* +1 to bring it inline with the enum */
|
||||
}
|
||||
static const char TZ_REGKEY[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\";
|
||||
|
||||
static LONG openTZRegKey(HKEY *hkey, const char *winid)
|
||||
{
|
||||
char subKeyName[110]; /* TODO: why 96?? */
|
||||
char subKeyName[110]; /* TODO: why 110?? */
|
||||
char *name;
|
||||
LONG result;
|
||||
|
||||
/* This isn't thread safe, but it's good enough because the result should be constant per system. */
|
||||
if (gWinType <= 0) {
|
||||
gWinType = detectWindowsType();
|
||||
}
|
||||
|
||||
uprv_strcpy(subKeyName, TZ_REGKEY[(gWinType != WIN_9X_ME_TYPE)]);
|
||||
uprv_strcpy(subKeyName, TZ_REGKEY);
|
||||
name = &subKeyName[strlen(subKeyName)];
|
||||
uprv_strcat(subKeyName, winid);
|
||||
|
||||
if (gWinType == WIN_9X_ME_TYPE) {
|
||||
/* Remove " Standard Time" */
|
||||
char *pStd = uprv_strstr(subKeyName, STANDARD_TIME_REGKEY);
|
||||
if (pStd) {
|
||||
*pStd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
result = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
|
||||
subKeyName,
|
||||
0,
|
||||
|
@ -158,7 +82,8 @@ static LONG getTZI(const char *winid, TZI *tzi)
|
|||
|
||||
result = openTZRegKey(&hkey, winid);
|
||||
|
||||
if (result == ERROR_SUCCESS) {
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
result = RegQueryValueExA(hkey,
|
||||
TZI_REGKEY,
|
||||
NULL,
|
||||
|
@ -171,14 +96,16 @@ static LONG getTZI(const char *winid, TZI *tzi)
|
|||
return result;
|
||||
}
|
||||
|
||||
static LONG getSTDName(const char *winid, char *regStdName, int32_t length) {
|
||||
static LONG getSTDName(const char *winid, char *regStdName, int32_t length)
|
||||
{
|
||||
DWORD cbData = length;
|
||||
LONG result;
|
||||
HKEY hkey;
|
||||
|
||||
result = openTZRegKey(&hkey, winid);
|
||||
|
||||
if (result == ERROR_SUCCESS) {
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
result = RegQueryValueExA(hkey,
|
||||
STD_REGKEY,
|
||||
NULL,
|
||||
|
@ -191,7 +118,8 @@ static LONG getSTDName(const char *winid, char *regStdName, int32_t length) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static LONG getTZKeyName(char* tzKeyName, int32_t length) {
|
||||
static LONG getTZKeyName(char* tzKeyName, int32_t length)
|
||||
{
|
||||
HKEY hkey;
|
||||
LONG result = FALSE;
|
||||
DWORD cbData = length;
|
||||
|
@ -218,21 +146,19 @@ static LONG getTZKeyName(char* tzKeyName, int32_t length) {
|
|||
}
|
||||
|
||||
/*
|
||||
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
|
||||
This code attempts to detect the Windows time zone directly,
|
||||
as set in the Windows Date and Time control panel. It attempts
|
||||
to work on windows greater than Windows Vista 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):
|
||||
registry contains time zone data under this key:
|
||||
|
||||
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
|
||||
subkeys are named "Pacific Standard Time" on Vista+.
|
||||
There are some other wrinkles; see the code for
|
||||
details. The subkey name is NOT LOCALIZED, allowing us to support
|
||||
localized installs.
|
||||
|
||||
|
@ -270,7 +196,8 @@ static LONG getTZKeyName(char* tzKeyName, int32_t length) {
|
|||
* time zone, translated to an ICU time zone, or NULL upon failure.
|
||||
*/
|
||||
U_CFUNC const char* U_EXPORT2
|
||||
uprv_detectWindowsTimeZone() {
|
||||
uprv_detectWindowsTimeZone()
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UResourceBundle* bundle = NULL;
|
||||
char* icuid = NULL;
|
||||
|
@ -288,7 +215,6 @@ uprv_detectWindowsTimeZone() {
|
|||
TZI tziReg;
|
||||
TIME_ZONE_INFORMATION apiTZI;
|
||||
|
||||
BOOL isVistaOrHigher;
|
||||
BOOL tryPreVistaFallback;
|
||||
OSVERSIONINFO osVerInfo;
|
||||
|
||||
|
@ -325,75 +251,86 @@ uprv_detectWindowsTimeZone() {
|
|||
*/
|
||||
uprv_memset(&osVerInfo, 0, sizeof(osVerInfo));
|
||||
osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
|
||||
GetVersionEx(&osVerInfo);
|
||||
isVistaOrHigher = osVerInfo.dwMajorVersion >= 6; /* actually includes Windows Server 2008 as well, but don't worry about it */
|
||||
tryPreVistaFallback = TRUE;
|
||||
if(isVistaOrHigher) {
|
||||
result = getTZKeyName(regStdName, sizeof(regStdName));
|
||||
if(ERROR_SUCCESS == result) {
|
||||
UResourceBundle* winTZ = ures_getByKey(bundle, regStdName, NULL, &status);
|
||||
if(U_SUCCESS(status)) {
|
||||
const UChar* icuTZ = NULL;
|
||||
if (errorCode != 0) {
|
||||
icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
|
||||
}
|
||||
if (errorCode==0 || icuTZ==NULL) {
|
||||
/* fallback to default "001" and reset status */
|
||||
status = U_ZERO_ERROR;
|
||||
icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
|
||||
}
|
||||
|
||||
if(U_SUCCESS(status)) {
|
||||
int index=0;
|
||||
while (! (*icuTZ == '\0' || *icuTZ ==' ')) {
|
||||
tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */
|
||||
}
|
||||
tmpid[index]='\0';
|
||||
tryPreVistaFallback = FALSE;
|
||||
}
|
||||
result = getTZKeyName(regStdName, sizeof(regStdName));
|
||||
if(ERROR_SUCCESS == result)
|
||||
{
|
||||
UResourceBundle* winTZ = ures_getByKey(bundle, regStdName, NULL, &status);
|
||||
if(U_SUCCESS(status))
|
||||
{
|
||||
const UChar* icuTZ = NULL;
|
||||
if (errorCode != 0)
|
||||
{
|
||||
icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
|
||||
}
|
||||
if (errorCode==0 || icuTZ==NULL)
|
||||
{
|
||||
/* fallback to default "001" and reset status */
|
||||
status = U_ZERO_ERROR;
|
||||
icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
|
||||
}
|
||||
|
||||
if(U_SUCCESS(status))
|
||||
{
|
||||
int index=0;
|
||||
while (! (*icuTZ == '\0' || *icuTZ ==' '))
|
||||
{
|
||||
tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */
|
||||
}
|
||||
tmpid[index]='\0';
|
||||
tryPreVistaFallback = FALSE;
|
||||
}
|
||||
ures_close(winTZ);
|
||||
}
|
||||
ures_close(winTZ);
|
||||
}
|
||||
|
||||
if(tryPreVistaFallback) {
|
||||
|
||||
if(tryPreVistaFallback)
|
||||
{
|
||||
/* Note: We get the winid not from static tables but from resource bundle. */
|
||||
while (U_SUCCESS(status) && ures_hasNext(bundle)) {
|
||||
while (U_SUCCESS(status) && ures_hasNext(bundle))
|
||||
{
|
||||
UBool idFound = FALSE;
|
||||
const char* winid;
|
||||
UResourceBundle* winTZ = ures_getNextResource(bundle, NULL, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
if (U_FAILURE(status))
|
||||
{
|
||||
break;
|
||||
}
|
||||
winid = ures_getKey(winTZ);
|
||||
result = getTZI(winid, &tziReg);
|
||||
|
||||
if (result == ERROR_SUCCESS) {
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
/* 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 (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) == 0)
|
||||
{
|
||||
const UChar* icuTZ = NULL;
|
||||
if (errorCode != 0) {
|
||||
if (errorCode != 0)
|
||||
{
|
||||
icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
|
||||
}
|
||||
if (errorCode==0 || icuTZ==NULL) {
|
||||
if (errorCode==0 || icuTZ==NULL)
|
||||
{
|
||||
/* fallback to default "001" and reset status */
|
||||
status = U_ZERO_ERROR;
|
||||
icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
|
||||
}
|
||||
|
||||
if (U_SUCCESS(status)) {
|
||||
if (U_SUCCESS(status))
|
||||
{
|
||||
/* Get the standard name from the registry key to compare with
|
||||
the one from Windows API call. */
|
||||
uprv_memset(regStdName, 0, sizeof(regStdName));
|
||||
result = getSTDName(winid, regStdName, sizeof(regStdName));
|
||||
if (result == ERROR_SUCCESS) {
|
||||
if (uprv_strcmp(apiStdName, regStdName) == 0) {
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
if (uprv_strcmp(apiStdName, regStdName) == 0)
|
||||
{
|
||||
idFound = TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -402,10 +339,12 @@ uprv_detectWindowsTimeZone() {
|
|||
* If none is found, tmpid buffer will contain a fallback ID (i.e. the time zone ID matching
|
||||
* the current time zone information)
|
||||
*/
|
||||
if (idFound || tmpid[0] == 0) {
|
||||
if (idFound || tmpid[0] == 0)
|
||||
{
|
||||
/* if icuTZ has more than one city, take only the first (i.e. terminate icuTZ at first space) */
|
||||
int index=0;
|
||||
while (! (*icuTZ == '\0' || *icuTZ ==' ')) {
|
||||
while (! (*icuTZ == '\0' || *icuTZ ==' '))
|
||||
{
|
||||
tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */
|
||||
}
|
||||
tmpid[index]='\0';
|
||||
|
@ -414,7 +353,8 @@ uprv_detectWindowsTimeZone() {
|
|||
}
|
||||
}
|
||||
ures_close(winTZ);
|
||||
if (idFound) {
|
||||
if (idFound)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -423,10 +363,12 @@ uprv_detectWindowsTimeZone() {
|
|||
/*
|
||||
* Copy the timezone ID to icuid to be returned.
|
||||
*/
|
||||
if (tmpid[0] != 0) {
|
||||
if (tmpid[0] != 0)
|
||||
{
|
||||
len = uprv_strlen(tmpid);
|
||||
icuid = (char*)uprv_calloc(len + 1, sizeof(char));
|
||||
if (icuid != NULL) {
|
||||
if (icuid != NULL)
|
||||
{
|
||||
uprv_strcpy(icuid, tmpid);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue