This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-java.git


The following commit(s) were added to refs/heads/main by this push:
     new d035fc8c5 GH-1293: Use floor division when splitting epoch millis into 
day and time (#1294)
d035fc8c5 is described below

commit d035fc8c5b4c414fa2e5eec44dc3c711b5e074f0
Author: Abhishek Pathania <[email protected]>
AuthorDate: Wed Sep 16 12:13:41 2026 +0530

    GH-1293: Use floor division when splitting epoch millis into day and time 
(#1294)
    
    ## What's Changed
    
    `DateTimeUtils.getTimestampValue(long)` used `/` and `%` to split epoch
    milliseconds into an epoch day and a time within that day. These
    operators round toward zero. For negative values that were not exactly
    midnight, the existing code fixed the remainder but not the epoch day.
    The two parts then referred to different days, so the timestamp came
    back one day late.
    
    For example, `-618102000000` ms is 1950-06-01 01:00:00 UTC. The old
    division produced epoch day `-7153`, which is 1950-06-02, while the
    remainder was 01:00. The method returned 1950-06-02 01:00:00.
    
    This affects DATE values before 1970 when
    `ArrowFlightJdbcDateVectorAccessor.getDate(Calendar)` applies a non-zero
    calendar offset. The offset moves the value away from midnight and
    exposes the division bug.
    
    Closes #1293.
---
 .../org/apache/arrow/driver/jdbc/utils/DateTimeUtils.java   | 13 +++++--------
 .../apache/arrow/driver/jdbc/utils/DateTimeUtilsTest.java   | 11 +++++++++++
 2 files changed, 16 insertions(+), 8 deletions(-)

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 9363e3486..c4e7fda59 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 class DateTimeUtils {
    * @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 9c6635202..70bcb9b4c 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 class DateTimeUtilsTest {
 
     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));
+  }
 }

Reply via email to