diff --git a/paimon-core/src/main/java/org/apache/paimon/casting/CastExecutors.java b/paimon-core/src/main/java/org/apache/paimon/casting/CastExecutors.java index 10ad114e60a7..054d6fca17c5 100644 --- a/paimon-core/src/main/java/org/apache/paimon/casting/CastExecutors.java +++ b/paimon-core/src/main/java/org/apache/paimon/casting/CastExecutors.java @@ -45,6 +45,7 @@ public class CastExecutors { .addRule(NumericPrimitiveToDecimalCastRule.INSTANCE) .addRule(DecimalToNumericPrimitiveCastRule.INSTANCE) .addRule(NumericPrimitiveCastRule.INSTANCE) + .addRule(NumericPrimitiveToTimestamp.INSTANCE) // Boolean <-> numeric rules .addRule(BooleanToNumericCastRule.INSTANCE) .addRule(NumericToBooleanCastRule.INSTANCE) diff --git a/paimon-core/src/main/java/org/apache/paimon/casting/NumericPrimitiveToTimestamp.java b/paimon-core/src/main/java/org/apache/paimon/casting/NumericPrimitiveToTimestamp.java new file mode 100644 index 000000000000..b8c667cee13f --- /dev/null +++ b/paimon-core/src/main/java/org/apache/paimon/casting/NumericPrimitiveToTimestamp.java @@ -0,0 +1,62 @@ +/* + * 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.casting; + +import org.apache.paimon.data.Timestamp; +import org.apache.paimon.types.DataType; +import org.apache.paimon.types.DataTypeFamily; +import org.apache.paimon.types.DataTypeRoot; +import org.apache.paimon.utils.DateTimeUtils; + +import java.time.ZoneId; + +/** + * {{@link DataTypeFamily#INTEGER_NUMERIC} to @link DataTypeRoot#TIMESTAMP_WITHOUT_TIME_ZONE}/{@link + * DataTypeRoot#TIMESTAMP_WITH_LOCAL_TIME_ZONE}. + */ +public class NumericPrimitiveToTimestamp extends AbstractCastRule { + + static final NumericPrimitiveToTimestamp INSTANCE = new NumericPrimitiveToTimestamp(); + + private NumericPrimitiveToTimestamp() { + super( + CastRulePredicate.builder() + .input(DataTypeFamily.NUMERIC) + .target(DataTypeRoot.TIMESTAMP_WITHOUT_TIME_ZONE) + .target(DataTypeRoot.TIMESTAMP_WITH_LOCAL_TIME_ZONE) + .build()); + } + + @Override + public CastExecutor create(DataType inputType, DataType targetType) { + ZoneId zoneId = + targetType.is(DataTypeRoot.TIMESTAMP_WITH_LOCAL_TIME_ZONE) + ? ZoneId.systemDefault() + : DateTimeUtils.UTC_ZONE.toZoneId(); + switch (inputType.getTypeRoot()) { + case INTEGER: + case BIGINT: + return value -> + Timestamp.fromLocalDateTime( + DateTimeUtils.toLocalDateTime(value.longValue() * 1000, zoneId)); + default: + return null; + } + } +} diff --git a/paimon-core/src/test/java/org/apache/paimon/casting/CastExecutorTest.java b/paimon-core/src/test/java/org/apache/paimon/casting/CastExecutorTest.java index d80cb1a6c7bd..ff805993c5bd 100644 --- a/paimon-core/src/test/java/org/apache/paimon/casting/CastExecutorTest.java +++ b/paimon-core/src/test/java/org/apache/paimon/casting/CastExecutorTest.java @@ -101,6 +101,23 @@ public void testNumericToNumeric() { CastExecutors.resolve(new FloatType(false), new DoubleType(false)), 1F, 1D); } + @Test + public void testNumericToTimestamp() { + compareCastResult( + CastExecutors.resolve(new BigIntType(false), new TimestampType(3)), + 1721898748, + DateTimeUtils.parseTimestampData("2024-07-25 09:12:28.000", 3)); + + Timestamp timestamp = Timestamp.fromEpochMillis(1721898748000L); + String tsString = DateTimeUtils.formatTimestamp(timestamp, TimeZone.getDefault(), 3); + Timestamp timestamp1 = DateTimeUtils.parseTimestampData(tsString, 3); + + compareCastResult( + CastExecutors.resolve(new BigIntType(false), new LocalZonedTimestampType(3)), + 1721898748L, + timestamp1); + } + @Test public void testNumericToDecimal() { compareCastResult(