0N/A/*
2051N/A * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
0N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
1472N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1472N/A *
1472N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
0N/A * or visit www.oracle.com if you need additional information or have any
0N/A * questions.
0N/A */
1879N/A
1879N/A#include <windows.h>
1879N/A#include <stdio.h>
1879N/A#include <stdlib.h>
1879N/A#include "jvm.h"
1879N/A#include "TimeZone_md.h"
0N/A
0N/A#define VALUE_UNKNOWN 0
1504N/A#define VALUE_KEY 1
1504N/A#define VALUE_MAPID 2
1504N/A#define VALUE_GMTOFFSET 3
1504N/A
1504N/A#define MAX_ZONE_CHAR 256
0N/A#define MAX_MAPID_LENGTH 32
0N/A
0N/A#define NT_TZ_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"
0N/A#define WIN_TZ_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones"
0N/A#define WIN_CURRENT_TZ_KEY "System\\CurrentControlSet\\Control\\TimeZoneInformation"
0N/A
0N/Atypedef struct _TziValue {
3800N/A LONG bias;
3800N/A LONG stdBias;
3800N/A LONG dstBias;
3800N/A SYSTEMTIME stdDate;
3800N/A SYSTEMTIME dstDate;
0N/A} TziValue;
1504N/A
1504N/A/*
0N/A * Registry key names
0N/A */
1504N/Astatic void *keyNames[] = {
0N/A (void *) L"StandardName",
0N/A (void *) "StandardName",
0N/A (void *) L"Std",
0N/A (void *) "Std"
0N/A};
0N/A
0N/A/*
0N/A * Indices to keyNames[]
0N/A */
0N/A#define STANDARD_NAME 0
0N/A#define STD_NAME 2
0N/A
0N/Astatic int isNT = FALSE; /* TRUE if it is NT. */
0N/A
0N/A/*
0N/A * Calls RegQueryValueEx() to get the value for the specified key. If
0N/A * the platform is NT, 2000 or XP, it calls the Unicode
0N/A * version. Otherwise, it calls the ANSI version and converts the
0N/A * value to Unicode. In this case, it assumes that the current ANSI
0N/A * Code Page is the same as the native platform code page (e.g., Code
0N/A * Page 932 for the Japanese Windows systems.
0N/A *
0N/A * `keyIndex' is an index value to the keyNames in Unicode
0N/A * (WCHAR). `keyIndex' + 1 points to its ANSI value.
0N/A *
0N/A * Returns the status value. ERROR_SUCCESS if succeeded, a
0N/A * non-ERROR_SUCCESS value otherwise.
0N/A */
0N/Astatic LONG
0N/AgetValueInRegistry(HKEY hKey,
0N/A int keyIndex,
0N/A LPDWORD typePtr,
0N/A LPBYTE buf,
0N/A LPDWORD bufLengthPtr)
0N/A{
0N/A LONG ret;
0N/A DWORD bufLength = *bufLengthPtr;
0N/A char val[MAX_ZONE_CHAR];
0N/A DWORD valSize;
0N/A int len;
0N/A
0N/A *typePtr = 0;
0N/A if (isNT) {
0N/A ret = RegQueryValueExW(hKey, (WCHAR *) keyNames[keyIndex], NULL,
0N/A typePtr, buf, bufLengthPtr);
0N/A if (ret == ERROR_SUCCESS && *typePtr == REG_SZ) {
0N/A return ret;
0N/A }
0N/A }
0N/A
0N/A valSize = sizeof(val);
0N/A ret = RegQueryValueExA(hKey, (char *) keyNames[keyIndex + 1], NULL,
0N/A typePtr, val, &valSize);
0N/A if (ret != ERROR_SUCCESS) {
0N/A return ret;
0N/A }
0N/A if (*typePtr != REG_SZ) {
0N/A return ERROR_BADKEY;
0N/A }
0N/A
0N/A len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,
0N/A (LPCSTR) val, -1,
0N/A (LPWSTR) buf, bufLength/sizeof(WCHAR));
0N/A if (len <= 0) {
0N/A return ERROR_BADKEY;
0N/A }
0N/A return ERROR_SUCCESS;
0N/A}
0N/A
0N/A/*
0N/A * Produces custom name "GMT+hh:mm" from the given bias in buffer.
0N/A */
0N/Astatic void customZoneName(LONG bias, char *buffer) {
0N/A LONG gmtOffset;
0N/A int sign;
0N/A
0N/A if (bias > 0) {
0N/A gmtOffset = bias;
0N/A sign = -1;
0N/A } else {
0N/A gmtOffset = -bias;
0N/A sign = 1;
0N/A }
0N/A if (gmtOffset != 0) {
0N/A sprintf(buffer, "GMT%c%02d:%02d",
0N/A ((sign >= 0) ? '+' : '-'),
0N/A gmtOffset / 60,
0N/A gmtOffset % 60);
0N/A } else {
0N/A strcpy(buffer, "GMT");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Gets the current time zone entry in the "Time Zones" registry.
0N/A */
0N/Astatic int getWinTimeZone(char *winZoneName, char *winMapID)
0N/A{
0N/A TIME_ZONE_INFORMATION tzi;
0N/A OSVERSIONINFO ver;
0N/A int onlyMapID;
0N/A HANDLE hKey = NULL, hSubKey = NULL;
0N/A LONG ret;
0N/A DWORD nSubKeys, i;
0N/A ULONG valueType;
0N/A TCHAR subKeyName[MAX_ZONE_CHAR];
0N/A TCHAR szValue[MAX_ZONE_CHAR];
0N/A WCHAR stdNameInReg[MAX_ZONE_CHAR];
0N/A TziValue tempTzi;
0N/A WCHAR *stdNamePtr = tzi.StandardName;
0N/A DWORD valueSize;
0N/A DWORD timeType;
0N/A int isVista;
0N/A
0N/A /*
0N/A * Get the current time zone setting of the platform.
0N/A */
0N/A timeType = GetTimeZoneInformation(&tzi);
0N/A if (timeType == TIME_ZONE_ID_INVALID) {
0N/A goto err;
0N/A }
0N/A
0N/A /*
0N/A * Determine if this is an NT system.
0N/A */
0N/A ver.dwOSVersionInfoSize = sizeof(ver);
0N/A GetVersionEx(&ver);
0N/A isNT = ver.dwPlatformId == VER_PLATFORM_WIN32_NT;
0N/A isVista = isNT && ver.dwMajorVersion >= 6;
0N/A
0N/A ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,
0N/A KEY_READ, (PHKEY)&hKey);
0N/A if (ret == ERROR_SUCCESS) {
0N/A DWORD val;
0N/A DWORD bufSize;
0N/A
0N/A /*
0N/A * Determine if auto-daylight time adjustment is turned off.
0N/A */
0N/A valueType = 0;
0N/A bufSize = sizeof(val);
0N/A ret = RegQueryValueExA(hKey, "DisableAutoDaylightTimeSet",
0N/A NULL, &valueType, (LPBYTE) &val, &bufSize);
0N/A /*
0N/A * Vista uses the different key name.
0N/A */
0N/A if (ret != ERROR_SUCCESS) {
0N/A bufSize = sizeof(val);
0N/A ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled",
0N/A NULL, &valueType, (LPBYTE) &val, &bufSize);
0N/A }
0N/A
0N/A if (ret == ERROR_SUCCESS) {
0N/A int daylightSavingsUpdateDisabledOther = val == 1 && tzi.DaylightDate.wMonth != 0;
0N/A int daylightSavingsUpdateDisabledVista = val == 1;
0N/A int daylightSavingsUpdateDisabled = isVista ? daylightSavingsUpdateDisabledVista : daylightSavingsUpdateDisabledOther;
0N/A
0N/A if (daylightSavingsUpdateDisabled) {
0N/A (void) RegCloseKey(hKey);
0N/A customZoneName(tzi.Bias, winZoneName);
0N/A return VALUE_GMTOFFSET;
2199N/A }
0N/A }
0N/A
0N/A /*
0N/A * Vista has the key for the current "Time Zones" entry.
0N/A */
0N/A if (isVista) {
0N/A valueType = 0;
0N/A bufSize = MAX_ZONE_CHAR;
0N/A ret = RegQueryValueExA(hKey, "TimeZoneKeyName", NULL,
0N/A &valueType, (LPBYTE) winZoneName, &bufSize);
0N/A if (ret != ERROR_SUCCESS) {
0N/A goto err;
0N/A }
0N/A (void) RegCloseKey(hKey);
0N/A return VALUE_KEY;
0N/A }
0N/A
0N/A /*
0N/A * Win32 problem: If the length of the standard time name is equal
1295N/A * to (or probably longer than) 32 in the registry,
1295N/A * GetTimeZoneInformation() on NT returns a null string as its
1295N/A * standard time name. We need to work around this problem by
1295N/A * getting the same information from the TimeZoneInformation
1295N/A * registry. The function on Win98 seems to return its key name.
1295N/A * We can't do anything in that case.
1295N/A */
1295N/A if (tzi.StandardName[0] == 0) {
1295N/A bufSize = sizeof(stdNameInReg);
0N/A ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType,
0N/A (LPBYTE) stdNameInReg, &bufSize);
0N/A if (ret != ERROR_SUCCESS) {
0N/A goto err;
0N/A }
0N/A stdNamePtr = stdNameInReg;
0N/A }
0N/A (void) RegCloseKey(hKey);
0N/A }
0N/A
0N/A /*
0N/A * Open the "Time Zones" registry.
0N/A */
0N/A ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
0N/A if (ret != ERROR_SUCCESS) {
0N/A ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
0N/A /*
0N/A * If both failed, then give up.
0N/A */
0N/A if (ret != ERROR_SUCCESS) {
0N/A return VALUE_UNKNOWN;
0N/A }
1739N/A }
0N/A
0N/A /*
0N/A * Get the number of subkeys of the "Time Zones" registry for
0N/A * enumeration.
0N/A */
0N/A ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, &nSubKeys,
0N/A NULL, NULL, NULL, NULL, NULL, NULL, NULL);
0N/A if (ret != ERROR_SUCCESS) {
0N/A goto err;
0N/A }
0N/A
0N/A /*
0N/A * Compare to the "Std" value of each subkey and find the entry that
0N/A * matches the current control panel setting.
0N/A */
0N/A onlyMapID = 0;
0N/A for (i = 0; i < nSubKeys; ++i) {
0N/A DWORD size = sizeof(subKeyName);
0N/A ret = RegEnumKeyEx(hKey, i, subKeyName, &size, NULL, NULL, NULL, NULL);
0N/A if (ret != ERROR_SUCCESS) {
0N/A goto err;
0N/A }
0N/A ret = RegOpenKeyEx(hKey, subKeyName, 0, KEY_READ, (PHKEY)&hSubKey);
0N/A if (ret != ERROR_SUCCESS) {
0N/A goto err;
0N/A }
0N/A
0N/A size = sizeof(szValue);
0N/A ret = getValueInRegistry(hSubKey, STD_NAME, &valueType,
0N/A szValue, &size);
0N/A if (ret != ERROR_SUCCESS) {
0N/A /*
0N/A * NT 4.0 SP3 fails here since it doesn't have the "Std"
0N/A * entry in the Time Zones registry.
0N/A */
0N/A RegCloseKey(hSubKey);
0N/A onlyMapID = 1;
0N/A ret = RegOpenKeyExW(hKey, stdNamePtr, 0, KEY_READ, (PHKEY)&hSubKey);
0N/A if (ret != ERROR_SUCCESS) {
0N/A goto err;
0N/A }
0N/A break;
0N/A }
0N/A
0N/A if (wcscmp((WCHAR *)szValue, stdNamePtr) == 0) {
0N/A /*
0N/A * Some localized Win32 platforms use a same name to
0N/A * different time zones. So, we can't rely only on the name
0N/A * here. We need to check GMT offsets and transition dates
0N/A * to make sure it's the registry of the current time
0N/A * zone.
0N/A */
0N/A DWORD tziValueSize = sizeof(tempTzi);
0N/A ret = RegQueryValueEx(hSubKey, "TZI", NULL, &valueType,
0N/A (unsigned char *) &tempTzi, &tziValueSize);
0N/A if (ret == ERROR_SUCCESS) {
0N/A if ((tzi.Bias != tempTzi.bias) ||
0N/A (memcmp((const void *) &tzi.StandardDate,
0N/A (const void *) &tempTzi.stdDate,
0N/A sizeof(SYSTEMTIME)) != 0)) {
0N/A goto out;
0N/A }
0N/A
0N/A if (tzi.DaylightBias != 0) {
0N/A if ((tzi.DaylightBias != tempTzi.dstBias) ||
0N/A (memcmp((const void *) &tzi.DaylightDate,
0N/A (const void *) &tempTzi.dstDate,
0N/A sizeof(SYSTEMTIME)) != 0)) {
0N/A goto out;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * found matched record, terminate search
0N/A */
0N/A strcpy(winZoneName, subKeyName);
0N/A break;
0N/A }
0N/A out:
0N/A (void) RegCloseKey(hSubKey);
0N/A }
0N/A
0N/A /*
0N/A * Get the "MapID" value of the registry to be able to eliminate
0N/A * duplicated key names later.
0N/A */
0N/A valueSize = MAX_MAPID_LENGTH;
0N/A ret = RegQueryValueExA(hSubKey, "MapID", NULL, &valueType, winMapID, &valueSize);
0N/A (void) RegCloseKey(hSubKey);
0N/A (void) RegCloseKey(hKey);
0N/A
0N/A if (ret != ERROR_SUCCESS) {
0N/A /*
0N/A * Vista doesn't have mapID. VALUE_UNKNOWN should be returned
0N/A * only for Windows NT.
0N/A */
0N/A if (onlyMapID == 1) {
0N/A return VALUE_UNKNOWN;
0N/A }
0N/A }
0N/A
0N/A return VALUE_KEY;
0N/A
0N/A err:
0N/A if (hKey != NULL) {
0N/A (void) RegCloseKey(hKey);
0N/A }
0N/A return VALUE_UNKNOWN;
0N/A}
0N/A
0N/A/*
0N/A * The mapping table file name.
0N/A */
0N/A#define MAPPINGS_FILE "\\lib\\tzmappings"
0N/A
0N/A/*
0N/A * Index values for the mapping table.
0N/A */
0N/A#define TZ_WIN_NAME 0
0N/A#define TZ_MAPID 1
0N/A#define TZ_REGION 2
0N/A#define TZ_JAVA_NAME 3
0N/A
0N/A#define TZ_NITEMS 4 /* number of items (fields) */
0N/A
0N/A/*
0N/A * Looks up the mapping table (tzmappings) and returns a Java time
0N/A * zone ID (e.g., "America/Los_Angeles") if found. Otherwise, NULL is
0N/A * returned.
0N/A *
0N/A * value_type is one of the following values:
0N/A * VALUE_KEY for exact key matching
0N/A * VALUE_MAPID for MapID and country-based mapping (this is
0N/A * required for the old Windows, such as NT 4.0 SP3).
0N/A */
0N/Astatic char *matchJavaTZ(const char *java_home_dir, int value_type, char *tzName,
0N/A char *mapID, const char *country)
0N/A{
0N/A int line;
0N/A int IDmatched = 0;
0N/A FILE *fp;
0N/A char *javaTZName = NULL;
0N/A char *items[TZ_NITEMS];
0N/A char mapFileName[_MAX_PATH + 1];
0N/A char lineBuffer[MAX_ZONE_CHAR * 4];
0N/A char bestMatch[MAX_ZONE_CHAR];
0N/A int noMapID = *mapID == '\0'; /* no mapID on Vista */
0N/A
0N/A bestMatch[0] = '\0';
0N/A
0N/A strcpy(mapFileName, java_home_dir);
0N/A strcat(mapFileName, MAPPINGS_FILE);
0N/A
0N/A if ((fp = fopen(mapFileName, "r")) == NULL) {
0N/A jio_fprintf(stderr, "can't open %s.\n", mapFileName);
0N/A return NULL;
0N/A }
0N/A
0N/A line = 0;
0N/A while (fgets(lineBuffer, sizeof(lineBuffer), fp) != NULL) {
0N/A char *start, *idx, *endp;
0N/A int itemIndex = 0;
0N/A
0N/A line++;
0N/A start = idx = lineBuffer;
0N/A endp = &lineBuffer[sizeof(lineBuffer)];
0N/A
0N/A /*
0N/A * Ignore comment and blank lines.
0N/A */
0N/A if (*idx == '#' || *idx == '\n') {
0N/A continue;
0N/A }
0N/A
0N/A for (itemIndex = 0; itemIndex < TZ_NITEMS; itemIndex++) {
0N/A items[itemIndex] = start;
0N/A while (*idx && *idx != ':') {
0N/A if (++idx >= endp) {
0N/A goto illegal_format;
0N/A }
0N/A }
0N/A if (*idx == '\0') {
0N/A goto illegal_format;
0N/A }
0N/A *idx++ = '\0';
0N/A start = idx;
0N/A }
0N/A
0N/A if (*idx != '\n') {
3802N/A goto illegal_format;
3802N/A }
3802N/A
3802N/A if (noMapID || strcmp(mapID, items[TZ_MAPID]) == 0) {
3802N/A /*
3802N/A * When there's no mapID, we need to scan items until the
3802N/A * exact match is found or the end of data is detected.
3802N/A */
3802N/A if (!noMapID) {
3802N/A IDmatched = 1;
3803N/A }
3802N/A if (strcmp(items[TZ_WIN_NAME], tzName) == 0) {
3802N/A /*
3802N/A * Found the time zone in the mapping table.
3802N/A */
3802N/A javaTZName = _strdup(items[TZ_JAVA_NAME]);
3802N/A break;
3802N/A }
3802N/A /*
3802N/A * Try to find the most likely time zone.
3802N/A */
3802N/A if (*items[TZ_REGION] == '\0') {
0N/A strncpy(bestMatch, items[TZ_JAVA_NAME], MAX_ZONE_CHAR);
0N/A } else if (country != NULL && strcmp(items[TZ_REGION], country) == 0) {
0N/A if (value_type == VALUE_MAPID) {
0N/A javaTZName = _strdup(items[TZ_JAVA_NAME]);
0N/A break;
0N/A }
0N/A strncpy(bestMatch, items[TZ_JAVA_NAME], MAX_ZONE_CHAR);
0N/A }
0N/A } else {
0N/A if (IDmatched == 1) {
0N/A /*
0N/A * No need to look up the mapping table further.
0N/A */
0N/A break;
0N/A }
0N/A }
0N/A }
0N/A fclose(fp);
0N/A
0N/A if (javaTZName == NULL && bestMatch[0] != '\0') {
0N/A javaTZName = _strdup(bestMatch);
0N/A }
0N/A return javaTZName;
0N/A
0N/A illegal_format:
0N/A (void) fclose(fp);
0N/A jio_fprintf(stderr, "tzmappings: Illegal format at line %d.\n", line);
0N/A return NULL;
0N/A}
0N/A
0N/A/*
0N/A * Detects the platform time zone which maps to a Java time zone ID.
0N/A */
0N/Achar *findJavaTZ_md(const char *java_home_dir, const char *country)
0N/A{
0N/A char winZoneName[MAX_ZONE_CHAR];
0N/A char winMapID[MAX_MAPID_LENGTH];
0N/A char *std_timezone = NULL;
0N/A int result;
0N/A
0N/A winMapID[0] = 0;
0N/A result = getWinTimeZone(winZoneName, winMapID);
0N/A
0N/A if (result != VALUE_UNKNOWN) {
0N/A if (result == VALUE_GMTOFFSET) {
0N/A std_timezone = _strdup(winZoneName);
0N/A } else {
0N/A std_timezone = matchJavaTZ(java_home_dir, result,
0N/A winZoneName, winMapID, country);
0N/A }
0N/A }
0N/A
0N/A return std_timezone;
0N/A}
0N/A
0N/A/**
0N/A * Returns a GMT-offset-based time zone ID. On Win32, it always return
0N/A * NULL since the fall back is performed in getWinTimeZone().
0N/A */
0N/Achar *
0N/AgetGMTOffsetID()
0N/A{
0N/A return NULL;
0N/A}
0N/A