Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/native/java/util/TimeZone_md.c
32287 views
/*1* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <windows.h>26#include <stdio.h>27#include <stdlib.h>28#include "jvm.h"29#include "TimeZone_md.h"30#include "jdk_util.h"3132#define VALUE_UNKNOWN 033#define VALUE_KEY 134#define VALUE_MAPID 235#define VALUE_GMTOFFSET 33637#define MAX_ZONE_CHAR 25638#define MAX_MAPID_LENGTH 323940#define NT_TZ_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"41#define WIN_TZ_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones"42#define WIN_CURRENT_TZ_KEY "System\\CurrentControlSet\\Control\\TimeZoneInformation"4344typedef struct _TziValue {45LONG bias;46LONG stdBias;47LONG dstBias;48SYSTEMTIME stdDate;49SYSTEMTIME dstDate;50} TziValue;5152#if _WIN32_WINNT < 0x0600 /* < _WIN32_WINNT_VISTA */53typedef struct _TIME_DYNAMIC_ZONE_INFORMATION {54LONG Bias;55WCHAR StandardName[32];56SYSTEMTIME StandardDate;57LONG StandardBias;58WCHAR DaylightName[32];59SYSTEMTIME DaylightDate;60LONG DaylightBias;61WCHAR TimeZoneKeyName[128];62BOOLEAN DynamicDaylightTimeDisabled;63} DYNAMIC_TIME_ZONE_INFORMATION, *PDYNAMIC_TIME_ZONE_INFORMATION;64#endif6566/*67* Registry key names68*/69static void *keyNames[] = {70(void *) L"StandardName",71(void *) "StandardName",72(void *) L"Std",73(void *) "Std"74};7576/*77* Indices to keyNames[]78*/79#define STANDARD_NAME 080#define STD_NAME 28182/*83* Calls RegQueryValueEx() to get the value for the specified key. If84* the platform is NT, 2000 or XP, it calls the Unicode85* version. Otherwise, it calls the ANSI version and converts the86* value to Unicode. In this case, it assumes that the current ANSI87* Code Page is the same as the native platform code page (e.g., Code88* Page 932 for the Japanese Windows systems.89*90* `keyIndex' is an index value to the keyNames in Unicode91* (WCHAR). `keyIndex' + 1 points to its ANSI value.92*93* Returns the status value. ERROR_SUCCESS if succeeded, a94* non-ERROR_SUCCESS value otherwise.95*/96static LONG97getValueInRegistry(HKEY hKey,98int keyIndex,99LPDWORD typePtr,100LPBYTE buf,101LPDWORD bufLengthPtr)102{103LONG ret;104DWORD bufLength = *bufLengthPtr;105char val[MAX_ZONE_CHAR];106DWORD valSize;107int len;108109*typePtr = 0;110ret = RegQueryValueExW(hKey, (WCHAR *) keyNames[keyIndex], NULL,111typePtr, buf, bufLengthPtr);112if (ret == ERROR_SUCCESS && *typePtr == REG_SZ) {113return ret;114}115116valSize = sizeof(val);117ret = RegQueryValueExA(hKey, (char *) keyNames[keyIndex + 1], NULL,118typePtr, val, &valSize);119if (ret != ERROR_SUCCESS) {120return ret;121}122if (*typePtr != REG_SZ) {123return ERROR_BADKEY;124}125126len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,127(LPCSTR) val, -1,128(LPWSTR) buf, bufLength/sizeof(WCHAR));129if (len <= 0) {130return ERROR_BADKEY;131}132return ERROR_SUCCESS;133}134135/*136* Produces custom name "GMT+hh:mm" from the given bias in buffer.137*/138static void customZoneName(LONG bias, char *buffer) {139LONG gmtOffset;140int sign;141142if (bias > 0) {143gmtOffset = bias;144sign = -1;145} else {146gmtOffset = -bias;147sign = 1;148}149if (gmtOffset != 0) {150sprintf(buffer, "GMT%c%02d:%02d",151((sign >= 0) ? '+' : '-'),152gmtOffset / 60,153gmtOffset % 60);154} else {155strcpy(buffer, "GMT");156}157}158159/*160* Use NO_DYNAMIC_TIME_ZONE_INFO as the return value indicating that no161* dynamic time zone information is available.162*/163#define NO_DYNAMIC_TIME_ZONE_INFO (-128)164165static int getDynamicTimeZoneInfo(PDYNAMIC_TIME_ZONE_INFORMATION pdtzi) {166DWORD timeType = NO_DYNAMIC_TIME_ZONE_INFO;167HMODULE dllHandle;168169/*170* Dynamically load the dll to call GetDynamicTimeZoneInformation.171*/172dllHandle = JDK_LoadSystemLibrary("Kernel32.dll");173if (dllHandle != NULL) {174typedef DWORD (WINAPI *GetDynamicTimezoneInfoType)(PDYNAMIC_TIME_ZONE_INFORMATION);175GetDynamicTimezoneInfoType getDynamicTimeZoneInfoFunc =176(GetDynamicTimezoneInfoType) GetProcAddress(dllHandle,177"GetDynamicTimeZoneInformation");178179if (getDynamicTimeZoneInfo != NULL) {180timeType = getDynamicTimeZoneInfoFunc(pdtzi);181}182}183return timeType;184}185186/*187* Gets the current time zone entry in the "Time Zones" registry.188*/189static int getWinTimeZone(char *winZoneName, char *winMapID)190{191TIME_ZONE_INFORMATION tzi;192OSVERSIONINFO ver;193int onlyMapID;194HANDLE hKey = NULL, hSubKey = NULL;195LONG ret;196DWORD nSubKeys, i;197ULONG valueType;198TCHAR subKeyName[MAX_ZONE_CHAR];199TCHAR szValue[MAX_ZONE_CHAR];200WCHAR stdNameInReg[MAX_ZONE_CHAR];201TziValue tempTzi;202WCHAR *stdNamePtr = tzi.StandardName;203DWORD valueSize;204DWORD timeType;205int isVistaOrLater;206207/*208* Determine if this is a Vista or later.209*/210ver.dwOSVersionInfoSize = sizeof(ver);211GetVersionEx(&ver);212isVistaOrLater = (ver.dwMajorVersion >= 6);213214if (isVistaOrLater) {215DYNAMIC_TIME_ZONE_INFORMATION dtzi;216DWORD bufSize;217DWORD val;218219/*220* Get the dynamic time zone information, if available, so that time221* zone redirection can be supported. (see JDK-7044727)222*/223timeType = getDynamicTimeZoneInfo(&dtzi);224if (timeType == TIME_ZONE_ID_INVALID) {225goto err;226}227228if (timeType != NO_DYNAMIC_TIME_ZONE_INFO) {229/*230* Make sure TimeZoneKeyName is available from the API call. If231* DynamicDaylightTime is disabled, return a custom time zone name232* based on the GMT offset. Otherwise, return the TimeZoneKeyName233* value.234*/235if (dtzi.TimeZoneKeyName[0] != 0) {236if (dtzi.DynamicDaylightTimeDisabled) {237customZoneName(dtzi.Bias, winZoneName);238return VALUE_GMTOFFSET;239}240wcstombs(winZoneName, dtzi.TimeZoneKeyName, MAX_ZONE_CHAR);241return VALUE_KEY;242}243244/*245* If TimeZoneKeyName is not available, check whether StandardName246* is available to fall back to the older API GetTimeZoneInformation.247* If not, directly read the value from registry keys.248*/249if (dtzi.StandardName[0] == 0) {250ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,251KEY_READ, (PHKEY)&hKey);252if (ret != ERROR_SUCCESS) {253goto err;254}255256/*257* Determine if auto-daylight time adjustment is turned off.258*/259bufSize = sizeof(val);260ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL,261&valueType, (LPBYTE) &val, &bufSize);262if (ret != ERROR_SUCCESS) {263goto err;264}265/*266* Return a custom time zone name if auto-daylight time267* adjustment is disabled.268*/269if (val == 1) {270customZoneName(dtzi.Bias, winZoneName);271(void) RegCloseKey(hKey);272return VALUE_GMTOFFSET;273}274275bufSize = MAX_ZONE_CHAR;276ret = RegQueryValueExA(hKey, "TimeZoneKeyName",NULL,277&valueType, (LPBYTE)winZoneName, &bufSize);278if (ret != ERROR_SUCCESS) {279goto err;280}281(void) RegCloseKey(hKey);282return VALUE_KEY;283}284}285}286287/*288* Fall back to GetTimeZoneInformation289*/290timeType = GetTimeZoneInformation(&tzi);291if (timeType == TIME_ZONE_ID_INVALID) {292goto err;293}294295ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,296KEY_READ, (PHKEY)&hKey);297if (ret == ERROR_SUCCESS) {298DWORD val;299DWORD bufSize;300301/*302* Determine if auto-daylight time adjustment is turned off.303*/304bufSize = sizeof(val);305ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL,306&valueType, (LPBYTE) &val, &bufSize);307if (ret != ERROR_SUCCESS) {308/*309* Try the old key name.310*/311bufSize = sizeof(val);312ret = RegQueryValueExA(hKey, "DisableAutoDaylightTimeSet", NULL,313&valueType, (LPBYTE) &val, &bufSize);314}315316if (ret == ERROR_SUCCESS) {317int daylightSavingsUpdateDisabledOther = (val == 1 && tzi.DaylightDate.wMonth != 0);318int daylightSavingsUpdateDisabledVista = (val == 1);319int daylightSavingsUpdateDisabled320= (isVistaOrLater ? daylightSavingsUpdateDisabledVista : daylightSavingsUpdateDisabledOther);321322if (daylightSavingsUpdateDisabled) {323(void) RegCloseKey(hKey);324customZoneName(tzi.Bias, winZoneName);325return VALUE_GMTOFFSET;326}327}328329/*330* Win32 problem: If the length of the standard time name is equal331* to (or probably longer than) 32 in the registry,332* GetTimeZoneInformation() on NT returns a null string as its333* standard time name. We need to work around this problem by334* getting the same information from the TimeZoneInformation335* registry.336*/337if (tzi.StandardName[0] == 0) {338bufSize = sizeof(stdNameInReg);339ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType,340(LPBYTE) stdNameInReg, &bufSize);341if (ret != ERROR_SUCCESS) {342goto err;343}344stdNamePtr = stdNameInReg;345}346(void) RegCloseKey(hKey);347}348349/*350* Open the "Time Zones" registry.351*/352ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);353if (ret != ERROR_SUCCESS) {354ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);355/*356* If both failed, then give up.357*/358if (ret != ERROR_SUCCESS) {359return VALUE_UNKNOWN;360}361}362363/*364* Get the number of subkeys of the "Time Zones" registry for365* enumeration.366*/367ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, &nSubKeys,368NULL, NULL, NULL, NULL, NULL, NULL, NULL);369if (ret != ERROR_SUCCESS) {370goto err;371}372373/*374* Compare to the "Std" value of each subkey and find the entry that375* matches the current control panel setting.376*/377onlyMapID = 0;378for (i = 0; i < nSubKeys; ++i) {379DWORD size = sizeof(subKeyName);380ret = RegEnumKeyEx(hKey, i, subKeyName, &size, NULL, NULL, NULL, NULL);381if (ret != ERROR_SUCCESS) {382goto err;383}384ret = RegOpenKeyEx(hKey, subKeyName, 0, KEY_READ, (PHKEY)&hSubKey);385if (ret != ERROR_SUCCESS) {386goto err;387}388389size = sizeof(szValue);390ret = getValueInRegistry(hSubKey, STD_NAME, &valueType,391szValue, &size);392if (ret != ERROR_SUCCESS) {393/*394* NT 4.0 SP3 fails here since it doesn't have the "Std"395* entry in the Time Zones registry.396*/397RegCloseKey(hSubKey);398onlyMapID = 1;399ret = RegOpenKeyExW(hKey, stdNamePtr, 0, KEY_READ, (PHKEY)&hSubKey);400if (ret != ERROR_SUCCESS) {401goto err;402}403break;404}405406if (wcscmp((WCHAR *)szValue, stdNamePtr) == 0) {407/*408* Some localized Win32 platforms use a same name to409* different time zones. So, we can't rely only on the name410* here. We need to check GMT offsets and transition dates411* to make sure it's the registry of the current time412* zone.413*/414DWORD tziValueSize = sizeof(tempTzi);415ret = RegQueryValueEx(hSubKey, "TZI", NULL, &valueType,416(unsigned char *) &tempTzi, &tziValueSize);417if (ret == ERROR_SUCCESS) {418if ((tzi.Bias != tempTzi.bias) ||419(memcmp((const void *) &tzi.StandardDate,420(const void *) &tempTzi.stdDate,421sizeof(SYSTEMTIME)) != 0)) {422goto out;423}424425if (tzi.DaylightBias != 0) {426if ((tzi.DaylightBias != tempTzi.dstBias) ||427(memcmp((const void *) &tzi.DaylightDate,428(const void *) &tempTzi.dstDate,429sizeof(SYSTEMTIME)) != 0)) {430goto out;431}432}433}434435/*436* found matched record, terminate search437*/438strcpy(winZoneName, subKeyName);439break;440}441out:442(void) RegCloseKey(hSubKey);443}444445/*446* Get the "MapID" value of the registry to be able to eliminate447* duplicated key names later.448*/449valueSize = MAX_MAPID_LENGTH;450ret = RegQueryValueExA(hSubKey, "MapID", NULL, &valueType, winMapID, &valueSize);451(void) RegCloseKey(hSubKey);452(void) RegCloseKey(hKey);453454if (ret != ERROR_SUCCESS) {455/*456* Vista doesn't have mapID. VALUE_UNKNOWN should be returned457* only for Windows NT.458*/459if (onlyMapID == 1) {460return VALUE_UNKNOWN;461}462}463464return VALUE_KEY;465466err:467if (hKey != NULL) {468(void) RegCloseKey(hKey);469}470return VALUE_UNKNOWN;471}472473/*474* The mapping table file name.475*/476#define MAPPINGS_FILE "\\lib\\tzmappings"477478/*479* Index values for the mapping table.480*/481#define TZ_WIN_NAME 0482#define TZ_MAPID 1483#define TZ_REGION 2484#define TZ_JAVA_NAME 3485486#define TZ_NITEMS 4 /* number of items (fields) */487488/*489* Looks up the mapping table (tzmappings) and returns a Java time490* zone ID (e.g., "America/Los_Angeles") if found. Otherwise, NULL is491* returned.492*493* value_type is one of the following values:494* VALUE_KEY for exact key matching495* VALUE_MAPID for MapID (this is496* required for the old Windows, such as NT 4.0 SP3).497*/498static char *matchJavaTZ(const char *java_home_dir, int value_type, char *tzName,499char *mapID)500{501int line;502int IDmatched = 0;503FILE *fp;504char *javaTZName = NULL;505char *items[TZ_NITEMS];506char *mapFileName;507char lineBuffer[MAX_ZONE_CHAR * 4];508int noMapID = *mapID == '\0'; /* no mapID on Vista and later */509510mapFileName = malloc(strlen(java_home_dir) + strlen(MAPPINGS_FILE) + 1);511if (mapFileName == NULL) {512return NULL;513}514strcpy(mapFileName, java_home_dir);515strcat(mapFileName, MAPPINGS_FILE);516517if ((fp = fopen(mapFileName, "r")) == NULL) {518jio_fprintf(stderr, "can't open %s.\n", mapFileName);519free((void *) mapFileName);520return NULL;521}522free((void *) mapFileName);523524line = 0;525while (fgets(lineBuffer, sizeof(lineBuffer), fp) != NULL) {526char *start, *idx, *endp;527int itemIndex = 0;528529line++;530start = idx = lineBuffer;531endp = &lineBuffer[sizeof(lineBuffer)];532533/*534* Ignore comment and blank lines.535*/536if (*idx == '#' || *idx == '\n') {537continue;538}539540for (itemIndex = 0; itemIndex < TZ_NITEMS; itemIndex++) {541items[itemIndex] = start;542while (*idx && *idx != ':') {543if (++idx >= endp) {544goto illegal_format;545}546}547if (*idx == '\0') {548goto illegal_format;549}550*idx++ = '\0';551start = idx;552}553554if (*idx != '\n') {555goto illegal_format;556}557558if (noMapID || strcmp(mapID, items[TZ_MAPID]) == 0) {559/*560* When there's no mapID, we need to scan items until the561* exact match is found or the end of data is detected.562*/563if (!noMapID) {564IDmatched = 1;565}566if (strcmp(items[TZ_WIN_NAME], tzName) == 0) {567/*568* Found the time zone in the mapping table.569*/570javaTZName = _strdup(items[TZ_JAVA_NAME]);571break;572}573} else {574if (IDmatched == 1) {575/*576* No need to look up the mapping table further.577*/578break;579}580}581}582fclose(fp);583584return javaTZName;585586illegal_format:587(void) fclose(fp);588jio_fprintf(stderr, "tzmappings: Illegal format at line %d.\n", line);589return NULL;590}591592/*593* Detects the platform time zone which maps to a Java time zone ID.594*/595char *findJavaTZ_md(const char *java_home_dir)596{597char winZoneName[MAX_ZONE_CHAR];598char winMapID[MAX_MAPID_LENGTH];599char *std_timezone = NULL;600int result;601602winMapID[0] = 0;603result = getWinTimeZone(winZoneName, winMapID);604605if (result != VALUE_UNKNOWN) {606if (result == VALUE_GMTOFFSET) {607std_timezone = _strdup(winZoneName);608} else {609std_timezone = matchJavaTZ(java_home_dir, result,610winZoneName, winMapID);611if (std_timezone == NULL) {612std_timezone = getGMTOffsetID();613}614}615}616return std_timezone;617}618619/**620* Returns a GMT-offset-based time zone ID.621*/622char *623getGMTOffsetID()624{625LONG bias = 0;626LONG ret;627HANDLE hKey = NULL;628char zonename[32];629630// Obtain the current GMT offset value of ActiveTimeBias.631ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,632KEY_READ, (PHKEY)&hKey);633if (ret == ERROR_SUCCESS) {634DWORD val;635DWORD bufSize = sizeof(val);636ULONG valueType = 0;637ret = RegQueryValueExA(hKey, "ActiveTimeBias",638NULL, &valueType, (LPBYTE) &val, &bufSize);639if (ret == ERROR_SUCCESS) {640bias = (LONG) val;641}642(void) RegCloseKey(hKey);643}644645// If we can't get the ActiveTimeBias value, use Bias of TimeZoneInformation.646// Note: Bias doesn't reflect current daylight saving.647if (ret != ERROR_SUCCESS) {648TIME_ZONE_INFORMATION tzi;649if (GetTimeZoneInformation(&tzi) != TIME_ZONE_ID_INVALID) {650bias = tzi.Bias;651}652}653654customZoneName(bias, zonename);655return _strdup(zonename);656}657658659