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

lzljs3620320 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 161ff4a845 [common] Allow timestamp smaller than 1970 (#6122)
161ff4a845 is described below

commit 161ff4a8453c994b413efb42e29f0929c166d332
Author: YeJunHao <[email protected]>
AuthorDate: Fri Aug 22 19:58:04 2025 +0800

    [common] Allow timestamp smaller than 1970 (#6122)
---
 .../paimon/table/Timestamp9ReadWriteTest.java      | 87 ++++++++++++++++++++++
 .../reader/ParquetVectorUpdaterFactory.java        |  4 +
 2 files changed, 91 insertions(+)

diff --git 
a/paimon-core/src/test/java/org/apache/paimon/table/Timestamp9ReadWriteTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/table/Timestamp9ReadWriteTest.java
new file mode 100644
index 0000000000..7c60fc960b
--- /dev/null
+++ 
b/paimon-core/src/test/java/org/apache/paimon/table/Timestamp9ReadWriteTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.paimon.table;
+
+import org.apache.paimon.data.Decimal;
+import org.apache.paimon.data.GenericRow;
+import org.apache.paimon.data.Timestamp;
+import org.apache.paimon.schema.Schema;
+import org.apache.paimon.table.sink.BatchTableWrite;
+import org.apache.paimon.table.sink.BatchWriteBuilder;
+import org.apache.paimon.types.DataTypes;
+
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Test for timestamp 9 read write. */
+public class Timestamp9ReadWriteTest extends TableTestBase {
+
+    private static final DateTimeFormatter TIMESTAMP_FORMATTER =
+            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");
+
+    @Test
+    public void testTimestamp9() throws Exception {
+        createTableDefault();
+
+        LocalDateTime localDateTime =
+                LocalDateTime.parse("1899-01-01 00:59:20.001001001", 
TIMESTAMP_FORMATTER);
+        GenericRow record2 =
+                GenericRow.of(
+                        20,
+                        1,
+                        -1234,
+                        Timestamp.fromLocalDateTime(localDateTime),
+                        Decimal.fromBigDecimal(new 
BigDecimal("-123456789987654321.45678"), 23, 5),
+                        "bad".getBytes(StandardCharsets.UTF_8));
+
+        Table table = getTableDefault();
+        BatchWriteBuilder writeBuilder = table.newBatchWriteBuilder();
+        try (BatchTableWrite write = writeBuilder.newWrite()) {
+            write.write(record2);
+            writeBuilder.newCommit().commit(write.prepareCommit());
+        }
+
+        table.newReadBuilder()
+                .newRead()
+                .createReader(table.newReadBuilder().newScan().plan())
+                .forEachRemaining(
+                        row ->
+                                assertThat(row.getTimestamp(3, 9))
+                                        
.isEqualTo(Timestamp.fromLocalDateTime(localDateTime)));
+    }
+
+    @Override
+    protected Schema schemaDefault() {
+        Schema.Builder schemaBuilder = Schema.newBuilder();
+        schemaBuilder.partitionKeys("f1");
+        schemaBuilder.column("f1", DataTypes.INT());
+        schemaBuilder.column("f2", DataTypes.INT());
+        schemaBuilder.column("f3", DataTypes.DATE());
+        schemaBuilder.column("f4", DataTypes.TIMESTAMP(9));
+        schemaBuilder.column("f5", DataTypes.DECIMAL(23, 5));
+        schemaBuilder.column("f6", DataTypes.BYTES());
+        return schemaBuilder.build();
+    }
+}
diff --git 
a/paimon-format/src/main/java/org/apache/paimon/format/parquet/reader/ParquetVectorUpdaterFactory.java
 
b/paimon-format/src/main/java/org/apache/paimon/format/parquet/reader/ParquetVectorUpdaterFactory.java
index 94f03f66a9..4ae94a84fa 100644
--- 
a/paimon-format/src/main/java/org/apache/paimon/format/parquet/reader/ParquetVectorUpdaterFactory.java
+++ 
b/paimon-format/src/main/java/org/apache/paimon/format/parquet/reader/ParquetVectorUpdaterFactory.java
@@ -531,6 +531,10 @@ public class ParquetVectorUpdaterFactory {
 
             if (utcTimestamp) {
                 int nanoOfMillisecond = (int) (nanosOfDay % 
NANOS_PER_MILLISECOND);
+                if (nanoOfMillisecond < 0) {
+                    millisecond -= 1;
+                    nanoOfMillisecond += (int) NANOS_PER_MILLISECOND;
+                }
                 return Timestamp.fromEpochMillis(millisecond, 
nanoOfMillisecond);
             } else {
                 java.sql.Timestamp timestamp = new 
java.sql.Timestamp(millisecond);

Reply via email to