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

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 7110b8fa73 [iceberg] Fix ClassCastException reading timestamp with 
local timezone columns (#7764)
7110b8fa73 is described below

commit 7110b8fa73619961f66734fc0c70cb90e9e37544
Author: Arnav Balyan <[email protected]>
AuthorDate: Thu May 7 08:58:26 2026 +0530

    [iceberg] Fix ClassCastException reading timestamp with local timezone 
columns (#7764)
    
    - Reading a timestamp with local timezone column from the Iceberg
    manifest throws ClassCastException. In IcebergConversions.toPaimonObject
    the two timestamp variants share a switch arm that casts the data type
    to TimestampType, but LocalZonedTimestampType is not a subtype of
    TimestampType.
    - Split the arm so each case casts to its own type. Write side was
    already doing this.
---
 .../iceberg/manifest/IcebergConversions.java       | 22 ++++++++++++----------
 .../manifest/IcebergConversionsTimestampTest.java  | 16 ++++++++++++++++
 2 files changed, 28 insertions(+), 10 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/iceberg/manifest/IcebergConversions.java
 
b/paimon-core/src/main/java/org/apache/paimon/iceberg/manifest/IcebergConversions.java
index b1cc1abf0c..c5bdaf4420 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/iceberg/manifest/IcebergConversions.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/iceberg/manifest/IcebergConversions.java
@@ -126,6 +126,14 @@ public class IcebergConversions {
                 .putLong(0, timestamp.toMicros());
     }
 
+    private static Timestamp timestampFromBytes(byte[] bytes, int precision) {
+        Preconditions.checkArgument(
+                precision >= 3 && precision <= 6,
+                "Paimon Iceberg compatibility only support timestamp type with 
precision from 3 to 6.");
+        long encoded = 
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong();
+        return precision == 3 ? Timestamp.fromEpochMillis(encoded) : 
Timestamp.fromMicros(encoded);
+    }
+
     private static ByteBuffer timeToByteBuffer(int millisOfDay, int precision) 
{
         Preconditions.checkArgument(
                 precision >= 0 && precision <= 3,
@@ -164,17 +172,11 @@ public class IcebergConversions {
                 return Decimal.fromUnscaledBytes(
                         bytes, decimalType.getPrecision(), 
decimalType.getScale());
             case TIMESTAMP_WITHOUT_TIME_ZONE:
+                return timestampFromBytes(bytes, ((TimestampType) 
type).getPrecision());
             case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
-                int timestampPrecision = ((TimestampType) type).getPrecision();
-                long timestampLong =
-                        
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong();
-                Preconditions.checkArgument(
-                        timestampPrecision >= 3 && timestampPrecision <= 6,
-                        "Paimon Iceberg compatibility only support timestamp 
type with precision from 3 to 6.");
-                if (timestampPrecision == 3) {
-                    return Timestamp.fromEpochMillis(timestampLong);
-                }
-                return Timestamp.fromMicros(timestampLong);
+                // LocalZonedTimestampType does not extend TimestampType, so 
it cannot
+                // share a switch arm with TIMESTAMP_WITHOUT_TIME_ZONE.
+                return timestampFromBytes(bytes, ((LocalZonedTimestampType) 
type).getPrecision());
             case TIME_WITHOUT_TIME_ZONE:
                 int timePrecision = ((TimeType) type).getPrecision();
                 Preconditions.checkArgument(
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/iceberg/manifest/IcebergConversionsTimestampTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/iceberg/manifest/IcebergConversionsTimestampTest.java
index 677b41d54d..73da8331c1 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/iceberg/manifest/IcebergConversionsTimestampTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/iceberg/manifest/IcebergConversionsTimestampTest.java
@@ -125,4 +125,20 @@ class IcebergConversionsTimestampTest {
     private static Stream<Arguments> provideInvalidTimestampCases() {
         return Stream.of(Arguments.of(0, 1698686153L), Arguments.of(9, 
1698686153123456789L));
     }
+
+    @ParameterizedTest
+    @MethodSource("provideTimestampToPaimonCases")
+    @DisplayName("toPaimonObject decodes TIMESTAMP_WITH_LOCAL_TIME_ZONE 
without ClassCastException")
+    void testToPaimonObjectForTimestampWithLocalTimeZone(
+            int precision, long serializedMicros, String expectedTs) {
+        byte[] bytes = new byte[8];
+        
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).putLong(serializedMicros);
+
+        Timestamp actual =
+                (Timestamp)
+                        IcebergConversions.toPaimonObject(
+                                
DataTypes.TIMESTAMP_WITH_LOCAL_TIME_ZONE(precision), bytes);
+
+        assertThat(actual.toString()).isEqualTo(expectedTs);
+    }
 }

Reply via email to