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

pvary pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git


The following commit(s) were added to refs/heads/main by this push:
     new 03ed4ba9af Fix Internal to Generic conversion of TIMESTAMP_NANO 
(#15099)
03ed4ba9af is described below

commit 03ed4ba9af4e47d32bdb22b7e3d033eb2a4b2c83
Author: Ayush Saxena <[email protected]>
AuthorDate: Wed Jan 28 20:18:31 2026 +0530

    Fix Internal to Generic conversion of TIMESTAMP_NANO (#15099)
---
 .../org/apache/iceberg/data/GenericDataUtil.java   |   6 ++
 .../apache/iceberg/data/TestGenericDataUtil.java   | 109 +++++++++++++++++++++
 2 files changed, 115 insertions(+)

diff --git a/core/src/main/java/org/apache/iceberg/data/GenericDataUtil.java 
b/core/src/main/java/org/apache/iceberg/data/GenericDataUtil.java
index 152ef31ac8..9b720c1f86 100644
--- a/core/src/main/java/org/apache/iceberg/data/GenericDataUtil.java
+++ b/core/src/main/java/org/apache/iceberg/data/GenericDataUtil.java
@@ -51,6 +51,12 @@ public class GenericDataUtil {
         } else {
           return DateTimeUtil.timestampFromMicros((Long) value);
         }
+      case TIMESTAMP_NANO:
+        if (((Types.TimestampNanoType) type).shouldAdjustToUTC()) {
+          return DateTimeUtil.timestamptzFromNanos((Long) value);
+        } else {
+          return DateTimeUtil.timestampFromNanos((Long) value);
+        }
       case FIXED:
         return ByteBuffers.toByteArray((ByteBuffer) value);
     }
diff --git 
a/core/src/test/java/org/apache/iceberg/data/TestGenericDataUtil.java 
b/core/src/test/java/org/apache/iceberg/data/TestGenericDataUtil.java
new file mode 100644
index 0000000000..2b0da11970
--- /dev/null
+++ b/core/src/test/java/org/apache/iceberg/data/TestGenericDataUtil.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.iceberg.data;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.nio.ByteBuffer;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import org.apache.iceberg.types.Types;
+import org.apache.iceberg.util.DateTimeUtil;
+import org.junit.jupiter.api.Test;
+
+public class TestGenericDataUtil {
+
+  @Test
+  public void testDateConversion() {
+    int days = DateTimeUtil.daysFromDate(LocalDate.of(2025, 1, 15));
+
+    Object result = GenericDataUtil.internalToGeneric(Types.DateType.get(), 
days);
+
+    
assertThat(result).isInstanceOf(LocalDate.class).isEqualTo(LocalDate.of(2025, 
1, 15));
+  }
+
+  @Test
+  public void testTimeConversion() {
+    long micros = DateTimeUtil.microsFromTime(LocalTime.of(11, 25, 20, 
111_456_000));
+
+    Object result = GenericDataUtil.internalToGeneric(Types.TimeType.get(), 
micros);
+
+    assertThat(result)
+        .isInstanceOf(LocalTime.class)
+        .isEqualTo(LocalTime.of(11, 25, 20, 111_456_000));
+  }
+
+  @Test
+  public void testTimestampConversion() {
+    long micros = 
DateTimeUtil.isoTimestampToMicros("2025-01-15T11:25:20.111456");
+
+    Object withoutZone =
+        GenericDataUtil.internalToGeneric(Types.TimestampType.withoutZone(), 
micros);
+
+    Object withZone = 
GenericDataUtil.internalToGeneric(Types.TimestampType.withZone(), micros);
+
+    assertThat(withoutZone)
+        .as("TIMESTAMP without zone should materialize as LocalDateTime")
+        .isInstanceOf(LocalDateTime.class)
+        .isEqualTo(LocalDateTime.of(2025, 1, 15, 11, 25, 20, 111_456_000));
+
+    assertThat(withZone)
+        .as("TIMESTAMP with zone should materialize as OffsetDateTime")
+        .isInstanceOf(OffsetDateTime.class)
+        .isEqualTo(OffsetDateTime.parse("2025-01-15T11:25:20.111456Z"));
+  }
+
+  @Test
+  public void testTimestampNanoConversion() {
+    long nanos = 
DateTimeUtil.isoTimestampToNanos("2025-01-15T11:25:20.111456789");
+
+    Object withoutZone =
+        
GenericDataUtil.internalToGeneric(Types.TimestampNanoType.withoutZone(), nanos);
+
+    Object withZone = 
GenericDataUtil.internalToGeneric(Types.TimestampNanoType.withZone(), nanos);
+
+    assertThat(withoutZone)
+        .as("TIMESTAMP_NANO without zone should materialize as LocalDateTime")
+        .isInstanceOf(LocalDateTime.class)
+        .isEqualTo(LocalDateTime.of(2025, 1, 15, 11, 25, 20, 111_456_789));
+
+    assertThat(withZone)
+        .as("TIMESTAMP_NANO with zone should materialize as OffsetDateTime")
+        .isInstanceOf(OffsetDateTime.class)
+        .isEqualTo(OffsetDateTime.parse("2025-01-15T11:25:20.111456789Z"));
+  }
+
+  @Test
+  public void testFixedConversion() {
+    byte[] bytes = new byte[] {1, 2, 3, 4};
+    ByteBuffer buffer = ByteBuffer.wrap(bytes);
+
+    Object result = 
GenericDataUtil.internalToGeneric(Types.FixedType.ofLength(4), buffer);
+
+    assertThat(result).isInstanceOf(byte[].class).isEqualTo(bytes);
+  }
+
+  @Test
+  public void testNullValueConversion() {
+    Object result = GenericDataUtil.internalToGeneric(Types.IntegerType.get(), 
null);
+    assertThat(result).isNull();
+  }
+}

Reply via email to