7044727: (tz) TimeZone.getDefault() call returns incorrect value in Windows terminal session

Reviewed-by: peytoia
This commit is contained in:
Masayoshi Okutsu 2015-04-17 16:50:16 +09:00
parent afdabb44dc
commit 204901217f

View File

@ -147,219 +147,248 @@ static void customZoneName(LONG bias, char *buffer) {
*/ */
static int getWinTimeZone(char *winZoneName, char *winMapID) static int getWinTimeZone(char *winZoneName, char *winMapID)
{ {
TIME_ZONE_INFORMATION tzi; DYNAMIC_TIME_ZONE_INFORMATION dtzi;
OSVERSIONINFO ver;
int onlyMapID;
HANDLE hKey = NULL, hSubKey = NULL;
LONG ret;
DWORD nSubKeys, i;
ULONG valueType;
TCHAR subKeyName[MAX_ZONE_CHAR];
TCHAR szValue[MAX_ZONE_CHAR];
WCHAR stdNameInReg[MAX_ZONE_CHAR];
TziValue tempTzi;
WCHAR *stdNamePtr = tzi.StandardName;
DWORD valueSize;
DWORD timeType; DWORD timeType;
int isVista; DWORD bufSize;
DWORD val;
HANDLE hKey = NULL;
LONG ret;
ULONG valueType;
/* /*
* Get the current time zone setting of the platform. * Get the dynamic time zone information so that time zone redirection
* can be supported. (see JDK-7044727)
*/ */
timeType = GetTimeZoneInformation(&tzi); timeType = GetDynamicTimeZoneInformation(&dtzi);
if (timeType == TIME_ZONE_ID_INVALID) { if (timeType == TIME_ZONE_ID_INVALID) {
goto err; goto err;
} }
/* /*
* Determine if this is an NT system. * Make sure TimeZoneKeyName is available from the API call. If
* DynamicDaylightTime is disabled, return a custom time zone name
* based on the GMT offset. Otherwise, return the TimeZoneKeyName
* value.
*/ */
ver.dwOSVersionInfoSize = sizeof(ver); if (dtzi.TimeZoneKeyName[0] != 0) {
GetVersionEx(&ver); if (dtzi.DynamicDaylightTimeDisabled) {
isVista = ver.dwMajorVersion >= 6; customZoneName(dtzi.Bias, winZoneName);
return VALUE_GMTOFFSET;
}
wcstombs(winZoneName, dtzi.TimeZoneKeyName, MAX_ZONE_CHAR);
return VALUE_KEY;
}
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, /*
KEY_READ, (PHKEY)&hKey); * If TimeZoneKeyName is not available, check whether StandardName
if (ret == ERROR_SUCCESS) { * is available to fall back to the older API GetTimeZoneInformation.
DWORD val; * If not, directly read the value from registry keys.
DWORD bufSize; */
if (dtzi.StandardName[0] == 0) {
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,
KEY_READ, (PHKEY)&hKey);
if (ret != ERROR_SUCCESS) {
goto err;
}
/* /*
* Determine if auto-daylight time adjustment is turned off. * Determine if auto-daylight time adjustment is turned off.
*/ */
valueType = 0;
bufSize = sizeof(val); bufSize = sizeof(val);
ret = RegQueryValueExA(hKey, "DisableAutoDaylightTimeSet", ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL,
NULL, &valueType, (LPBYTE) &val, &bufSize); &valueType, (LPBYTE) &val, &bufSize);
/*
* Vista uses the different key name.
*/
if (ret != ERROR_SUCCESS) { if (ret != ERROR_SUCCESS) {
bufSize = sizeof(val); goto err;
ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled",
NULL, &valueType, (LPBYTE) &val, &bufSize);
} }
if (ret == ERROR_SUCCESS) {
int daylightSavingsUpdateDisabledOther = val == 1 && tzi.DaylightDate.wMonth != 0;
int daylightSavingsUpdateDisabledVista = val == 1;
int daylightSavingsUpdateDisabled = isVista ? daylightSavingsUpdateDisabledVista : daylightSavingsUpdateDisabledOther;
if (daylightSavingsUpdateDisabled) {
(void) RegCloseKey(hKey);
customZoneName(tzi.Bias, winZoneName);
return VALUE_GMTOFFSET;
}
}
/* /*
* Vista has the key for the current "Time Zones" entry. * Return a custom time zone name if auto-daylight time adjustment
* is disabled.
*/ */
if (isVista) { if (val == 1) {
valueType = 0; customZoneName(dtzi.Bias, winZoneName);
bufSize = MAX_ZONE_CHAR;
ret = RegQueryValueExA(hKey, "TimeZoneKeyName", NULL,
&valueType, (LPBYTE) winZoneName, &bufSize);
if (ret != ERROR_SUCCESS) {
goto err;
}
(void) RegCloseKey(hKey); (void) RegCloseKey(hKey);
return VALUE_KEY; return VALUE_GMTOFFSET;
} }
/* bufSize = MAX_ZONE_CHAR;
* Win32 problem: If the length of the standard time name is equal ret = RegQueryValueExA(hKey, "TimeZoneKeyName", NULL,
* to (or probably longer than) 32 in the registry, &valueType, (LPBYTE) winZoneName, &bufSize);
* GetTimeZoneInformation() on NT returns a null string as its if (ret != ERROR_SUCCESS) {
* standard time name. We need to work around this problem by goto err;
* getting the same information from the TimeZoneInformation
* registry. The function on Win98 seems to return its key name.
* We can't do anything in that case.
*/
if (tzi.StandardName[0] == 0) {
bufSize = sizeof(stdNameInReg);
ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType,
(LPBYTE) stdNameInReg, &bufSize);
if (ret != ERROR_SUCCESS) {
goto err;
}
stdNamePtr = stdNameInReg;
} }
(void) RegCloseKey(hKey); (void) RegCloseKey(hKey);
} return VALUE_KEY;
} else {
/*
* Open the "Time Zones" registry.
*/
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
if (ret != ERROR_SUCCESS) {
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
/* /*
* If both failed, then give up. * Fall back to GetTimeZoneInformation
*/ */
if (ret != ERROR_SUCCESS) { TIME_ZONE_INFORMATION tzi;
return VALUE_UNKNOWN; HANDLE hSubKey = NULL;
} DWORD nSubKeys, i;
} ULONG valueType;
TCHAR subKeyName[MAX_ZONE_CHAR];
TCHAR szValue[MAX_ZONE_CHAR];
WCHAR stdNameInReg[MAX_ZONE_CHAR];
TziValue tempTzi;
WCHAR *stdNamePtr = tzi.StandardName;
DWORD valueSize;
int onlyMapID;
/* timeType = GetTimeZoneInformation(&tzi);
* Get the number of subkeys of the "Time Zones" registry for if (timeType == TIME_ZONE_ID_INVALID) {
* enumeration.
*/
ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, &nSubKeys,
NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS) {
goto err;
}
/*
* Compare to the "Std" value of each subkey and find the entry that
* matches the current control panel setting.
*/
onlyMapID = 0;
for (i = 0; i < nSubKeys; ++i) {
DWORD size = sizeof(subKeyName);
ret = RegEnumKeyEx(hKey, i, subKeyName, &size, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS) {
goto err;
}
ret = RegOpenKeyEx(hKey, subKeyName, 0, KEY_READ, (PHKEY)&hSubKey);
if (ret != ERROR_SUCCESS) {
goto err; goto err;
} }
size = sizeof(szValue); ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,
ret = getValueInRegistry(hSubKey, STD_NAME, &valueType, KEY_READ, (PHKEY)&hKey);
szValue, &size); if (ret == ERROR_SUCCESS) {
if (ret != ERROR_SUCCESS) {
/* /*
* NT 4.0 SP3 fails here since it doesn't have the "Std" * Determine if auto-daylight time adjustment is turned off.
* entry in the Time Zones registry.
*/ */
RegCloseKey(hSubKey); bufSize = sizeof(val);
onlyMapID = 1; ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL,
ret = RegOpenKeyExW(hKey, stdNamePtr, 0, KEY_READ, (PHKEY)&hSubKey); &valueType, (LPBYTE) &val, &bufSize);
if (ret == ERROR_SUCCESS) {
if (val == 1 && tzi.DaylightDate.wMonth != 0) {
(void) RegCloseKey(hKey);
customZoneName(tzi.Bias, winZoneName);
return VALUE_GMTOFFSET;
}
}
/*
* Win32 problem: If the length of the standard time name is equal
* to (or probably longer than) 32 in the registry,
* GetTimeZoneInformation() on NT returns a null string as its
* standard time name. We need to work around this problem by
* getting the same information from the TimeZoneInformation
* registry.
*/
if (tzi.StandardName[0] == 0) {
bufSize = sizeof(stdNameInReg);
ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType,
(LPBYTE) stdNameInReg, &bufSize);
if (ret != ERROR_SUCCESS) {
goto err;
}
stdNamePtr = stdNameInReg;
}
(void) RegCloseKey(hKey);
}
/*
* Open the "Time Zones" registry.
*/
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
if (ret != ERROR_SUCCESS) {
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
/*
* If both failed, then give up.
*/
if (ret != ERROR_SUCCESS) {
return VALUE_UNKNOWN;
}
}
/*
* Get the number of subkeys of the "Time Zones" registry for
* enumeration.
*/
ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, &nSubKeys,
NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS) {
goto err;
}
/*
* Compare to the "Std" value of each subkey and find the entry that
* matches the current control panel setting.
*/
onlyMapID = 0;
for (i = 0; i < nSubKeys; ++i) {
DWORD size = sizeof(subKeyName);
ret = RegEnumKeyEx(hKey, i, subKeyName, &size, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS) {
goto err;
}
ret = RegOpenKeyEx(hKey, subKeyName, 0, KEY_READ, (PHKEY)&hSubKey);
if (ret != ERROR_SUCCESS) { if (ret != ERROR_SUCCESS) {
goto err; goto err;
} }
break;
}
if (wcscmp((WCHAR *)szValue, stdNamePtr) == 0) { size = sizeof(szValue);
/* ret = getValueInRegistry(hSubKey, STD_NAME, &valueType,
* Some localized Win32 platforms use a same name to szValue, &size);
* different time zones. So, we can't rely only on the name if (ret != ERROR_SUCCESS) {
* here. We need to check GMT offsets and transition dates /*
* to make sure it's the registry of the current time * NT 4.0 SP3 fails here since it doesn't have the "Std"
* zone. * entry in the Time Zones registry.
*/ */
DWORD tziValueSize = sizeof(tempTzi); RegCloseKey(hSubKey);
ret = RegQueryValueEx(hSubKey, "TZI", NULL, &valueType, onlyMapID = 1;
(unsigned char *) &tempTzi, &tziValueSize); ret = RegOpenKeyExW(hKey, stdNamePtr, 0, KEY_READ, (PHKEY)&hSubKey);
if (ret == ERROR_SUCCESS) { if (ret != ERROR_SUCCESS) {
if ((tzi.Bias != tempTzi.bias) || goto err;
(memcmp((const void *) &tzi.StandardDate,
(const void *) &tempTzi.stdDate,
sizeof(SYSTEMTIME)) != 0)) {
goto out;
} }
break;
}
if (tzi.DaylightBias != 0) { if (wcscmp((WCHAR *)szValue, stdNamePtr) == 0) {
if ((tzi.DaylightBias != tempTzi.dstBias) || /*
(memcmp((const void *) &tzi.DaylightDate, * Some localized Win32 platforms use a same name to
(const void *) &tempTzi.dstDate, * different time zones. So, we can't rely only on the name
* here. We need to check GMT offsets and transition dates
* to make sure it's the registry of the current time
* zone.
*/
DWORD tziValueSize = sizeof(tempTzi);
ret = RegQueryValueEx(hSubKey, "TZI", NULL, &valueType,
(unsigned char *) &tempTzi, &tziValueSize);
if (ret == ERROR_SUCCESS) {
if ((tzi.Bias != tempTzi.bias) ||
(memcmp((const void *) &tzi.StandardDate,
(const void *) &tempTzi.stdDate,
sizeof(SYSTEMTIME)) != 0)) { sizeof(SYSTEMTIME)) != 0)) {
goto out; goto out;
} }
if (tzi.DaylightBias != 0) {
if ((tzi.DaylightBias != tempTzi.dstBias) ||
(memcmp((const void *) &tzi.DaylightDate,
(const void *) &tempTzi.dstDate,
sizeof(SYSTEMTIME)) != 0)) {
goto out;
}
}
} }
/*
* found matched record, terminate search
*/
strcpy(winZoneName, subKeyName);
break;
} }
out:
/* (void) RegCloseKey(hSubKey);
* found matched record, terminate search
*/
strcpy(winZoneName, subKeyName);
break;
} }
out:
(void) RegCloseKey(hSubKey);
}
/*
* Get the "MapID" value of the registry to be able to eliminate
* duplicated key names later.
*/
valueSize = MAX_MAPID_LENGTH;
ret = RegQueryValueExA(hSubKey, "MapID", NULL, &valueType, winMapID, &valueSize);
(void) RegCloseKey(hSubKey);
(void) RegCloseKey(hKey);
if (ret != ERROR_SUCCESS) {
/* /*
* Vista doesn't have mapID. VALUE_UNKNOWN should be returned * Get the "MapID" value of the registry to be able to eliminate
* only for Windows NT. * duplicated key names later.
*/ */
if (onlyMapID == 1) { valueSize = MAX_MAPID_LENGTH;
return VALUE_UNKNOWN; ret = RegQueryValueExA(hSubKey, "MapID", NULL, &valueType, winMapID, &valueSize);
(void) RegCloseKey(hSubKey);
(void) RegCloseKey(hKey);
if (ret != ERROR_SUCCESS) {
/*
* Vista doesn't have mapID. VALUE_UNKNOWN should be returned
* only for Windows NT.
*/
if (onlyMapID == 1) {
return VALUE_UNKNOWN;
}
} }
} }