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

jbonofre 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 ccaac9ad6 GH-125: Allow null timestamp holder sans timezone (#941)
ccaac9ad6 is described below

commit ccaac9ad688265d272d4c3d9d824426ef7681669
Author: Kaustav Sarkar <[email protected]>
AuthorDate: Thu Jan 22 23:26:49 2026 +0530

    GH-125: Allow null timestamp holder sans timezone (#941)
    
    ## Description
    Fixes an `IllegalArgumentException` in `TimeStamp*TZVector.set/setSafe`
    when unsetting values using a holder with a `null` timezone. The
    validation logic now correctly ignores the timezone check when
    `holder.isSet <= 0`, allowing default-constructed holders to be used for
    unsetting values as expected.
    
    Comprehensive tests added for all timestamp precisions (Micro, Milli,
    Nano, Sec) to verify the fix and ensure the existing workaround (setting
    explicit timezone) remains supported.
    
    Closes #125 .
---
 .../arrow/vector/TimeStampMicroTZVector.java       |  11 +-
 .../arrow/vector/TimeStampMilliTZVector.java       |  11 +-
 .../apache/arrow/vector/TimeStampNanoTZVector.java |  11 +-
 .../apache/arrow/vector/TimeStampSecTZVector.java  |  11 +-
 .../org/apache/arrow/vector/TestValueVector.java   | 193 +++++++++++++++++++++
 5 files changed, 217 insertions(+), 20 deletions(-)

diff --git 
a/vector/src/main/java/org/apache/arrow/vector/TimeStampMicroTZVector.java 
b/vector/src/main/java/org/apache/arrow/vector/TimeStampMicroTZVector.java
index abaefcfc1..50f2f066c 100644
--- a/vector/src/main/java/org/apache/arrow/vector/TimeStampMicroTZVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/TimeStampMicroTZVector.java
@@ -155,12 +155,13 @@ public final class TimeStampMicroTZVector extends 
TimeStampVector
       throws IllegalArgumentException {
     if (holder.isSet < 0) {
       throw new IllegalArgumentException();
-    } else if (!this.timeZone.equals(holder.timezone)) {
-      throw new IllegalArgumentException(
-          String.format(
-              "holder.timezone: %s not equal to vector timezone: %s",
-              holder.timezone, this.timeZone));
     } else if (holder.isSet > 0) {
+      if (!this.timeZone.equals(holder.timezone)) {
+        throw new IllegalArgumentException(
+            String.format(
+                "holder.timezone: %s not equal to vector timezone: %s",
+                holder.timezone, this.timeZone));
+      }
       BitVectorHelper.setBit(validityBuffer, index);
       setValue(index, holder.value);
     } else {
diff --git 
a/vector/src/main/java/org/apache/arrow/vector/TimeStampMilliTZVector.java 
b/vector/src/main/java/org/apache/arrow/vector/TimeStampMilliTZVector.java
index b5e5fb1be..9e4998396 100644
--- a/vector/src/main/java/org/apache/arrow/vector/TimeStampMilliTZVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/TimeStampMilliTZVector.java
@@ -155,12 +155,13 @@ public final class TimeStampMilliTZVector extends 
TimeStampVector
       throws IllegalArgumentException {
     if (holder.isSet < 0) {
       throw new IllegalArgumentException();
-    } else if (!this.timeZone.equals(holder.timezone)) {
-      throw new IllegalArgumentException(
-          String.format(
-              "holder.timezone: %s not equal to vector timezone: %s",
-              holder.timezone, this.timeZone));
     } else if (holder.isSet > 0) {
+      if (!this.timeZone.equals(holder.timezone)) {
+        throw new IllegalArgumentException(
+            String.format(
+                "holder.timezone: %s not equal to vector timezone: %s",
+                holder.timezone, this.timeZone));
+      }
       BitVectorHelper.setBit(validityBuffer, index);
       setValue(index, holder.value);
     } else {
diff --git 
a/vector/src/main/java/org/apache/arrow/vector/TimeStampNanoTZVector.java 
b/vector/src/main/java/org/apache/arrow/vector/TimeStampNanoTZVector.java
index 2386b3a85..b44b3da8d 100644
--- a/vector/src/main/java/org/apache/arrow/vector/TimeStampNanoTZVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/TimeStampNanoTZVector.java
@@ -154,12 +154,13 @@ public final class TimeStampNanoTZVector extends 
TimeStampVector
   public void set(int index, NullableTimeStampNanoTZHolder holder) throws 
IllegalArgumentException {
     if (holder.isSet < 0) {
       throw new IllegalArgumentException();
-    } else if (!this.timeZone.equals(holder.timezone)) {
-      throw new IllegalArgumentException(
-          String.format(
-              "holder.timezone: %s not equal to vector timezone: %s",
-              holder.timezone, this.timeZone));
     } else if (holder.isSet > 0) {
+      if (!this.timeZone.equals(holder.timezone)) {
+        throw new IllegalArgumentException(
+            String.format(
+                "holder.timezone: %s not equal to vector timezone: %s",
+                holder.timezone, this.timeZone));
+      }
       BitVectorHelper.setBit(validityBuffer, index);
       setValue(index, holder.value);
     } else {
diff --git 
a/vector/src/main/java/org/apache/arrow/vector/TimeStampSecTZVector.java 
b/vector/src/main/java/org/apache/arrow/vector/TimeStampSecTZVector.java
index f1774f270..a64a87f69 100644
--- a/vector/src/main/java/org/apache/arrow/vector/TimeStampSecTZVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/TimeStampSecTZVector.java
@@ -150,12 +150,13 @@ public final class TimeStampSecTZVector extends 
TimeStampVector
   public void set(int index, NullableTimeStampSecTZHolder holder) throws 
IllegalArgumentException {
     if (holder.isSet < 0) {
       throw new IllegalArgumentException();
-    } else if (!this.timeZone.equals(holder.timezone)) {
-      throw new IllegalArgumentException(
-          String.format(
-              "holder.timezone: %s not equal to vector timezone: %s",
-              holder.timezone, this.timeZone));
     } else if (holder.isSet > 0) {
+      if (!this.timeZone.equals(holder.timezone)) {
+        throw new IllegalArgumentException(
+            String.format(
+                "holder.timezone: %s not equal to vector timezone: %s",
+                holder.timezone, this.timeZone));
+      }
       BitVectorHelper.setBit(validityBuffer, index);
       setValue(index, holder.value);
     } else {
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java 
b/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java
index ac8224667..df42d04e6 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java
@@ -57,6 +57,10 @@ import 
org.apache.arrow.vector.complex.impl.UnionLargeListViewWriter;
 import org.apache.arrow.vector.complex.impl.UnionListViewWriter;
 import org.apache.arrow.vector.complex.impl.UnionListWriter;
 import org.apache.arrow.vector.holders.NullableIntHolder;
+import org.apache.arrow.vector.holders.NullableTimeStampMicroTZHolder;
+import org.apache.arrow.vector.holders.NullableTimeStampMilliTZHolder;
+import org.apache.arrow.vector.holders.NullableTimeStampNanoTZHolder;
+import org.apache.arrow.vector.holders.NullableTimeStampSecTZHolder;
 import org.apache.arrow.vector.holders.NullableUInt4Holder;
 import org.apache.arrow.vector.holders.NullableVarBinaryHolder;
 import org.apache.arrow.vector.holders.NullableVarCharHolder;
@@ -2567,6 +2571,195 @@ public class TestValueVector {
     }
   }
 
+  @Test
+  public void testTimeStampTZVectorSetSafeUnset() {
+    // reproduction of https://github.com/apache/arrow/issues/45084
+    try (TimeStampMicroTZVector vector = new TimeStampMicroTZVector("vector", 
allocator, "UTC")) {
+      vector.allocateNew();
+      // Set a valid value
+      NullableTimeStampMicroTZHolder validHolder = new 
NullableTimeStampMicroTZHolder();
+      validHolder.isSet = 1;
+      validHolder.value = 1000L;
+      validHolder.timezone = "UTC";
+      vector.setSafe(0, validHolder);
+
+      assertEquals(1000L, vector.get(0));
+
+      // Unset the value using a holder with default (null) timezone
+      // The bug used to throw IllegalArgumentException because 
holder.timezone (null) !=
+      // vector.timezone ("UTC")
+      // The correct behaviour is to not throw an exception and to unset the 
value.
+      NullableTimeStampMicroTZHolder unsetHolder = new 
NullableTimeStampMicroTZHolder();
+      unsetHolder.isSet = 0;
+      vector.setSafe(0, unsetHolder);
+
+      assertNull(vector.getObject(0));
+    }
+  }
+
+  @Test
+  public void testTimeStampMilliTZVectorSetSafeUnset() {
+    // reproduction of https://github.com/apache/arrow/issues/45084
+    try (TimeStampMilliTZVector vector = new TimeStampMilliTZVector("vector", 
allocator, "UTC")) {
+      vector.allocateNew();
+
+      NullableTimeStampMilliTZHolder validHolder = new 
NullableTimeStampMilliTZHolder();
+      validHolder.isSet = 1;
+      validHolder.value = 1000L;
+      validHolder.timezone = "UTC";
+      vector.setSafe(0, validHolder);
+
+      assertEquals(1000L, vector.get(0));
+
+      NullableTimeStampMilliTZHolder unsetHolder = new 
NullableTimeStampMilliTZHolder();
+      unsetHolder.isSet = 0;
+      vector.setSafe(0, unsetHolder);
+
+      assertNull(vector.getObject(0));
+    }
+  }
+
+  @Test
+  public void testTimeStampNanoTZVectorSetSafeUnset() {
+    // reproduction of https://github.com/apache/arrow/issues/45084
+    try (TimeStampNanoTZVector vector = new TimeStampNanoTZVector("vector", 
allocator, "UTC")) {
+      vector.allocateNew();
+
+      NullableTimeStampNanoTZHolder validHolder = new 
NullableTimeStampNanoTZHolder();
+      validHolder.isSet = 1;
+      validHolder.value = 1000L;
+      validHolder.timezone = "UTC";
+      vector.setSafe(0, validHolder);
+
+      assertEquals(1000L, vector.get(0));
+
+      NullableTimeStampNanoTZHolder unsetHolder = new 
NullableTimeStampNanoTZHolder();
+      unsetHolder.isSet = 0;
+      vector.setSafe(0, unsetHolder);
+
+      assertNull(vector.getObject(0));
+    }
+  }
+
+  @Test
+  public void testTimeStampSecTZVectorSetSafeUnset() {
+    // reproduction of https://github.com/apache/arrow/issues/45084
+    try (TimeStampSecTZVector vector = new TimeStampSecTZVector("vector", 
allocator, "UTC")) {
+      vector.allocateNew();
+
+      NullableTimeStampSecTZHolder validHolder = new 
NullableTimeStampSecTZHolder();
+      validHolder.isSet = 1;
+      validHolder.value = 1000L;
+      validHolder.timezone = "UTC";
+      vector.setSafe(0, validHolder);
+
+      assertEquals(1000L, vector.get(0));
+
+      NullableTimeStampSecTZHolder unsetHolder = new 
NullableTimeStampSecTZHolder();
+      unsetHolder.isSet = 0;
+      vector.setSafe(0, unsetHolder);
+
+      assertNull(vector.getObject(0));
+    }
+  }
+
+  @Test
+  public void testTimeStampMicroTZVectorSetSafeUnsetExplicitTimezone() {
+    // Test to ensure fix added for 
https://github.com/apache/arrow/issues/45084 does not break
+    // workaround.
+    try (TimeStampMicroTZVector vector = new TimeStampMicroTZVector("vector", 
allocator, "UTC")) {
+      vector.allocateNew();
+
+      NullableTimeStampMicroTZHolder validHolder = new 
NullableTimeStampMicroTZHolder();
+      validHolder.isSet = 1;
+      validHolder.value = 1000L;
+      validHolder.timezone = "UTC";
+      vector.setSafe(0, validHolder);
+
+      assertEquals(1000L, vector.get(0));
+
+      NullableTimeStampMicroTZHolder unsetHolder = new 
NullableTimeStampMicroTZHolder();
+      unsetHolder.isSet = 0;
+      unsetHolder.timezone = "UTC";
+
+      vector.setSafe(0, unsetHolder);
+
+      assertNull(vector.getObject(0));
+    }
+  }
+
+  @Test
+  public void testTimeStampMilliTZVectorSetSafeUnsetExplicitTimezone() {
+    // Test to ensure fix added for 
https://github.com/apache/arrow/issues/45084 does not break
+    // workaround.
+    try (TimeStampMilliTZVector vector = new TimeStampMilliTZVector("vector", 
allocator, "UTC")) {
+      vector.allocateNew();
+
+      NullableTimeStampMilliTZHolder validHolder = new 
NullableTimeStampMilliTZHolder();
+      validHolder.isSet = 1;
+      validHolder.value = 1000L;
+      validHolder.timezone = "UTC";
+      vector.setSafe(0, validHolder);
+
+      assertEquals(1000L, vector.get(0));
+
+      NullableTimeStampMilliTZHolder unsetHolder = new 
NullableTimeStampMilliTZHolder();
+      unsetHolder.isSet = 0;
+      unsetHolder.timezone = "UTC";
+      vector.setSafe(0, unsetHolder);
+
+      assertNull(vector.getObject(0));
+    }
+  }
+
+  @Test
+  public void testTimeStampNanoTZVectorSetSafeUnsetExplicitTimezone() {
+    // Test to ensure fix added for 
https://github.com/apache/arrow/issues/45084 does not break
+    // workaround.
+    try (TimeStampNanoTZVector vector = new TimeStampNanoTZVector("vector", 
allocator, "UTC")) {
+      vector.allocateNew();
+
+      NullableTimeStampNanoTZHolder validHolder = new 
NullableTimeStampNanoTZHolder();
+      validHolder.isSet = 1;
+      validHolder.value = 1000L;
+      validHolder.timezone = "UTC";
+      vector.setSafe(0, validHolder);
+
+      assertEquals(1000L, vector.get(0));
+
+      NullableTimeStampNanoTZHolder unsetHolder = new 
NullableTimeStampNanoTZHolder();
+      unsetHolder.isSet = 0;
+      unsetHolder.timezone = "UTC";
+      vector.setSafe(0, unsetHolder);
+
+      assertNull(vector.getObject(0));
+    }
+  }
+
+  @Test
+  public void testTimeStampSecTZVectorSetSafeUnsetExplicitTimezone() {
+    // Test to ensure fix added for 
https://github.com/apache/arrow/issues/45084 does not break
+    // workaround.
+    try (TimeStampSecTZVector vector = new TimeStampSecTZVector("vector", 
allocator, "UTC")) {
+      vector.allocateNew();
+
+      NullableTimeStampSecTZHolder validHolder = new 
NullableTimeStampSecTZHolder();
+      validHolder.isSet = 1;
+      validHolder.value = 1000L;
+      validHolder.timezone = "UTC";
+      vector.setSafe(0, validHolder);
+
+      assertEquals(1000L, vector.get(0));
+
+      NullableTimeStampSecTZHolder unsetHolder = new 
NullableTimeStampSecTZHolder();
+      unsetHolder.isSet = 0;
+      unsetHolder.timezone = "UTC";
+      vector.setSafe(0, unsetHolder);
+
+      assertNull(vector.getObject(0));
+    }
+  }
+
   @Test
   public void testSetNullableVarBinaryHolder() {
     try (VarBinaryVector vector = new VarBinaryVector("", allocator)) {

Reply via email to