From f923015008f4277ea6bb3230eb27786f715e9263 Mon Sep 17 00:00:00 2001 From: Gaurav Chaudhari Date: Tue, 10 May 2022 08:55:13 -0700 Subject: [PATCH 1/4] 8288377: Offset calculation fix for DST with custom TZ. 8288377: Offset calculation fix for DST with custom TZ. --- .../unix/native/libjava/TimeZone_md.c | 44 ++++++++++- .../util/TimeZone/CustomTzIDCheckDST.java | 77 +++++++++++++++++++ 2 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index b7b6d2592bbed..dd9c63a4b2180 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -552,6 +552,29 @@ findJavaTZ_md(const char *java_home_dir) return javatz; } +/** + * Used to calculate the offset between two tm structs. + */ +static time_t +calculateTimeOffset(struct tm tm1, struct tm tm2) +{ + time_t offset; + const int seconds_per_minute = 60; + const int seconds_per_hour = seconds_per_minute * 60; + const int seconds_per_day = seconds_per_hour * 24; + // The conversion of years and months is not important, as offset should never exceed a day. + const int seconds_per_month = seconds_per_day * 31; + const int seconds_per_year = seconds_per_month * 12; + + // Apply mod to the result in order to normalize offset result to be under a day. + offset = ((tm1.tm_year - tm2.tm_year) * seconds_per_year + + (tm1.tm_mon - tm2.tm_mon) * seconds_per_month + + (tm1.tm_mday - tm2.tm_mday) * seconds_per_day + + (tm1.tm_hour - tm2.tm_hour) * seconds_per_hour + + (tm1.tm_min - tm2.tm_min) * seconds_per_minute) % seconds_per_day; + return offset; +} + /** * Returns a GMT-offset-based zone ID. (e.g., "GMT-08:00") */ @@ -592,18 +615,31 @@ getGMTOffsetID() { time_t offset; char sign, buf[32]; - offset = timezone; + struct tm localtm; + time_t clock; + + clock = time(NULL); + if (localtime_r(&clock, &localtm) == NULL) { + return strdup("GMT"); + } + + struct tm gmt; + + if (gmtime_r(&clock, &gmt) == NULL) { + return strdup("GMT"); + } + + offset = calculateTimeOffset(localtm, gmt); if (offset == 0) { return strdup("GMT"); } - /* Note that the time offset direction is opposite. */ if (offset > 0) { - sign = '-'; + sign = '+'; } else { offset = -offset; - sign = '+'; + sign = '-'; } sprintf(buf, (const char *)"GMT%c%02d:%02d", sign, (int)(offset/3600), (int)((offset%3600)/60)); diff --git a/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java b/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java new file mode 100644 index 0000000000000..234f6fdba7b1a --- /dev/null +++ b/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8285838 + * @library /test/lib + * @summary This test will ensure that daylight savings rules are followed + * appropriately when setting a custom timezone ID via the TZ env variable. + * @requires os.family != "windows" + * @run main/othervm CustomTzIDCheckDST + */ +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.SimpleTimeZone; +import java.time.DayOfWeek; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAdjusters; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +public class CustomTzIDCheckDST { + private static String CUSTOM_TZ = "MEZ-1MESZ,M3.5.0,M10.5.0"; + public static void main(String args[]) throws Throwable { + if (args.length == 0) { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(List.of("CustomTzIDCheckDST", "runTZTest")); + pb.environment().put("TZ", CUSTOM_TZ); + OutputAnalyzer output = ProcessTools.executeProcess(pb); + output.shouldHaveExitValue(0); + } else { + runTZTest(); + } + } + + /* TZ code will always be set to "MEZ-1MESZ,M3.5.0,M10.5.0". + * This ensures the transition periods for Daylights Savings should be at March's last + * Sunday and October's last Sunday. + */ + private static void runTZTest() { + Date time = new Date(); + if (new SimpleTimeZone(3600000, "MEZ-1MESZ", Calendar.MARCH, -1, Calendar.SUNDAY, 0, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 0).inDaylightTime(time)) { + // We are in Daylight savings period. + if (time.toString().endsWith("GMT+02:00 " + Integer.toString(time.getYear() + 1900))) + return; + } else { + if (time.toString().endsWith("GMT+01:00 " + Integer.toString(time.getYear() + 1900))) + return; + } + + // Reaching here means time zone did not match up as expected. + throw new RuntimeException("Got unexpected timezone information: " + time); + } + + private static ZonedDateTime getLastSundayOfMonth(ZonedDateTime date) { + return date.with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY)); + } +} From 7e462d95c2671df2ab0d70c4c386948088a4d836 Mon Sep 17 00:00:00 2001 From: Gaurav Chaudhari Date: Thu, 30 Jun 2022 11:27:45 -0700 Subject: [PATCH 2/4] 8288377: Simplified TZ offset calc and consolidate MACOS impl. --- .../unix/native/libjava/TimeZone_md.c | 86 ++----------------- 1 file changed, 6 insertions(+), 80 deletions(-) diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index dd9c63a4b2180..2170da3ba795c 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -552,97 +552,23 @@ findJavaTZ_md(const char *java_home_dir) return javatz; } -/** - * Used to calculate the offset between two tm structs. - */ -static time_t -calculateTimeOffset(struct tm tm1, struct tm tm2) -{ - time_t offset; - const int seconds_per_minute = 60; - const int seconds_per_hour = seconds_per_minute * 60; - const int seconds_per_day = seconds_per_hour * 24; - // The conversion of years and months is not important, as offset should never exceed a day. - const int seconds_per_month = seconds_per_day * 31; - const int seconds_per_year = seconds_per_month * 12; - - // Apply mod to the result in order to normalize offset result to be under a day. - offset = ((tm1.tm_year - tm2.tm_year) * seconds_per_year + - (tm1.tm_mon - tm2.tm_mon) * seconds_per_month + - (tm1.tm_mday - tm2.tm_mday) * seconds_per_day + - (tm1.tm_hour - tm2.tm_hour) * seconds_per_hour + - (tm1.tm_min - tm2.tm_min) * seconds_per_minute) % seconds_per_day; - return offset; -} - /** * Returns a GMT-offset-based zone ID. (e.g., "GMT-08:00") */ - -#if defined(MACOSX) - -char * -getGMTOffsetID() -{ - time_t offset; - char sign, buf[32]; - struct tm local_tm; - time_t clock; - - clock = time(NULL); - if (localtime_r(&clock, &local_tm) == NULL) { - return strdup("GMT"); - } - offset = (time_t)local_tm.tm_gmtoff; - if (offset == 0) { - return strdup("GMT"); - } - if (offset > 0) { - sign = '+'; - } else { - offset = -offset; - sign = '-'; - } - sprintf(buf, (const char *)"GMT%c%02d:%02d", - sign, (int)(offset/3600), (int)((offset%3600)/60)); - return strdup(buf); -} - -#else - char * getGMTOffsetID() { - time_t offset; - char sign, buf[32]; + char buf[32]; + char offset[6]; struct tm localtm; - time_t clock; - - clock = time(NULL); + time_t clock = time(NULL); if (localtime_r(&clock, &localtm) == NULL) { return strdup("GMT"); } - struct tm gmt; + strftime(offset, 6, "%z", &localtm); + char gmt_offset[] = {offset[0], offset[1], offset[2], ':', offset[3], offset[4], '\0'}; - if (gmtime_r(&clock, &gmt) == NULL) { - return strdup("GMT"); - } - - offset = calculateTimeOffset(localtm, gmt); - - if (offset == 0) { - return strdup("GMT"); - } - - if (offset > 0) { - sign = '+'; - } else { - offset = -offset; - sign = '-'; - } - sprintf(buf, (const char *)"GMT%c%02d:%02d", - sign, (int)(offset/3600), (int)((offset%3600)/60)); + sprintf(buf, (const char *)"GMT%s", gmt_offset); return strdup(buf); } -#endif /* MACOSX */ From 8ca0a7271136c770c5b49eb696a979854f4b76a8 Mon Sep 17 00:00:00 2001 From: Gaurav Chaudhari Date: Wed, 27 Jul 2022 13:12:30 -0700 Subject: [PATCH 3/4] 8288377: Added GMT0 corner case and minor format fixes. --- .../unix/native/libjava/TimeZone_md.c | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index 2170da3ba795c..089f40aa9df02 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -566,9 +566,29 @@ getGMTOffsetID() return strdup("GMT"); } +#if defined(MACOSX) + time_t gmt_offset; + gmt_offset = (time_t)localtm.tm_gmtoff; + if (gmt_offset == 0) { + return strdup("GMT"); + } +#else + struct tm gmt; + if (gmtime_r(&clock, &gmt) == NULL) { + return strdup("GMT"); + } + + if(localtm.tm_hour == gmt.tm_hour && localtm.tm_min == gmt.tm_min) { + return strdup("GMT"); + } +#endif + strftime(offset, 6, "%z", &localtm); - char gmt_offset[] = {offset[0], offset[1], offset[2], ':', offset[3], offset[4], '\0'}; + if (strlen(offset) != 5) { + return strdup("GMT"); + } - sprintf(buf, (const char *)"GMT%s", gmt_offset); + sprintf(buf, (const char *)"GMT%c%c%c:%c%c", offset[0], offset[1], offset[2], + offset[3], offset[4]); return strdup(buf); } From b09df3d5b790cee23195df4b5a796b6d0c062884 Mon Sep 17 00:00:00 2001 From: Gaurav Chaudhari Date: Thu, 28 Jul 2022 12:39:32 -0700 Subject: [PATCH 4/4] 8288377: Adjusted length check for returned gmt-offset --- src/java.base/unix/native/libjava/TimeZone_md.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index 089f40aa9df02..3ef2a26e7cd42 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -583,8 +583,7 @@ getGMTOffsetID() } #endif - strftime(offset, 6, "%z", &localtm); - if (strlen(offset) != 5) { + if (strftime(offset, 6, "%z", &localtm) != 5) { return strdup("GMT"); }