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:
Shawn Steele 2017-03-13 22:30:03 +00:00
parent 05c591d7ef
commit 72c8be3fb6

View file

@ -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);
}
}