Skip to content

Commit 567ecce

Browse files
srutjayjerboaa
authored andcommitted
8380993: [REDO] Incorrect Interpretation of POSIX TZ Environment Variable on AIX
Backport-of: 132072077aa4a15db108989e3d17e6d9249d3ba2
1 parent 85925b9 commit 567ecce

3 files changed

Lines changed: 158 additions & 23 deletions

File tree

src/java.base/unix/native/libjava/TimeZone_md.c

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -352,33 +352,15 @@ getPlatformTimeZoneID()
352352
}
353353

354354
static char *
355-
mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) {
355+
getJavaTimezoneFromPlatform(const char *tz_buf, size_t tz_len, const char *mapfilename) {
356356
FILE *tzmapf;
357-
char mapfilename[PATH_MAX + 1];
358357
char line[256];
359358
int linecount = 0;
360-
char *tz_buf = NULL;
361-
char *temp_tz = NULL;
362359
char *javatz = NULL;
363-
size_t tz_len = 0;
364-
365-
/* On AIX, the TZ environment variable may end with a comma
366-
* followed by modifier fields until early AIX6.1.
367-
* This restriction has been removed from AIX7. */
368360

369-
tz_buf = strdup(tz);
370-
tz_len = strlen(tz_buf);
371-
372-
/* Open tzmappings file, with buffer overrun check */
373-
if ((strlen(java_home_dir) + 15) > PATH_MAX) {
374-
jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir);
375-
goto tzerr;
376-
}
377-
strcpy(mapfilename, java_home_dir);
378-
strcat(mapfilename, "/lib/tzmappings");
379361
if ((tzmapf = fopen(mapfilename, "r")) == NULL) {
380362
jio_fprintf(stderr, "can't open %s\n", mapfilename);
381-
goto tzerr;
363+
return NULL;
382364
}
383365

384366
while (fgets(line, sizeof(line), tzmapf) != NULL) {
@@ -431,10 +413,58 @@ mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) {
431413
break;
432414
}
433415
}
416+
434417
(void) fclose(tzmapf);
418+
return javatz;
419+
}
420+
421+
static char *
422+
mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) {
423+
char mapfilename[PATH_MAX + 1];
424+
char *tz_buf = NULL;
425+
char *javatz = NULL;
426+
char *temp_tz = NULL;
427+
size_t tz_len = 0;
428+
429+
/* On AIX, the TZ environment variable may end with a comma
430+
* followed by modifier fields until early AIX6.1.
431+
* This restriction has been removed from AIX7. */
432+
433+
tz_buf = strdup(tz);
434+
if (tz_buf == NULL) {
435+
jio_fprintf(stderr, "Failed to allocate timezone buffer\n");
436+
goto tzerr;
437+
}
438+
tz_len = strlen(tz_buf);
439+
440+
/* Open tzmappings file, with buffer overrun check */
441+
if ((strlen(java_home_dir) + 15) > PATH_MAX) {
442+
jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir);
443+
goto tzerr;
444+
}
445+
strcpy(mapfilename, java_home_dir);
446+
strcat(mapfilename, "/lib/tzmappings");
447+
448+
// First attempt to find the Java timezone for the full tz string
449+
javatz = getJavaTimezoneFromPlatform(tz_buf, tz_len, mapfilename);
450+
451+
// If no match was found, check for timezone with truncated value
452+
if (javatz == NULL) {
453+
temp_tz = strchr(tz, ',');
454+
tz_len = (temp_tz == NULL) ? strlen(tz) : temp_tz - tz;
455+
free((void *) tz_buf);
456+
tz_buf = (char *)malloc(tz_len + 1);
457+
if (tz_buf == NULL) {
458+
jio_fprintf(stderr, "Failed to allocate timezone buffer\n");
459+
goto tzerr;
460+
}
461+
memcpy(tz_buf, tz, tz_len);
462+
tz_buf[tz_len] = '\0';
463+
javatz = getJavaTimezoneFromPlatform(tz_buf, tz_len, mapfilename);
464+
}
435465

