diff --git a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/utils/MetaStoreUtils.java b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/utils/MetaStoreUtils.java index c12c009b81cd..cc092fd1c740 100644 --- a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/utils/MetaStoreUtils.java +++ b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/utils/MetaStoreUtils.java @@ -123,7 +123,13 @@ public static String normalizeDate(String date) { * @return Timestamp in string format. */ public static String convertTimestampToString(Timestamp timestamp) { - return TIMESTAMP_FORMATTER.format(timestamp.toLocalDateTime()); + TimeZone defaultTimeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + + String str = TIMESTAMP_FORMATTER.format(timestamp.toLocalDateTime()); + + TimeZone.setDefault(defaultTimeZone); + return str; } /** @@ -132,8 +138,14 @@ public static String convertTimestampToString(Timestamp timestamp) { * @return java.sql.Timestamp object. */ public static Timestamp convertStringToTimestamp(String timestamp) { + TimeZone defaultTimeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + LocalDateTime val = LocalDateTime.from(TIMESTAMP_FORMATTER.parse(timestamp)); - return Timestamp.valueOf(val); + Timestamp ts = Timestamp.valueOf(val); + + TimeZone.setDefault(defaultTimeZone); + return ts; } // Indicates a type was derived from the deserializer rather than Hive's metadata. diff --git a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/utils/TestMetaStoreUtils.java b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/utils/TestMetaStoreUtils.java index 8632f233e040..bf5fe7d185cd 100644 --- a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/utils/TestMetaStoreUtils.java +++ b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/utils/TestMetaStoreUtils.java @@ -48,18 +48,23 @@ public class TestMetaStoreUtils { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"); private final TimeZone timezone; private final String date; - private final String timestamp; + private final String timestampString; + private final Timestamp timestamp; public TestMetaStoreUtils(String zoneId, LocalDateTime timestamp) { this.timezone = TimeZone.getTimeZone(zoneId); - this.timestamp = timestamp.format(FORMATTER); + this.timestampString = timestamp.format(FORMATTER); this.date = timestamp.toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE); + + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + this.timestamp = Timestamp.valueOf(timestamp); + TimeZone.setDefault(DEFAULT); } @Parameterized.Parameters(name = "zoneId={0}, timestamp={1}") public static Collection generateZoneTimestampPairs() { List params = new ArrayList<>(); - long minDate = LocalDate.of(0, 1, 1).atStartOfDay().toEpochSecond(ZoneOffset.UTC); + long minDate = LocalDate.of(1, 1, 1).atStartOfDay().toEpochSecond(ZoneOffset.UTC); long maxDate = LocalDate.of(9999, 12, 31).atStartOfDay().toEpochSecond(ZoneOffset.UTC); new Random(23).longs(500, minDate, maxDate).forEach(i -> { LocalDateTime datetime = LocalDateTime.ofEpochSecond(i, 0, ZoneOffset.UTC); @@ -67,9 +72,28 @@ public static Collection generateZoneTimestampPairs() { params.add(new Object[] { zone, datetime }); } }); + generateDaylightSavingTimestampPairs(params); return params; } + public static void generateDaylightSavingTimestampPairs(List params) { + // For U.S timezones, the timestamp 2024-03-10 02:01:00 falls between the transition from 02:00 AM to 03:00 AM for daylight savings + params.add(new Object[] {"America/Anchorage", LocalDateTime.ofEpochSecond(1710036060L, 0, ZoneOffset.UTC)}); + params.add(new Object[] {"America/St_Johns", LocalDateTime.ofEpochSecond(1710036060L, 0, ZoneOffset.UTC)}); + params.add(new Object[] {"America/Chicago", LocalDateTime.ofEpochSecond(1710036060L, 0, ZoneOffset.UTC)}); + params.add(new Object[] {"America/Indiana/Indianapolis", LocalDateTime.ofEpochSecond(1710036060L, 0, ZoneOffset.UTC)}); + params.add(new Object[] {"America/Los_Angeles", LocalDateTime.ofEpochSecond(1710036060L, 0, ZoneOffset.UTC)}); + + // For Paris timezone, the timestamp 2024-03-31 02:02:02 falls between the transition from 02:00 AM to 03:00 AM for daylight savings + params.add(new Object[] {"Europe/Paris", LocalDateTime.ofEpochSecond(1711850522L, 0, ZoneOffset.UTC)}); + + // For New Zealand timezone, the timestamp 2024-09-29 02:03:04 falls between the transition from 02:00 AM to 03:00 AM for daylight savings + params.add(new Object[] {"Pacific/Auckland", LocalDateTime.ofEpochSecond(1727575384L, 0, ZoneOffset.UTC)}); + + // For Sydney timezone, the timestamp 2024-10-06 02:04:06 falls between the transition from 02:00 AM to 03:00 AM for daylight savings + params.add(new Object[] {"Australia/Sydney", LocalDateTime.ofEpochSecond(1728180246L, 0, ZoneOffset.UTC)}); + } + @Before public void setup() { TimeZone.setDefault(timezone); @@ -82,7 +106,7 @@ public void testDateToString() { @Test public void testTimestampToString() { - assertEquals(timestamp, MetaStoreUtils.convertTimestampToString(Timestamp.valueOf(timestamp))); + assertEquals(timestampString, MetaStoreUtils.convertTimestampToString(timestamp)); } @Test @@ -92,7 +116,7 @@ public void testStringToDate() { @Test public void testStringToTimestamp() { - assertEquals(Timestamp.valueOf(timestamp), MetaStoreUtils.convertStringToTimestamp(timestamp)); + assertEquals(timestamp, MetaStoreUtils.convertStringToTimestamp(timestampString)); } @AfterClass