From 84c6799a5a6c10fe52312218ef07abc35146361c Mon Sep 17 00:00:00 2001 From: Aymeric Date: Tue, 11 Oct 2022 04:46:44 +1100 Subject: [PATCH] Feature/julian gregorian conversion fix (#100) --- .../java/integration/tests/TimestampTest.java | 19 +++---- .../jdbc/resultset/FireboltColumn.java | 44 +++++++++-------- .../jdbc/type/array/SqlArrayUtil.java | 2 +- .../firebolt/jdbc/type/date/SqlDateUtil.java | 45 +++++++++++++++-- .../jdbc/resultset/FireboltColumnTest.java | 40 +++++++++++++-- .../jdbc/resultset/FireboltResultSetTest.java | 32 ++++++------ .../jdbc/type/date/SqlDateUtilTest.java | 49 ++++++++++++++----- 7 files changed, 166 insertions(+), 65 deletions(-) diff --git a/src/integrationTest/java/integration/tests/TimestampTest.java b/src/integrationTest/java/integration/tests/TimestampTest.java index ae0081a13..cc384770f 100644 --- a/src/integrationTest/java/integration/tests/TimestampTest.java +++ b/src/integrationTest/java/integration/tests/TimestampTest.java @@ -1,5 +1,6 @@ package integration.tests; +import static com.firebolt.jdbc.type.date.SqlDateUtil.ONE_DAY_MILLIS; import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.*; @@ -27,9 +28,9 @@ void shouldGetTimeObjectsInEstTimezone() throws SQLException { ZonedDateTime zonedDateTime = ZonedDateTime.of(1975, 1, 2, 4, 1, 1, 0, TimeZone.getTimeZone("UTC").toZoneId()); - Timestamp expectedTimestamp = Timestamp.valueOf(zonedDateTime.toLocalDateTime()); - Time expectedTime = Time.valueOf(zonedDateTime.toLocalTime()); - Date expectedDate = Date.valueOf(zonedDateTime.toLocalDate()); + Timestamp expectedTimestamp = new Timestamp(zonedDateTime.toInstant().toEpochMilli()); + Time expectedTime = new Time(zonedDateTime.toInstant().toEpochMilli()); + Date expectedDate = new Date(zonedDateTime.toInstant().toEpochMilli()); assertEquals(expectedTimestamp, resultSet.getTimestamp(1)); assertEquals(expectedTimestamp, resultSet.getObject(1)); @@ -47,9 +48,9 @@ void shouldGetTimeObjectsInDefaultUTCTimezone() throws SQLException { ZonedDateTime zonedDateTime = ZonedDateTime.of(1975, 1, 1, 23, 1, 1, 0, TimeZone.getTimeZone("UTC").toZoneId()); - Timestamp expectedTimestamp = Timestamp.valueOf(zonedDateTime.toLocalDateTime()); - Time expectedTime = Time.valueOf(zonedDateTime.toLocalTime()); - Date expectedDate = Date.valueOf(zonedDateTime.toLocalDate()); + Timestamp expectedTimestamp = new Timestamp(zonedDateTime.toInstant().toEpochMilli()); + Time expectedTime = new Time(zonedDateTime.toInstant().toEpochMilli()); + Date expectedDate = new Date(zonedDateTime.toInstant().toEpochMilli()); assertEquals(expectedTimestamp, resultSet.getTimestamp(1)); assertEquals(expectedTimestamp, resultSet.getObject(1)); @@ -68,9 +69,9 @@ void shouldGetParsedTimeStampExtTimeObjects() throws SQLException { ZonedDateTime zonedDateTime = ZonedDateTime.of(1111, 11, 11, 12, 0, 3, 0, TimeZone.getTimeZone("UTC").toZoneId()); - Timestamp expectedTimestamp = Timestamp.valueOf(zonedDateTime.toLocalDateTime()); - Time expectedTime = Time.valueOf(zonedDateTime.toLocalTime()); - Date expectedDate = Date.valueOf(zonedDateTime.toLocalDate()); + Timestamp expectedTimestamp = new Timestamp(zonedDateTime.toInstant().toEpochMilli() + 7 * ONE_DAY_MILLIS); + Time expectedTime = new Time(zonedDateTime.toInstant().toEpochMilli() + 7 * ONE_DAY_MILLIS); + Date expectedDate = new Date(zonedDateTime.toInstant().toEpochMilli() + 7 * ONE_DAY_MILLIS); assertEquals(expectedTimestamp, resultSet.getTimestamp(1)); assertEquals(expectedTimestamp, resultSet.getObject(1)); diff --git a/src/main/java/com/firebolt/jdbc/resultset/FireboltColumn.java b/src/main/java/com/firebolt/jdbc/resultset/FireboltColumn.java index 652a0d49b..02238834a 100644 --- a/src/main/java/com/firebolt/jdbc/resultset/FireboltColumn.java +++ b/src/main/java/com/firebolt/jdbc/resultset/FireboltColumn.java @@ -2,10 +2,7 @@ import static com.firebolt.jdbc.type.FireboltDataType.*; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.TimeZone; +import java.util.*; import java.util.stream.Collectors; import org.apache.commons.lang3.RegExUtils; @@ -31,11 +28,13 @@ public final class FireboltColumn { private final FireboltDataType dataType; private final boolean nullable; private final FireboltDataType arrayBaseDataType; - private final List tupleBaseDateTypes; + private final List tupleBaseDataTypes; private final int arrayDepth; private final int precision; private final int scale; private final TimeZone timeZone; + private static final Set TIMEZONES = Arrays.stream(TimeZone.getAvailableIDs()) + .collect(Collectors.toCollection(HashSet::new)); public static FireboltColumn of(String columnType, String columnName) { log.debug("Creating column info for column: {} of type: {}", columnName, columnType); @@ -49,7 +48,7 @@ public static FireboltColumn of(String columnType, String columnName) { Optional, Optional>> scaleAndPrecisionPair; FireboltDataType fireboltType; if (typeInUpperCase.startsWith(FireboltDataType.TUPLE.getInternalName().toUpperCase())) { - tupleDataTypes = getTupleBaseDateTypes(typeInUpperCase, columnName); + tupleDataTypes = getTupleBaseDataTypes(typeInUpperCase, columnName); } while (typeInUpperCase.startsWith(FireboltDataType.ARRAY.getInternalName().toUpperCase(), currentIndex)) { @@ -67,21 +66,20 @@ public static FireboltColumn of(String columnType, String columnName) { arrayType = dataType; if (arrayType == TUPLE) { String tmp = columnType.substring(currentIndex, columnType.length() - arrayDepth); - tupleDataTypes = getTupleBaseDateTypes(tmp.toUpperCase(), columnName); + tupleDataTypes = getTupleBaseDataTypes(tmp.toUpperCase(), columnName); } fireboltType = FireboltDataType.ARRAY; } else { fireboltType = dataType; } String[] arguments = null; - if (!reachedEndOfTypeName(typeEndIndex, typeInUpperCase.length()) - || typeInUpperCase.startsWith("(", typeEndIndex)) { + if (!reachedEndOfTypeName(typeEndIndex, typeInUpperCase) || typeInUpperCase.startsWith("(", typeEndIndex)) { arguments = splitArguments(typeInUpperCase, typeEndIndex); scaleAndPrecisionPair = Optional.of(getsCaleAndPrecision(arguments, dataType)); } else { scaleAndPrecisionPair = Optional.empty(); } - if (dataType.isTime() && arguments != null) { + if (dataType.isTime() && arguments != null && arguments.length != 0) { timeZone = getTimeZoneFromArguments(arguments); } @@ -90,9 +88,8 @@ public static FireboltColumn of(String columnType, String columnName) { .orElse(dataType.getDefaultScale())) .precision(scaleAndPrecisionPair.map(Pair::getRight).filter(Optional::isPresent).map(Optional::get) .orElse(dataType.getDefaultPrecision())) - .timeZone(timeZone) - .arrayBaseDataType(arrayType).dataType(fireboltType).nullable(isNullable).arrayDepth(arrayDepth) - .tupleBaseDateTypes(tupleDataTypes).build(); + .timeZone(timeZone).arrayBaseDataType(arrayType).dataType(fireboltType).nullable(isNullable) + .arrayDepth(arrayDepth).tupleBaseDataTypes(tupleDataTypes).build(); } private static TimeZone getTimeZoneFromArguments(String[] arguments) { @@ -104,7 +101,13 @@ private static TimeZone getTimeZoneFromArguments(String[] arguments) { timeZoneArgument = arguments[0]; } if (timeZoneArgument != null) { - timeZone = TimeZone.getTimeZone(timeZoneArgument.replace("\\'", "")); + String id = timeZoneArgument.replace("\\'", ""); + if (TIMEZONES.contains(id)) { + timeZone = TimeZone.getTimeZone(timeZoneArgument.replace("\\'", "")); + } else { + log.warn("Could not use the timezone returned by the server with the id {} as it is not supported.", + id); + } } return timeZone; } @@ -113,7 +116,7 @@ public static FireboltColumn of(String columnType) { return of(columnType, null); } - private static List getTupleBaseDateTypes(String columnType, String columnName) { + private static List getTupleBaseDataTypes(String columnType, String columnName) { return Arrays.stream(getTupleTypes(columnType)).map(String::trim) .map(type -> FireboltColumn.of(type, columnName)).collect(Collectors.toList()); } @@ -127,8 +130,9 @@ private static String[] getTupleTypes(String columnType) { // parenthesis } - private static boolean reachedEndOfTypeName(int typeNameEndIndex, int type) { - return typeNameEndIndex == type; + private static boolean reachedEndOfTypeName(int typeNameEndIndex, String type) { + return typeNameEndIndex == type.length() || type.indexOf("(", typeNameEndIndex) < 0 + || type.indexOf(")", typeNameEndIndex) < 0; } private static int getTypeEndPosition(String type, int currentIndex) { @@ -150,7 +154,7 @@ private static Pair, Optional> getsCaleAndPrecision(S } break; case DECIMAL: - if (reachedEndOfTypeName(arguments.length, 2)) { + if (arguments.length == 2) { precision = Integer.parseInt(arguments[0]); scale = Integer.parseInt(arguments[1]); } @@ -170,7 +174,7 @@ public String getCompactTypeName() { if (this.isArray()) { return getArrayCompactTypeName(); } else if (this.isTuple()) { - return getTupleCompactTypeName(this.tupleBaseDateTypes); + return getTupleCompactTypeName(this.tupleBaseDataTypes); } else { Optional params = getTypeArguments(columnType); return dataType.getDisplayName() + params.orElse(""); @@ -186,7 +190,7 @@ private String getArrayCompactTypeName() { if (this.getArrayBaseDataType() != TUPLE) { type.append(this.getArrayBaseDataType().getDisplayName()); } else { - type.append(this.getTupleCompactTypeName(this.getTupleBaseDateTypes())); + type.append(this.getTupleCompactTypeName(this.getTupleBaseDataTypes())); } for (int i = 0; i < arrayDepth; i++) { type.append(")"); diff --git a/src/main/java/com/firebolt/jdbc/type/array/SqlArrayUtil.java b/src/main/java/com/firebolt/jdbc/type/array/SqlArrayUtil.java index 40f732bd4..f6bfc3abd 100644 --- a/src/main/java/com/firebolt/jdbc/type/array/SqlArrayUtil.java +++ b/src/main/java/com/firebolt/jdbc/type/array/SqlArrayUtil.java @@ -77,7 +77,7 @@ private static Object extractArrayFromOneDimensionalArray(String arrayContent, F private static Object[] getArrayOfTuples(FireboltColumn fireboltColumn, List tuples) throws SQLException { - List types = fireboltColumn.getTupleBaseDateTypes().stream().map(FireboltColumn::getDataType) + List types = fireboltColumn.getTupleBaseDataTypes().stream().map(FireboltColumn::getDataType) .collect(Collectors.toList()); List list = new ArrayList<>(); diff --git a/src/main/java/com/firebolt/jdbc/type/date/SqlDateUtil.java b/src/main/java/com/firebolt/jdbc/type/date/SqlDateUtil.java index 98007fada..238158179 100644 --- a/src/main/java/com/firebolt/jdbc/type/date/SqlDateUtil.java +++ b/src/main/java/com/firebolt/jdbc/type/date/SqlDateUtil.java @@ -25,6 +25,12 @@ public class SqlDateUtil { private static final TimeZone DEFAULT_TZ = TimeZone.getDefault(); + public static final long ONE_DAY_MILLIS = 86400000L; + + // Number of milliseconds at the start of the introduction of the gregorian + // calendar(1582-10-05T00:00:00Z) from the epoch of 1970-01-01T00:00:00Z + private static final long GREGORIAN_START_DATE_IN_MILLIS = -12220156800000L; + private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); public static final Function transformFromDateToSQLStringFunction = value -> String.format("'%s'", @@ -32,13 +38,17 @@ public class SqlDateUtil { DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd [HH:mm[:ss]]") .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter(); public static final BiFunction transformToTimestampFunction = (value, - fromTimeZone) -> parse(value, fromTimeZone).map(t -> Timestamp.valueOf(t.toLocalDateTime())).orElse(null); + fromTimeZone) -> parse(value, fromTimeZone).map(t -> { + Timestamp ts = new Timestamp(getEpochMilli(t)); + ts.setNanos(t.getNano()); + return ts; + }).orElse(null); public static final BiFunction transformToDateFunction = (value, - fromTimeZone) -> parse(value, fromTimeZone).map(t -> Date.valueOf(t.toLocalDate())).orElse(null); + fromTimeZone) -> parse(value, fromTimeZone).map(t -> new Date(getEpochMilli(t))).orElse(null); public static final BiFunction transformToTimeFunction = (value, - fromTimeZone) -> parse(value, fromTimeZone).map(t -> Time.valueOf(t.toLocalTime())).orElse(null); + fromTimeZone) -> parse(value, fromTimeZone).map(t -> new Time(getEpochMilli(t))).orElse(null); public static final Function transformFromTimestampToSQLStringFunction = value -> String .format("'%s'", dateTimeFormatter.format(value.toLocalDateTime())); @@ -56,4 +66,33 @@ private static Optional parse(String value, @Nullable TimeZone fr .atZone(zoneId).withZoneSameInstant(DEFAULT_TZ.toZoneId())); } } + + private static long getEpochMilli(ZonedDateTime t) { + return t.toInstant().toEpochMilli() + calculateJulianToGregorianDiffMillis(t); + } + + /** + * Calculates the difference in ms from Julian to Gregorian date for dates that + * are before the 5th of Oct 1582, which is before the introduction of the + * Gregorian Calendar + * + * @param zdt the date + * @return the difference in millis + */ + public static long calculateJulianToGregorianDiffMillis(ZonedDateTime zdt) { + if (zdt.toInstant().toEpochMilli() < GREGORIAN_START_DATE_IN_MILLIS) { + int year; + if (zdt.getMonthValue() == 1 || (zdt.getMonthValue() == 2 && zdt.getDayOfMonth() <= 28)) { + year = zdt.getYear() - 1; + } else { + year = zdt.getYear(); + } + int hundredsOfYears = year / 100; + long daysDiff = hundredsOfYears - (hundredsOfYears / 4L) - 2L; + return daysDiff * ONE_DAY_MILLIS; + } else { + return 0; + } + } + } diff --git a/src/test/java/com/firebolt/jdbc/resultset/FireboltColumnTest.java b/src/test/java/com/firebolt/jdbc/resultset/FireboltColumnTest.java index 3ba796a28..4f7a241e2 100644 --- a/src/test/java/com/firebolt/jdbc/resultset/FireboltColumnTest.java +++ b/src/test/java/com/firebolt/jdbc/resultset/FireboltColumnTest.java @@ -26,14 +26,15 @@ void shouldCreateColumDataForNullableString() { @Test void shouldCreateColumDataForArray() { - String type = "Array(Array(Nullable(String)))"; + String type = "Array(Array(Nullable(DateTime64(4, \\'EST\\'))))"; String name = "name"; FireboltColumn column = FireboltColumn.of(type, name); assertEquals(name, column.getColumnName()); assertEquals(type.toUpperCase(), column.getColumnType()); assertEquals(FireboltDataType.ARRAY, column.getDataType()); - assertEquals(FireboltDataType.STRING, column.getArrayBaseDataType()); - assertEquals("ARRAY(ARRAY(STRING))", column.getCompactTypeName()); + assertEquals(FireboltDataType.DATE_TIME_64, column.getArrayBaseDataType()); + assertEquals(TimeZone.getTimeZone("EST"), column.getTimeZone()); + assertEquals("ARRAY(ARRAY(TIMESTAMP_EXT))", column.getCompactTypeName()); } @Test @@ -128,7 +129,7 @@ void shouldCreateColumDataForDateTime64() { @Test void shouldCreateColumDataForDateTime64WithoutTimeZone() { - String type = "DateTime64(6)"; + String type = "Nullable(DateTime64(6))"; String name = "my_d"; FireboltColumn column = FireboltColumn.of(type, name); assertEquals(name, column.getColumnName()); @@ -138,6 +139,16 @@ void shouldCreateColumDataForDateTime64WithoutTimeZone() { assertEquals(25, column.getPrecision()); } + @Test + void shouldCreateColumDataForDateWithoutTimeZone() { + String type = "Nullable(Date)"; + String name = "my_d"; + FireboltColumn column = FireboltColumn.of(type, name); + assertEquals(name, column.getColumnName()); + assertEquals(type.toUpperCase(), column.getColumnType()); + assertNull(column.getTimeZone()); + } + @Test void shouldCreateColumDataForDateTimeWithTimeZone() { String type = "DateTime(\\'EST\\')"; @@ -148,4 +159,25 @@ void shouldCreateColumDataForDateTimeWithTimeZone() { assertEquals(FireboltDataType.DATE_TIME, column.getDataType()); assertEquals(TimeZone.getTimeZone("EST"), column.getTimeZone()); } + @Test + void shouldCreateColumDataForNullableDateTimeWithTimeZone() { + String type = "Nullable(DateTime(\\'EST\\'))"; + String name = "my_d"; + FireboltColumn column = FireboltColumn.of(type, name); + assertEquals(name, column.getColumnName()); + assertEquals(type.toUpperCase(), column.getColumnType()); + assertEquals(FireboltDataType.DATE_TIME, column.getDataType()); + assertEquals(TimeZone.getTimeZone("EST"), column.getTimeZone()); + } + + @Test + void shouldCreateColumDataForDateTimeWithoutTimezoneWhenTheTimezoneIsInvalid() { + String type = "DateTime(\\'HelloTz\\')"; + String name = "my_d"; + FireboltColumn column = FireboltColumn.of(type, name); + assertEquals(name, column.getColumnName()); + assertEquals(type.toUpperCase(), column.getColumnType()); + assertEquals(FireboltDataType.DATE_TIME, column.getDataType()); + assertNull(column.getTimeZone()); + } } diff --git a/src/test/java/com/firebolt/jdbc/resultset/FireboltResultSetTest.java b/src/test/java/com/firebolt/jdbc/resultset/FireboltResultSetTest.java index 79f335bfa..1820f1c02 100644 --- a/src/test/java/com/firebolt/jdbc/resultset/FireboltResultSetTest.java +++ b/src/test/java/com/firebolt/jdbc/resultset/FireboltResultSetTest.java @@ -320,7 +320,8 @@ void shouldReturnBoolean() throws SQLException { @Test void shouldReturnTime() throws SQLException { - Time expectedTime = Time.valueOf(ZonedDateTime.of(2022, 5, 10, 13, 1, 2, 0, UTC_TZ.toZoneId()).toLocalTime()); + Time expectedTime = new Time( + ZonedDateTime.of(2022, 5, 10, 13, 1, 2, 0, UTC_TZ.toZoneId()).toInstant().toEpochMilli()); inputStream = getInputStreamWithArray(); resultSet = new FireboltResultSet(inputStream, "array_test_table", "array_test_db", 65535); resultSet.next(); @@ -459,11 +460,11 @@ void shouldGetTimeWithTimezoneFromCalendar() throws SQLException { fireboltStatement, true); resultSet.next(); - Time firstExpectedTime = Time - .valueOf(ZonedDateTime.of(2022, 5, 10, 18, 1, 2, 0, UTC_TZ.toZoneId()).toLocalTime()); + Time firstExpectedTime = new Time( + ZonedDateTime.of(2022, 5, 10, 18, 1, 2, 0, UTC_TZ.toZoneId()).toInstant().toEpochMilli()); - Time secondExpectedTime = Time - .valueOf(ZonedDateTime.of(2022, 5, 10, 13, 1, 2, 0, UTC_TZ.toZoneId()).toLocalTime()); + Time secondExpectedTime = new Time( + ZonedDateTime.of(2022, 5, 10, 13, 1, 2, 0, UTC_TZ.toZoneId()).toInstant().toEpochMilli()); assertEquals(firstExpectedTime, resultSet.getTime("a_timestamp", EST_CALENDAR)); assertEquals(secondExpectedTime, resultSet.getTime("a_timestamp", UTC_CALENDAR)); @@ -505,12 +506,11 @@ void shouldGetTimeObjectsWithTimeZoneFromResponse() throws SQLException { resultSet.next(); ZonedDateTime zonedDateTime = ZonedDateTime.of(2022, 5, 10, 18, 1, 2, 0, UTC_TZ.toZoneId()); - Timestamp expectedTimestamp = Timestamp.valueOf(zonedDateTime.toLocalDateTime()); + Timestamp expectedTimestamp = new Timestamp(zonedDateTime.toInstant().toEpochMilli()); - Timestamp.valueOf(zonedDateTime.toLocalDateTime()); - Time expectedTime = Time.valueOf(zonedDateTime.toLocalTime()); - Date expectedDate = Date.valueOf( - ZonedDateTime.of(2022, 5, 11, 4, 1, 2, 0, TimeZone.getTimeZone("UTC").toZoneId()).toLocalDate()); + Time expectedTime = new Time(zonedDateTime.toInstant().toEpochMilli()); + Date expectedDate = new Date(ZonedDateTime + .of(2022, 5, 11, 4, 1, 2, 0, TimeZone.getTimeZone("UTC").toZoneId()).toInstant().toEpochMilli()); // The timezone returned by the db is always used regardless of the timezone // passed as an argument @@ -534,12 +534,12 @@ void shouldGetDateWithTimezoneFromCalendar() throws SQLException { resultSet = new FireboltResultSet(inputStream, "array_test_table", "array_test_db", 65535, false, fireboltStatement, true); resultSet.next(); - Date firstExpectedDateFromEST = Date.valueOf( - ZonedDateTime.of(2022, 5, 10, 18, 1, 2, 0, TimeZone.getTimeZone("UTC").toZoneId()).toLocalDate()); - Date secondExpectedDateFromEST = Date.valueOf( - ZonedDateTime.of(2022, 5, 11, 4, 1, 2, 0, TimeZone.getTimeZone("UTC").toZoneId()).toLocalDate()); - Date secondExpectedDateFromUTC = Date.valueOf( - ZonedDateTime.of(2022, 5, 10, 23, 1, 2, 0, TimeZone.getTimeZone("UTC").toZoneId()).toLocalDate()); + Date firstExpectedDateFromEST = new Date(ZonedDateTime + .of(2022, 5, 10, 18, 1, 2, 0, TimeZone.getTimeZone("UTC").toZoneId()).toInstant().toEpochMilli()); + Date secondExpectedDateFromEST = new Date(ZonedDateTime + .of(2022, 5, 11, 4, 1, 2, 0, TimeZone.getTimeZone("UTC").toZoneId()).toInstant().toEpochMilli()); + Date secondExpectedDateFromUTC = new Date(ZonedDateTime + .of(2022, 5, 10, 23, 1, 2, 0, TimeZone.getTimeZone("UTC").toZoneId()).toInstant().toEpochMilli()); assertEquals(firstExpectedDateFromEST, resultSet.getDate("a_timestamp", EST_CALENDAR)); resultSet.next(); diff --git a/src/test/java/com/firebolt/jdbc/type/date/SqlDateUtilTest.java b/src/test/java/com/firebolt/jdbc/type/date/SqlDateUtilTest.java index bf9b9b188..e55abaa23 100644 --- a/src/test/java/com/firebolt/jdbc/type/date/SqlDateUtilTest.java +++ b/src/test/java/com/firebolt/jdbc/type/date/SqlDateUtilTest.java @@ -1,5 +1,6 @@ package com.firebolt.jdbc.type.date; +import static com.firebolt.jdbc.type.date.SqlDateUtil.ONE_DAY_MILLIS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -22,15 +23,16 @@ class SqlDateUtilTest { void shouldTransformTimestampWithDefaultTzWhenTimeZoneIsNotSpecified() { String timestamp = "1000-08-23 12:57:13.073456789"; ZonedDateTime zonedDateTime = ZonedDateTime.of(1000, 8, 23, 12, 57, 13, 73456789, UTC_TZ.toZoneId()); - Timestamp expectedTimestamp = Timestamp.valueOf(zonedDateTime.toLocalDateTime()); + Timestamp expectedTimestamp = new Timestamp(zonedDateTime.toInstant().toEpochMilli() + ONE_DAY_MILLIS * 6 ); + expectedTimestamp.setNanos(73456789); assertEquals(expectedTimestamp, SqlDateUtil.transformToTimestampFunction.apply(timestamp, null)); } @Test void shouldTransformTimestampWithNanosToString() { String expectedTimestamp = "'2022-05-23 12:57:13.000173456'"; - Timestamp timestamp = Timestamp - .valueOf(ZonedDateTime.of(2022, 5, 23, 12, 57, 13, 173456, UTC_TZ.toZoneId()).toLocalDateTime()); + Timestamp timestamp = new Timestamp( + ZonedDateTime.of(2022, 5, 23, 12, 57, 13, 173456, UTC_TZ.toZoneId()).toInstant().toEpochMilli()); timestamp.setNanos(173456); assertEquals(expectedTimestamp, SqlDateUtil.transformFromTimestampToSQLStringFunction.apply(timestamp)); } @@ -38,23 +40,25 @@ void shouldTransformTimestampWithNanosToString() { @Test void shouldTransformTimestampToString() { String expectedTimestamp = "'2022-05-23 12:57:13'"; - Timestamp timestamp = Timestamp - .valueOf(ZonedDateTime.of(2022, 5, 23, 12, 57, 13, 0, UTC_TZ.toZoneId()).toLocalDateTime()); + Timestamp timestamp = new Timestamp( + ZonedDateTime.of(2022, 5, 23, 12, 57, 13, 0, UTC_TZ.toZoneId()).toInstant().toEpochMilli()); assertEquals(expectedTimestamp, SqlDateUtil.transformFromTimestampToSQLStringFunction.apply(timestamp)); } @Test void shouldTransformTimestampWithoutSeconds() { String timeWithoutSeconds = "2022-05-23 12:01"; - Timestamp expectedTimestamp = Timestamp - .valueOf(ZonedDateTime.of(2022, 5, 23, 12, 1, 0, 0, UTC_TZ.toZoneId()).toLocalDateTime()); + Timestamp expectedTimestamp = new Timestamp( + ZonedDateTime.of(2022, 5, 23, 12, 1, 0, 0, UTC_TZ.toZoneId()).toInstant().toEpochMilli()); assertEquals(expectedTimestamp, SqlDateUtil.transformToTimestampFunction.apply(timeWithoutSeconds, null)); } @Test void shouldTransformDate() { String date = "1000-05-23"; - Date expectedDate = Date.valueOf(ZonedDateTime.of(1000, 5, 23, 0, 0, 0, 0, UTC_TZ.toZoneId()).toLocalDate()); + Date expectedDate = new Date( + ZonedDateTime.of(1000, 5, 23, 0, 0, 0, 0, UTC_TZ.toZoneId()).toInstant().toEpochMilli() + + 6 * ONE_DAY_MILLIS); assertEquals(expectedDate, SqlDateUtil.transformToDateFunction.apply(date, null)); } @@ -62,7 +66,7 @@ void shouldTransformDate() { void shouldTransformDateUsingTimeZoneWhenProvided() { String date = "2022-05-22"; ZonedDateTime zonedDateTime = ZonedDateTime.of(2022, 5, 21, 14, 0, 0, 0, UTC_TZ.toZoneId()); - assertEquals(Date.valueOf(zonedDateTime.toLocalDate()), + assertEquals(new Date(zonedDateTime.toInstant().toEpochMilli()), SqlDateUtil.transformToDateFunction.apply(date, TimeZone.getTimeZone("Australia/Sydney"))); } @@ -70,7 +74,7 @@ void shouldTransformDateUsingTimeZoneWhenProvided() { void shouldTransformTime() { ZonedDateTime zdt = ZonedDateTime.of(2022, 5, 23, 12, 1, 13, 0, UTC_TZ.toZoneId()); String time = "2022-05-23 12:01:13"; - Time expectedTime = Time.valueOf(zdt.toLocalTime()); + Time expectedTime = new Time(zdt.toInstant().toEpochMilli()); assertEquals(expectedTime, SqlDateUtil.transformToTimeFunction.apply(time, UTC_TZ)); } @@ -79,7 +83,7 @@ void shouldTransformTime() { void shouldTransformTimeWithUTCWhenTimeZoneIsNotSpecified() { String time = "2022-08-23 12:01:13"; ZonedDateTime zdt = ZonedDateTime.of(2022, 8, 23, 12, 1, 13, 0, UTC_TZ.toZoneId()); - Time expectedTime = Time.valueOf(zdt.toLocalTime()); + Time expectedTime = new Time(zdt.toInstant().toEpochMilli()); assertEquals(expectedTime, SqlDateUtil.transformToTimeFunction.apply(time, null)); } @@ -87,7 +91,7 @@ void shouldTransformTimeWithUTCWhenTimeZoneIsNotSpecified() { void shouldTransformTimeWithoutSeconds() { String timeWithoutSeconds = "2022-05-23 12:01"; ZonedDateTime zdt = ZonedDateTime.of(2022, 5, 23, 12, 1, 0, 0, UTC_TZ.toZoneId()); - Time expectedTime = Time.valueOf(zdt.toLocalTime()); + Time expectedTime = new Time(zdt.toInstant().toEpochMilli()); assertEquals(expectedTime, SqlDateUtil.transformToTimeFunction.apply(timeWithoutSeconds, null)); } @@ -111,4 +115,25 @@ void shouldThrowExceptionWhenTheStringCannotBeParsedToADate() { assertThrows(DateTimeParseException.class, () -> SqlDateUtil.transformToDateFunction.apply(date, null)); } + @Test + void shouldGetJulianToGregorianDiffMillis() { + ZonedDateTime zonedDateTime1 = ZonedDateTime.of(1582, 10, 6, 12, 57, 13, 73456789, UTC_TZ.toZoneId()); + assertEquals(0, SqlDateUtil.calculateJulianToGregorianDiffMillis(zonedDateTime1) / ONE_DAY_MILLIS); + + ZonedDateTime zonedDateTime2 = ZonedDateTime.of(1582, 10, 4, 12, 57, 13, 73456789, UTC_TZ.toZoneId()); + assertEquals(10, SqlDateUtil.calculateJulianToGregorianDiffMillis(zonedDateTime2) / ONE_DAY_MILLIS); + + ZonedDateTime zonedDateTime3 = ZonedDateTime.of(1111, 10, 5, 12, 57, 13, 73456789, UTC_TZ.toZoneId()); + assertEquals(7, SqlDateUtil.calculateJulianToGregorianDiffMillis(zonedDateTime3) / ONE_DAY_MILLIS); + + ZonedDateTime zonedDateTime4 = ZonedDateTime.of(1100, 3, 1, 12, 57, 13, 73456789, UTC_TZ.toZoneId()); + assertEquals(7, SqlDateUtil.calculateJulianToGregorianDiffMillis(zonedDateTime4) / ONE_DAY_MILLIS); + + ZonedDateTime zonedDateTime5 = ZonedDateTime.of(1100, 2, 28, 12, 57, 13, 73456789, UTC_TZ.toZoneId()); + assertEquals(6, SqlDateUtil.calculateJulianToGregorianDiffMillis(zonedDateTime5) / ONE_DAY_MILLIS); + + ZonedDateTime zonedDateTime6 = ZonedDateTime.of(1099, 1, 29, 12, 57, 13, 73456789, UTC_TZ.toZoneId()); + assertEquals(6, SqlDateUtil.calculateJulianToGregorianDiffMillis(zonedDateTime6) / ONE_DAY_MILLIS); + } + }