436466
tzerr:
437-
if (tz_buf != NULL ) {
467+
if (tz_buf != NULL) {
438468
free((void *) tz_buf);
439469
}
440470

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
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.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/* @test
25+
* @bug 8380993
26+
* @library /test/lib
27+
* @summary Validates AIX timezone mapping behavior where POSIX TZ strings
28+
* with comma-separated DST rules are truncated and mapped through tzmappings
29+
* to the expected IANA timezone IDs.
30+
* @requires os.family == "aix"
31+
* @run main/othervm AIXTzMappingTest
32+
*/
33+
34+
import java.util.TimeZone;
35+
36+
import jdk.test.lib.process.ProcessTools;
37+
import jdk.test.lib.process.OutputAnalyzer;
38+
39+
public class AIXTzMappingTest {
40+
41+
// POSIX TZ strings that should be mapped via tzmappings
42+
private static final String TZ_CET = "CET-1CEST,M3.5.0,M10.5.0";
43+
private static final String TZ_MEZ = "MEZ-1MESZ,M3.5.0,M10.5.0/3";
44+
45+
private static final String ID_PARIS = "Europe/Paris";
46+
private static final String ID_BERLIN = "Europe/Berlin";
47+
48+
public static void main(String[] args) throws Throwable {
49+
if (args.length == 0) {
50+
runWithTZ(TZ_CET, ID_PARIS);
51+
runWithTZ(TZ_MEZ, ID_BERLIN);
52+
} else if (args.length == 1) {
53+
runTZTest(args[0]);
54+
} else {
55+
throw new RuntimeException(
56+
"Expected 0 or 1 arguments, got " + args.length);
57+
}
58+
}
59+
60+
private static void runWithTZ(String tz, String expectedId)
61+
throws Throwable {
62+
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
63+
"AIXTzMappingTest", expectedId);
64+
65+
pb.environment().put("TZ", tz);
66+
67+
OutputAnalyzer output = ProcessTools.executeProcess(pb);
68+
output.shouldHaveExitValue(0);
69+
}
70+
71+
/*
72+
* On AIX, POSIX TZ strings such as:
73+
* CET-1CEST,M3.5.0,M10.5.0
74+
* MEZ-1MESZ,M3.5.0,M10.5.0/3
75+
* are truncated at the comma and mapped through tzmappings to
76+
* IANA timezone IDs.
77+
*
78+
* This test verifies that the expected IANA timezone ID is selected.
79+
*/
80+
private static void runTZTest(String expectedId) {
81+
String tzStr = System.getenv("TZ");
82+
83+
if (tzStr == null) {
84+
throw new RuntimeException(
85+
"Got unexpected timezone information: TZ is null");
86+
}
87+
88+
TimeZone tz = TimeZone.getDefault();
89+
String tzId = tz.getID();
90+
91+
if (!expectedId.equals(tzId)) {
92+
throw new RuntimeException(
93+
"Expected timezone ID " + expectedId
94+
+ " but got " + tzId
95+
+ " for TZ=" + tzStr);
96+
}
97+
98+
System.out.println(
99+
"AIX timezone mapping test passed: "
100+
+ tzId + " for TZ=" + tzStr);
101+
}
102+
}

test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,10 @@
2626
* @library /test/lib
2727
* @summary This test will ensure that daylight savings rules are followed
2828
* appropriately when setting a custom timezone ID via the TZ env variable.
29-
* @requires os.family != "windows"
29+
* AIX is excluded because it uses a different timezone mapping mechanism
30+
* through the tzmappings file; see AIXTzMappingTest.java for AIX-specific
31+
* coverage.
32+
* @requires os.family != "windows" & os.family != "aix"
3033
* @run main/othervm CustomTzIDCheckDST
3134
*/
3235

0 commit comments

Comments
 (0)