diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DateTimeUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DateTimeUtils.java index 63e577407cb02..bd8e18dbf8eea 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DateTimeUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DateTimeUtils.java @@ -558,14 +558,14 @@ public static long convertTimestampOrDatetimeStrToLongWithDefaultZone(String tim public static long convertDatetimeStrToLong(String str, ZoneId zoneId) { return convertDatetimeStrToLong( str, - toZoneOffset(zoneId), + toZoneOffset(str, zoneId), 0, CommonDescriptor.getInstance().getConfig().getTimestampPrecision()); } public static long convertDatetimeStrToLong( String str, ZoneId zoneId, String timestampPrecision) { - return convertDatetimeStrToLong(str, toZoneOffset(zoneId), 0, timestampPrecision); + return convertDatetimeStrToLong(str, toZoneOffset(str, zoneId), 0, timestampPrecision); } public static long getInstantWithPrecision(String str, String timestampPrecision) { @@ -821,8 +821,20 @@ public static ZonedDateTime convertToZonedDateTime(long timestamp, ZoneId zoneId return ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), zoneId); } - public static ZoneOffset toZoneOffset(ZoneId zoneId) { - return zoneId.getRules().getOffset(Instant.now()); + public static ZoneOffset toZoneOffset(String str, ZoneId zoneId) { + if (str.endsWith("Z")) { + return ZoneOffset.UTC; + } + + int offsetIndex = Math.max(str.lastIndexOf('+'), str.lastIndexOf('-')); + if (offsetIndex != -1 && str.length() - offsetIndex == 6) { + return ZoneOffset.of(str.substring(offsetIndex)); + } + + long millis = convertDatetimeStrToLong(str, ZoneOffset.UTC, 0, "ms"); + LocalDateTime localDateTime = + LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneOffset.UTC); + return zoneId.getRules().getOffset(localDateTime); } public static ZonedDateTime convertMillsecondToZonedDateTime(long millisecond) { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/DateTimeUtilsTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/DateTimeUtilsTest.java index d0bbb97fbae85..12bbdf3378cc5 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/DateTimeUtilsTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/DateTimeUtilsTest.java @@ -27,12 +27,14 @@ import org.junit.Ignore; import org.junit.Test; +import java.time.DateTimeException; +import java.time.Instant; import java.time.ZoneId; import java.time.ZoneOffset; -import java.time.ZonedDateTime; import java.util.TimeZone; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; public class DateTimeUtilsTest { @@ -47,7 +49,7 @@ public class DateTimeUtilsTest { /** Test convertDatetimeStrToLong() method with different time precision. */ @Test public void convertDatetimeStrToLongTest1() { - zoneOffset = ZonedDateTime.now().getOffset(); + zoneOffset = Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).getOffset(); zoneId = ZoneId.systemDefault(); if (zoneOffset.toString().equals("Z")) { delta = 8 * 3600000; @@ -338,4 +340,81 @@ public void testConstructTimeDuration() { timeDuration = DateTimeUtils.constructTimeDuration("10000000000ms"); Assert.assertEquals(10000000000L, timeDuration.nonMonthDuration); } + + @Test + public void testToZoneOffsetForWinterTime() { + ZoneId zoneId = ZoneId.of("Europe/Warsaw"); + ZoneOffset offset = DateTimeUtils.toZoneOffset("2024-01-15 12:00:00", zoneId); + Assert.assertEquals(ZoneOffset.ofHours(1), offset); + } + + @Test + public void testToZoneOffsetForSummerTime() { + ZoneId zoneId = ZoneId.of("Europe/Warsaw"); + ZoneOffset offset = DateTimeUtils.toZoneOffset("2024-06-15 12:00:00", zoneId); + Assert.assertEquals(ZoneOffset.ofHours(2), offset); + } + + @Test + public void testToZoneOffsetJustBeforeSpringDST() { + ZoneId zoneId = ZoneId.of("Europe/Warsaw"); + ZoneOffset offset = DateTimeUtils.toZoneOffset("2024-03-31 02:00:00", zoneId); + Assert.assertEquals(ZoneOffset.ofHours(1), offset); + } + + @Test + public void testToZoneOffsetJustAfterSpringDST() { + ZoneId zoneId = ZoneId.of("Europe/Warsaw"); + ZoneOffset offset = DateTimeUtils.toZoneOffset("2024-03-31 03:00:00", zoneId); + Assert.assertEquals(ZoneOffset.ofHours(2), offset); + } + + @Test + public void testToZoneOffsetDuringSpringDSTGap() { + ZoneId zoneId = ZoneId.of("Europe/Warsaw"); + ZoneOffset offset = DateTimeUtils.toZoneOffset("2024-03-31 02:30:00", zoneId); + Assert.assertEquals(ZoneOffset.ofHours(1), offset); + } + + @Test + public void testToZoneOffsetDuringAutumnDSTTransition() { + ZoneId zoneId = ZoneId.of("Europe/Warsaw"); + ZoneOffset offset = DateTimeUtils.toZoneOffset("2024-10-27 02:30:00", zoneId); + Assert.assertEquals(ZoneOffset.ofHours(2), offset); + } + + @Test + public void testToZoneOffsetAfterAutumnDST() { + ZoneId zoneId = ZoneId.of("Europe/Warsaw"); + ZoneOffset offset = DateTimeUtils.toZoneOffset("2024-10-27 03:00:00", zoneId); + Assert.assertEquals(ZoneOffset.ofHours(1), offset); + } + + @Test + public void testToZoneOffsetWithExplicitOffsetInString() { + ZoneId zoneId = ZoneId.of("Europe/Warsaw"); + ZoneOffset offset = DateTimeUtils.toZoneOffset("2024-06-15 12:00:00+02:00", zoneId); + Assert.assertEquals(ZoneOffset.ofHours(2), offset); + offset = DateTimeUtils.toZoneOffset("2024-06-15 12:00:00Z", zoneId); + Assert.assertEquals(ZoneOffset.UTC, offset); + offset = DateTimeUtils.toZoneOffset("2024-06-15 12:00:00-08:00", zoneId); + Assert.assertEquals(ZoneOffset.ofHours(-8), offset); + } + + @Test + public void testToZoneOffsetWithUTCZoneId() { + ZoneId utc = ZoneId.of("UTC"); + Assert.assertEquals(ZoneOffset.UTC, DateTimeUtils.toZoneOffset("2024-06-15 12:00:00", utc)); + } + + @Test + public void testToZoneOffsetWithBrokenDate() { + ZoneId zoneId = ZoneId.of("Europe/Warsaw"); + DateTimeException exception = + assertThrows( + DateTimeException.class, + () -> { + DateTimeUtils.toZoneOffset("2024-12-31 10", zoneId); + }); + } }