2
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
30
#include "TimeZone_md.h"
32
#define VALUE_UNKNOWN 0
35
#define VALUE_GMTOFFSET 3
37
#define MAX_ZONE_CHAR 256
38
#define MAX_MAPID_LENGTH 32
39
#define MAX_REGION_LENGTH 4
41
#define NT_TZ_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"
42
#define WIN_TZ_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones"
43
#define WIN_CURRENT_TZ_KEY "System\\CurrentControlSet\\Control\\TimeZoneInformation"
45
typedef struct _TziValue {
56
static void *keyNames[] = {
57
(void *) L"StandardName",
58
(void *) "StandardName",
64
* Indices to keyNames[]
66
#define STANDARD_NAME 0
70
* Calls RegQueryValueEx() to get the value for the specified key. If
71
* the platform is NT, 2000 or XP, it calls the Unicode
72
* version. Otherwise, it calls the ANSI version and converts the
73
* value to Unicode. In this case, it assumes that the current ANSI
74
* Code Page is the same as the native platform code page (e.g., Code
75
* Page 932 for the Japanese Windows systems.
77
* `keyIndex' is an index value to the keyNames in Unicode
78
* (WCHAR). `keyIndex' + 1 points to its ANSI value.
80
* Returns the status value. ERROR_SUCCESS if succeeded, a
81
* non-ERROR_SUCCESS value otherwise.
84
getValueInRegistry(HKEY hKey,
91
DWORD bufLength = *bufLengthPtr;
92
char val[MAX_ZONE_CHAR];
97
ret = RegQueryValueExW(hKey, (WCHAR *) keyNames[keyIndex], NULL,
98
typePtr, buf, bufLengthPtr);
99
if (ret == ERROR_SUCCESS && *typePtr == REG_SZ) {
103
valSize = sizeof(val);
104
ret = RegQueryValueExA(hKey, (char *) keyNames[keyIndex + 1], NULL,
105
typePtr, val, &valSize);
106
if (ret != ERROR_SUCCESS) {
109
if (*typePtr != REG_SZ) {
113
len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,
115
(LPWSTR) buf, bufLength/sizeof(WCHAR));
119
return ERROR_SUCCESS;
123
* Produces custom name "GMT+hh:mm" from the given bias in buffer.
125
static void customZoneName(LONG bias, char *buffer, size_t bufSize) {
136
if (gmtOffset != 0) {
137
snprintf(buffer, bufSize, "GMT%c%02d:%02d",
138
((sign >= 0) ? '+' : '-'),
142
strcpy(buffer, "GMT");
147
* Gets the current time zone entry in the "Time Zones" registry.
149
static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize)
151
DYNAMIC_TIME_ZONE_INFORMATION dtzi;
160
* Get the dynamic time zone information so that time zone redirection
161
* can be supported. (see JDK-7044727)
163
timeType = GetDynamicTimeZoneInformation(&dtzi);
164
if (timeType == TIME_ZONE_ID_INVALID) {
169
* Make sure TimeZoneKeyName is available from the API call. If
170
* DynamicDaylightTime is disabled, return a custom time zone name
171
* based on the GMT offset. Otherwise, return the TimeZoneKeyName
174
if (dtzi.TimeZoneKeyName[0] != 0) {
175
if (dtzi.DynamicDaylightTimeDisabled) {
176
customZoneName(dtzi.Bias, winZoneName, winZoneNameBufSize);
177
return VALUE_GMTOFFSET;
179
wcstombs(winZoneName, dtzi.TimeZoneKeyName, MAX_ZONE_CHAR);
184
* If TimeZoneKeyName is not available, check whether StandardName
185
* is available to fall back to the older API GetTimeZoneInformation.
186
* If not, directly read the value from registry keys.
188
if (dtzi.StandardName[0] == 0) {
189
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,
190
KEY_READ, (PHKEY)&hKey);
191
if (ret != ERROR_SUCCESS) {
196
* Determine if auto-daylight time adjustment is turned off.
198
bufSize = sizeof(val);
199
ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL,
200
&valueType, (LPBYTE) &val, &bufSize);
201
if (ret != ERROR_SUCCESS) {
205
* Return a custom time zone name if auto-daylight time adjustment
209
customZoneName(dtzi.Bias, winZoneName, winZoneNameBufSize);
210
(void) RegCloseKey(hKey);
211
return VALUE_GMTOFFSET;
214
bufSize = MAX_ZONE_CHAR;
215
ret = RegQueryValueExA(hKey, "TimeZoneKeyName", NULL,
216
&valueType, (LPBYTE) winZoneName, &bufSize);
217
if (ret != ERROR_SUCCESS) {
220
(void) RegCloseKey(hKey);
224
* Fall back to GetTimeZoneInformation
226
TIME_ZONE_INFORMATION tzi;
227
HANDLE hSubKey = NULL;
230
TCHAR subKeyName[MAX_ZONE_CHAR];
231
TCHAR szValue[MAX_ZONE_CHAR];
232
WCHAR stdNameInReg[MAX_ZONE_CHAR];
234
WCHAR *stdNamePtr = tzi.StandardName;
237
timeType = GetTimeZoneInformation(&tzi);
238
if (timeType == TIME_ZONE_ID_INVALID) {
242
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,
243
KEY_READ, (PHKEY)&hKey);
244
if (ret == ERROR_SUCCESS) {
246
* Determine if auto-daylight time adjustment is turned off.
248
bufSize = sizeof(val);
249
ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL,
250
&valueType, (LPBYTE) &val, &bufSize);
251
if (ret == ERROR_SUCCESS) {
252
if (val == 1 && tzi.DaylightDate.wMonth != 0) {
253
(void) RegCloseKey(hKey);
254
customZoneName(tzi.Bias, winZoneName, winZoneNameBufSize);
255
return VALUE_GMTOFFSET;
260
* Win32 problem: If the length of the standard time name is equal
261
* to (or probably longer than) 32 in the registry,
262
* GetTimeZoneInformation() on NT returns a null string as its
263
* standard time name. We need to work around this problem by
264
* getting the same information from the TimeZoneInformation
267
if (tzi.StandardName[0] == 0) {
268
bufSize = sizeof(stdNameInReg);
269
ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType,
270
(LPBYTE) stdNameInReg, &bufSize);
271
if (ret != ERROR_SUCCESS) {
274
stdNamePtr = stdNameInReg;
276
(void) RegCloseKey(hKey);
280
* Open the "Time Zones" registry.
282
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
283
if (ret != ERROR_SUCCESS) {
284
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
286
* If both failed, then give up.
288
if (ret != ERROR_SUCCESS) {
289
return VALUE_UNKNOWN;
294
* Get the number of subkeys of the "Time Zones" registry for
297
ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, &nSubKeys,
298
NULL, NULL, NULL, NULL, NULL, NULL, NULL);
299
if (ret != ERROR_SUCCESS) {
304
* Compare to the "Std" value of each subkey and find the entry that
305
* matches the current control panel setting.
308
for (i = 0; i < nSubKeys; ++i) {
309
DWORD size = sizeof(subKeyName);
310
ret = RegEnumKeyEx(hKey, i, subKeyName, &size, NULL, NULL, NULL, NULL);
311
if (ret != ERROR_SUCCESS) {
314
ret = RegOpenKeyEx(hKey, subKeyName, 0, KEY_READ, (PHKEY)&hSubKey);
315
if (ret != ERROR_SUCCESS) {
319
size = sizeof(szValue);
320
ret = getValueInRegistry(hSubKey, STD_NAME, &valueType,
322
if (ret != ERROR_SUCCESS) {
324
* NT 4.0 SP3 fails here since it doesn't have the "Std"
325
* entry in the Time Zones registry.
327
RegCloseKey(hSubKey);
329
ret = RegOpenKeyExW(hKey, stdNamePtr, 0, KEY_READ, (PHKEY)&hSubKey);
330
if (ret != ERROR_SUCCESS) {
336
if (wcscmp((WCHAR *)szValue, stdNamePtr) == 0) {
338
* Some localized Win32 platforms use a same name to
339
* different time zones. So, we can't rely only on the name
340
* here. We need to check GMT offsets and transition dates
341
* to make sure it's the registry of the current time
344
DWORD tziValueSize = sizeof(tempTzi);
345
ret = RegQueryValueEx(hSubKey, "TZI", NULL, &valueType,
346
(unsigned char *) &tempTzi, &tziValueSize);
347
if (ret == ERROR_SUCCESS) {
348
if ((tzi.Bias != tempTzi.bias) ||
349
(memcmp((const void *) &tzi.StandardDate,
350
(const void *) &tempTzi.stdDate,
351
sizeof(SYSTEMTIME)) != 0)) {
355
if (tzi.DaylightBias != 0) {
356
if ((tzi.DaylightBias != tempTzi.dstBias) ||
357
(memcmp((const void *) &tzi.DaylightDate,
358
(const void *) &tempTzi.dstDate,
359
sizeof(SYSTEMTIME)) != 0)) {
366
* found matched record, terminate search
368
strcpy(winZoneName, subKeyName);
372
(void) RegCloseKey(hSubKey);
375
(void) RegCloseKey(hKey);
382
(void) RegCloseKey(hKey);
384
return VALUE_UNKNOWN;
388
* The mapping table file name.
390
#define MAPPINGS_FILE "\\lib\\tzmappings"
393
* Index values for the mapping table.
397
#define TZ_JAVA_NAME 2
399
#define TZ_NITEMS 3 /* number of items (fields) */
402
* Looks up the mapping table (tzmappings) and returns a Java time
403
* zone ID (e.g., "America/Los_Angeles") if found. Otherwise, NULL is
406
static char *matchJavaTZ(const char *java_home_dir, char *tzName)
410
char *javaTZName = NULL;
411
char *items[TZ_NITEMS];
413
char lineBuffer[MAX_ZONE_CHAR * 4];
415
const char* errorMessage = "unknown error";
416
char region[MAX_REGION_LENGTH];
418
// Get the user's location
419
if (GetGeoInfo(GetUserGeoID(GEOCLASS_NATION),
420
GEO_ISO2, region, MAX_REGION_LENGTH, 0) == 0) {
421
// If GetGeoInfo fails, fallback to LCID's country
422
LCID lcid = GetUserDefaultLCID();
423
if (GetLocaleInfo(lcid,
424
LOCALE_SISO3166CTRYNAME, region, MAX_REGION_LENGTH) == 0 &&
426
LOCALE_SISO3166CTRYNAME2, region, MAX_REGION_LENGTH) == 0) {
431
mapFileName = malloc(strlen(java_home_dir) + strlen(MAPPINGS_FILE) + 1);
432
if (mapFileName == NULL) {
435
strcpy(mapFileName, java_home_dir);
436
strcat(mapFileName, MAPPINGS_FILE);
438
if ((fp = fopen(mapFileName, "r")) == NULL) {
439
jio_fprintf(stderr, "can't open %s.\n", mapFileName);
440
free((void *) mapFileName);
443
free((void *) mapFileName);
446
while (fgets(lineBuffer, sizeof(lineBuffer), fp) != NULL) {
447
char *start, *idx, *endp;
451
start = idx = lineBuffer;
452
endp = &lineBuffer[sizeof(lineBuffer)];
455
* Ignore comment and blank lines.
457
if (*idx == '#' || *idx == '\n') {
461
for (itemIndex = 0; itemIndex < TZ_NITEMS; itemIndex++) {
462
items[itemIndex] = start;
463
while (*idx && *idx != ':') {
465
errorMessage = "premature end of line";
466
offset = (int)(idx - lineBuffer);
471
errorMessage = "illegal null character found";
472
offset = (int)(idx - lineBuffer);
480
errorMessage = "illegal non-newline character found";
481
offset = (int)(idx - lineBuffer);
486
* We need to scan items until the
487
* exact match is found or the end of data is detected.
489
if (strcmp(items[TZ_WIN_NAME], tzName) == 0) {
491
* Found the time zone in the mapping table.
492
* Check the region code and select the appropriate entry
494
if (strcmp(items[TZ_REGION], region) == 0 ||
495
strcmp(items[TZ_REGION], "001") == 0) {
496
javaTZName = _strdup(items[TZ_JAVA_NAME]);
507
jio_fprintf(stderr, "Illegal format in tzmappings file: %s at line %d, offset %d.\n",
508
errorMessage, line, offset);
513
* Detects the platform time zone which maps to a Java time zone ID.
515
char *findJavaTZ_md(const char *java_home_dir)
517
char winZoneName[MAX_ZONE_CHAR];
518
char *std_timezone = NULL;
521
result = getWinTimeZone(winZoneName, sizeof(winZoneName));
523
if (result != VALUE_UNKNOWN) {
524
if (result == VALUE_GMTOFFSET) {
525
std_timezone = _strdup(winZoneName);
527
std_timezone = matchJavaTZ(java_home_dir, winZoneName);
528
if (std_timezone == NULL) {
529
std_timezone = getGMTOffsetID();
537
* Returns a GMT-offset-based time zone ID.
547
// Obtain the current GMT offset value of ActiveTimeBias.
548
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,
549
KEY_READ, (PHKEY)&hKey);
550
if (ret == ERROR_SUCCESS) {
552
DWORD bufSize = sizeof(val);
554
ret = RegQueryValueExA(hKey, "ActiveTimeBias",
555
NULL, &valueType, (LPBYTE) &val, &bufSize);
556
if (ret == ERROR_SUCCESS) {
559
(void) RegCloseKey(hKey);
562
// If we can't get the ActiveTimeBias value, use Bias of TimeZoneInformation.
563
// Note: Bias doesn't reflect current daylight saving.
564
if (ret != ERROR_SUCCESS) {
565
TIME_ZONE_INFORMATION tzi;
566
if (GetTimeZoneInformation(&tzi) != TIME_ZONE_ID_INVALID) {
571
customZoneName(bias, zonename, sizeof(zonename));
572
return _strdup(zonename);