This is an automated email from the ASF dual-hosted git repository.
raminqaf pushed a commit to branch release-2.2
in repository https://gitbox.apache.org/repos/asf/flink.git
The following commit(s) were added to refs/heads/release-2.2 by this push:
new de3664ec022 [FLINK-41280][table] Fix CAST of a TIMESTAMP before 1970
to DATE, TIME and a lower precision (#29774)
de3664ec022 is described below
commit de3664ec022118af20e03a96d1387b80716735bf
Author: Rui Fan <[email protected]>
AuthorDate: Wed Nov 4 11:54:07 2026 +0100
[FLINK-41280][table] Fix CAST of a TIMESTAMP before 1970 to DATE, TIME and
a lower precision (#29774)
Three spots divided the epoch milliseconds with / or %, which round toward
zero, so a TIMESTAMP before 1970 moved toward 1970: TimestampToDateCastRule for
DATE, DateTimeUtils.timestampWithoutLocalZoneToTime for TIME, and
DateTimeUtils.zeroLastDigits, which truncate() uses for a TIMESTAMP or
TIMESTAMP_LTZ precision of 3 or less. Use floor division in all three. The
other callers of zeroLastDigits pass the nano-of-millisecond or a TIME
millis-of-day, which are never negative, so floor a [...]
Generated-by: Claude Code 2.1.314 (Claude Opus 5.5)
(cherry picked from commit 0196674040113fa18b8731e17817bbb8f0db8732)
Co-authored-by: Ramin Gharib <[email protected]>
---
.../main/java/org/apache/flink/table/utils/DateTimeUtils.java | 4 ++--
.../planner/functions/casting/TimestampToDateCastRule.java | 6 +++---
.../flink/table/planner/functions/CastFunctionITCase.java | 10 ++++++++++
3 files changed, 15 insertions(+), 5 deletions(-)
diff --git
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/utils/DateTimeUtils.java
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/utils/DateTimeUtils.java
index aaf2b075266..18368182f18 100644
---
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/utils/DateTimeUtils.java
+++
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/utils/DateTimeUtils.java
@@ -1542,7 +1542,7 @@ public class DateTimeUtils {
}
public static int timestampWithoutLocalZoneToTime(TimestampData ts, int
precision) {
- final int millisecond = (int) (ts.getMillisecond() %
DateTimeUtils.MILLIS_PER_DAY);
+ final int millisecond = (int) Math.floorMod(ts.getMillisecond(),
MILLIS_PER_DAY);
return applyTimePrecisionTruncation(millisecond, precision);
}
@@ -1700,7 +1700,7 @@ public class DateTimeUtils {
private static long zeroLastDigits(long l, int n) {
long tenToTheN = (long) Math.pow(10, n);
- return (l / tenToTheN) * tenToTheN;
+ return Math.floorDiv(l, tenToTheN) * tenToTheN;
}
public static long unixDateCeil(TimeUnitRange range, long date) {
diff --git
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/TimestampToDateCastRule.java
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/TimestampToDateCastRule.java
index 0d3858f017c..168b43e84a0 100644
---
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/TimestampToDateCastRule.java
+++
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/TimestampToDateCastRule.java
@@ -26,7 +26,6 @@ import org.apache.flink.table.utils.DateTimeUtils;
import static
org.apache.flink.table.planner.functions.casting.CastRuleUtils.cast;
import static
org.apache.flink.table.planner.functions.casting.CastRuleUtils.methodCall;
-import static
org.apache.flink.table.planner.functions.casting.CastRuleUtils.operator;
import static
org.apache.flink.table.planner.functions.casting.CastRuleUtils.staticCall;
/**
@@ -57,9 +56,10 @@ class TimestampToDateCastRule
if (inputLogicalType.is(LogicalTypeRoot.TIMESTAMP_WITHOUT_TIME_ZONE)) {
return cast(
"int",
- operator(
+ staticCall(
+ Math.class,
+ "floorDiv",
methodCall(inputTerm, "getMillisecond"),
- "/",
DateTimeUtils.MILLIS_PER_DAY));
} else if
(inputLogicalType.is(LogicalTypeRoot.TIMESTAMP_WITH_LOCAL_TIME_ZONE)) {
return staticCall(
diff --git
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CastFunctionITCase.java
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CastFunctionITCase.java
index a5b31c0dbac..f46ca41de20 100644
---
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CastFunctionITCase.java
+++
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CastFunctionITCase.java
@@ -107,6 +107,8 @@ public class CastFunctionITCase extends
BuiltInFunctionTestBase {
private static final LocalDateTime DEFAULT_TIMESTAMP =
LocalDateTime.parse("2021-09-24T12:34:56.1234567");
private static final Instant DEFAULT_TIMESTAMP_LTZ =
fromLocalTZ("2021-09-24T22:34:56.1234567");
+ private static final LocalDateTime TIMESTAMP_BEFORE_EPOCH =
+ LocalDateTime.parse("1969-12-31T23:59:59.999");
private static final Period DEFAULT_INTERVAL_YEAR = Period.of(10, 4, 0);
private static final Duration DEFAULT_INTERVAL_DAY = Duration.ofHours(12);
@@ -699,6 +701,7 @@ public class CastFunctionITCase extends
BuiltInFunctionTestBase {
//
.fromCase(TIMESTAMP(), DEFAULT_TIMESTAMP,
LocalDate.of(2021, 9, 24))
.fromCase(TIMESTAMP(4), DEFAULT_TIMESTAMP,
LocalDate.of(2021, 9, 24))
+ .fromCase(TIMESTAMP(3), TIMESTAMP_BEFORE_EPOCH,
LocalDate.of(1969, 12, 31))
// https://issues.apache.org/jira/browse/FLINK-20869
// TIMESTAMP_WITH_TIME_ZONE
@@ -747,6 +750,7 @@ public class CastFunctionITCase extends
BuiltInFunctionTestBase {
.fromCase(TIME(5), DEFAULT_TIME, LocalTime.of(12, 34,
56, 0))
.fromCase(TIMESTAMP(), DEFAULT_TIMESTAMP,
LocalTime.of(12, 34, 56, 0))
.fromCase(TIMESTAMP(4), DEFAULT_TIMESTAMP,
LocalTime.of(12, 34, 56, 0))
+ .fromCase(TIMESTAMP(3), TIMESTAMP_BEFORE_EPOCH,
LocalTime.of(23, 59, 59))
// https://issues.apache.org/jira/browse/FLINK-20869
// TIMESTAMP_WITH_TIME_ZONE
@@ -830,6 +834,12 @@ public class CastFunctionITCase extends
BuiltInFunctionTestBase {
// ROW
// RAW
.build(),
+ CastTestSpecBuilder.testCastTo(TIMESTAMP(0))
+ .fromCase(
+ TIMESTAMP(3),
+ TIMESTAMP_BEFORE_EPOCH,
+ LocalDateTime.of(1969, 12, 31, 23, 59, 59))
+ .build(),
CastTestSpecBuilder.testCastTo(TIMESTAMP_LTZ(9))
.fromCase(TIMESTAMP_LTZ(), null, null)
.failRuntime(CHAR(3), "foo",
DateTimeParseException.class)