raminqaf commented on code in PR #29690:
URL: https://github.com/apache/flink/pull/29690#discussion_r4453557460


##########
flink-table/flink-table-common/src/main/java/org/apache/flink/table/data/utils/VariantCastUtils.java:
##########
@@ -240,42 +256,107 @@ private static String decimalTarget(int precision, int 
scale) {
     }
 
     /**
-     * Reads a timestamp variant as the target {@code TIMESTAMP}. {@link 
Variant#getDateTime()}
-     * accepts both the microsecond ({@link Variant.Type#TIMESTAMP}) and 
nanosecond ({@link
-     * Variant.Type#TIMESTAMP_NS}) encodings. Fractional seconds beyond the 
target precision are
-     * truncated, the same as a regular {@code TIMESTAMP} to {@code 
TIMESTAMP(p)} cast.
+     * Reads a timestamp variant as the target {@code TIMESTAMP}. A {@code 
TIMESTAMP_LTZ} value is
+     * shifted into the session time zone, the same as a regular {@code 
TIMESTAMP_LTZ} to {@code
+     * TIMESTAMP} cast. Fractional seconds beyond the target precision are 
truncated.
      */
-    public static TimestampData toTimestamp(Variant variant, int precision) {
-        final Variant.Type type = variant.getType();
-        if (type == Variant.Type.TIMESTAMP || type == 
Variant.Type.TIMESTAMP_NS) {
-            return DateTimeUtils.truncate(
-                    TimestampData.fromLocalDateTime(variant.getDateTime()), 
precision);
+    public static TimestampData toTimestamp(Variant variant, int precision, 
ZoneId sessionZone) {
+        final LocalDateTime dateTime = toLocalDateTime(variant, sessionZone);
+        if (dateTime == null) {
+            throw unsupportedKind(variant, String.format("TIMESTAMP(%d)", 
precision));
         }
-        throw unsupportedKind(variant, String.format("TIMESTAMP(%d)", 
precision));
+        return TimestampData.fromLocalDateTime(
+                dateTime.withNano(truncateNanos(dateTime.getNano(), 
precision)));
     }
 
-    /** Reads a timestamp with local time zone variant. See {@link 
#toTimestamp(Variant, int)}. */
-    public static TimestampData toTimestampLtz(Variant variant, int precision) 
{
-        final Variant.Type type = variant.getType();
-        if (type == Variant.Type.TIMESTAMP_LTZ || type == 
Variant.Type.TIMESTAMP_LTZ_NS) {
-            return DateTimeUtils.truncate(
-                    TimestampData.fromInstant(variant.getInstant()), 
precision);
+    /**
+     * Reads a timestamp variant as the target {@code TIMESTAMP_LTZ}. A {@code 
TIMESTAMP} value is
+     * read as a wall-clock time in the session time zone, the same as a 
regular {@code TIMESTAMP}
+     * to {@code TIMESTAMP_LTZ} cast. Fractional seconds beyond the target 
precision are truncated.
+     */
+    public static TimestampData toTimestampLtz(Variant variant, int precision, 
ZoneId sessionZone) {
+        final Instant instant;
+        switch (variant.getType()) {
+            case TIMESTAMP_LTZ:
+            case TIMESTAMP_LTZ_NS:
+                instant = variant.getInstant();
+                break;
+            case TIMESTAMP:
+            case TIMESTAMP_NS:
+                instant = 
variant.getDateTime().atZone(sessionZone).toInstant();
+                break;
+            default:
+                throw unsupportedKind(variant, 
String.format("TIMESTAMP_LTZ(%d)", precision));
         }
-        throw unsupportedKind(variant, String.format("TIMESTAMP_LTZ(%d)", 
precision));
+        return TimestampData.fromInstant(
+                Instant.ofEpochSecond(
+                        instant.getEpochSecond(), 
truncateNanos(instant.getNano(), precision)));
     }
 
     /**
-     * Reads a time variant as the target {@code TIME}. The runtime TIME 
representation is
-     * millisecond-of-day, so a variant's microseconds are dropped and any 
fractional seconds beyond
-     * the target precision are then truncated, the same as a regular {@code 
TIME} to {@code
-     * TIME(p)} cast.
+     * Reads a date or timestamp variant as the target {@code DATE}, in days 
since the epoch. A
+     * timestamp keeps its date, read in the session time zone for a {@code 
TIMESTAMP_LTZ}.
      */
-    public static int toTime(Variant variant, int precision) {
+    public static int toDate(Variant variant, ZoneId sessionZone) {
+        if (variant.getType() == Variant.Type.DATE) {
+            return (int) variant.getDate().toEpochDay();
+        }
+        final LocalDateTime dateTime = toLocalDateTime(variant, sessionZone);
+        if (dateTime == null) {
+            throw unsupportedKind(variant, "DATE");
+        }
+        return (int) dateTime.toLocalDate().toEpochDay();
+    }
+
+    /**
+     * Reads a time or timestamp variant as the target {@code TIME}. A 
timestamp keeps its time of
+     * day, read in the session time zone for a {@code TIMESTAMP_LTZ}. The 
runtime TIME
+     * representation is millisecond-of-day, so a variant's microseconds are 
dropped and any
+     * fractional seconds beyond the target precision are then truncated, the 
same as a regular
+     * {@code TIME} to {@code TIME(p)} cast.
+     */
+    public static int toTime(Variant variant, int precision, ZoneId 
sessionZone) {
+        final LocalTime time;
         if (variant.getType() == Variant.Type.TIME) {
-            return DateTimeUtils.applyTimePrecisionTruncation(
-                    DateTimeUtils.toInternal(variant.getTime()), precision);
+            time = variant.getTime();
+        } else {
+            final LocalDateTime dateTime = toLocalDateTime(variant, 
sessionZone);
+            if (dateTime == null) {
+                throw unsupportedKind(variant, String.format("TIME(%d)", 
precision));
+            }
+            time = dateTime.toLocalTime();
         }
-        throw unsupportedKind(variant, String.format("TIME(%d)", precision));
+        return DateTimeUtils.applyTimePrecisionTruncation(
+                DateTimeUtils.toInternal(time), precision);
+    }
+
+    /**
+     * Reads a timestamp variant as a wall-clock time, shifting a {@code 
TIMESTAMP_LTZ} into the
+     * session time zone. Returns {@code null} for any other kind, so that the 
caller builds the
+     * error message only when the cast fails.
+     */
+    private static @Nullable LocalDateTime toLocalDateTime(Variant variant, 
ZoneId sessionZone) {
+        switch (variant.getType()) {
+            case TIMESTAMP:
+            case TIMESTAMP_NS:
+                return variant.getDateTime();
+            case TIMESTAMP_LTZ:
+            case TIMESTAMP_LTZ_NS:
+                return LocalDateTime.ofInstant(variant.getInstant(), 
sessionZone);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Zeroes the fractional-second digits beyond the precision. A java.time 
nano-of-second is never
+     * negative, so this also truncates toward the past before 1970, unlike 
dividing an epoch
+     * millisecond.
+     */
+    private static int truncateNanos(int nanoOfSecond, int precision) {
+        return precision >= TIMESTAMP_NANOS_PRECISION
+                ? nanoOfSecond
+                : nanoOfSecond - nanoOfSecond % NANOS_PER_DIGIT[precision];

Review Comment:
   It was intended when I wrote it. `DateTimeUtils.truncate` divided the epoch 
millisecond toward zero, so `1969-12-31 23:59:58.5` cast to `TIMESTAMP(0)` 
became `23:59:59`. FLINK-41280 fixed this. I have rebased and re-used the logic



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to