diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/DateTimeUtils.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/DateTimeUtils.java index 9363e3486c..c4e7fda59b 100644 --- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/DateTimeUtils.java +++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/DateTimeUtils.java @@ -55,15 +55,12 @@ public static long applyCalendarOffset(long milliseconds, Calendar calendar) { * @return a {@link Timestamp} object representing the given Epoch millis */ public static Timestamp getTimestampValue(long millisWithCalendar) { - long milliseconds = millisWithCalendar; - if (milliseconds < 0) { - // LocalTime#ofNanoDay only accepts positive values - milliseconds -= ((milliseconds / MILLIS_PER_DAY) - 1) * MILLIS_PER_DAY; - } - + // Millis are negative before 1970, where only floor semantics keep the epoch day + // and the time-of-day remainder on the same day (and the remainder non-negative). return Timestamp.valueOf( LocalDateTime.of( - LocalDate.ofEpochDay(millisWithCalendar / MILLIS_PER_DAY), - LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos(milliseconds % MILLIS_PER_DAY)))); + LocalDate.ofEpochDay(Math.floorDiv(millisWithCalendar, MILLIS_PER_DAY)), + LocalTime.ofNanoOfDay( + TimeUnit.MILLISECONDS.toNanos(Math.floorMod(millisWithCalendar, MILLIS_PER_DAY))))); } } diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/DateTimeUtilsTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/DateTimeUtilsTest.java index 9c66352023..70bcb9b4c2 100644 --- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/DateTimeUtilsTest.java +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/DateTimeUtilsTest.java @@ -95,4 +95,15 @@ public void testShouldGetTimestampNegative() { assertThat(expected, is(actual)); } + + @Test + public void testShouldGetTimestampNegativeNotAlignedToDay() { + final long epochMilli = negativeEpochMilli + 3600000L; // 1950-06-01 01:00:00 UTC + final Instant instant = Instant.ofEpochMilli(epochMilli); + + final Timestamp expected = Timestamp.from(instant); + final Timestamp actual = DateTimeUtils.getTimestampValue(epochMilli); + + assertThat(expected, is(actual)); + } }