You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by ja...@apache.org on 2020/04/08 03:12:40 UTC

[flink] branch master updated: [FLINK-16632][table-planner-blink] Fix SqlDateTimeUtils#toSqlTimestamp(String, String) may yield incorrect result

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

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


The following commit(s) were added to refs/heads/master by this push:
     new dd6d40f  [FLINK-16632][table-planner-blink] Fix SqlDateTimeUtils#toSqlTimestamp(String, String) may yield incorrect result
dd6d40f is described below

commit dd6d40fd82b5ac945a95783f1a50cd35fdeadbb1
Author: Zhenghua Gao <do...@gmail.com>
AuthorDate: Wed Apr 8 11:12:12 2020 +0800

    [FLINK-16632][table-planner-blink] Fix SqlDateTimeUtils#toSqlTimestamp(String, String) may yield incorrect result
    
    This closes #11654
---
 .../planner/expressions/TemporalTypesTest.scala    | 20 ++++++++-
 .../table/runtime/functions/SqlDateTimeUtils.java  | 47 +++++++++++++++++-----
 2 files changed, 54 insertions(+), 13 deletions(-)

diff --git a/flink-table/flink-table-planner-blink/src/test/scala/org/apache/flink/table/planner/expressions/TemporalTypesTest.scala b/flink-table/flink-table-planner-blink/src/test/scala/org/apache/flink/table/planner/expressions/TemporalTypesTest.scala
index 42eb097..dfb977f 100644
--- a/flink-table/flink-table-planner-blink/src/test/scala/org/apache/flink/table/planner/expressions/TemporalTypesTest.scala
+++ b/flink-table/flink-table-planner-blink/src/test/scala/org/apache/flink/table/planner/expressions/TemporalTypesTest.scala
@@ -118,8 +118,11 @@ class TemporalTypesTest extends ExpressionTestBase {
 
     testSqlApi(
       "CAST('1999-9-10 05:20:10.123456' AS TIMESTAMP)",
-      "1999-09-10 05:20:10.123456"
-    )
+      "1999-09-10 05:20:10.123456")
+
+    testSqlApi(
+      "CAST('1999-9-10' AS TIMESTAMP)",
+      "1999-09-10 00:00:00.000000")
   }
 
   @Test
@@ -1122,6 +1125,19 @@ class TemporalTypesTest extends ExpressionTestBase {
 
     testSqlApi("TO_TIMESTAMP('abc')", "null")
 
+    // TO_TIMESTAMP should complement YEAR/MONTH/DAY/HOUR/MINUTE/SECOND/NANO_OF_SECOND
+    testSqlApi(
+      "TO_TIMESTAMP('2000020210', 'yyyyMMddHH')",
+      "2000-02-02 10:00:00.000")
+
+    testSqlApi(
+      "TO_TIMESTAMP('20000202 59:59.1234567', 'yyyyMMdd mm:ss.SSSSSSS')",
+      "2000-02-02 00:59:59.1234567")
+
+    testSqlApi(
+      "TO_TIMESTAMP('1234567', 'SSSSSSS')",
+      "1970-01-01 00:00:00.1234567")
+
     // CAST between two TIMESTAMPs
     testSqlApi(
       "CAST(TIMESTAMP '1970-01-01 00:00:00.123456789' AS TIMESTAMP(6))",
diff --git a/flink-table/flink-table-runtime-blink/src/main/java/org/apache/flink/table/runtime/functions/SqlDateTimeUtils.java b/flink-table/flink-table-runtime-blink/src/main/java/org/apache/flink/table/runtime/functions/SqlDateTimeUtils.java
index a64ff98..42f3308 100644
--- a/flink-table/flink-table-runtime-blink/src/main/java/org/apache/flink/table/runtime/functions/SqlDateTimeUtils.java
+++ b/flink-table/flink-table-runtime-blink/src/main/java/org/apache/flink/table/runtime/functions/SqlDateTimeUtils.java
@@ -45,9 +45,18 @@ import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAccessor;
 import java.util.Date;
 import java.util.TimeZone;
 
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+import static java.time.temporal.ChronoField.YEAR;
+
 /**
  * Utility functions for datetime types: date, time, timestamp.
  * Currently, it is a bit messy putting date time functions in various classes because
@@ -245,19 +254,35 @@ public class SqlDateTimeUtils {
 		DateTimeFormatter formatter = DATETIME_FORMATTER_CACHE.get(format);
 
 		try {
-			if (dateStr.length() == 10) {
-				// Just a LocalDate
-				LocalDate ld = LocalDate.parse(dateStr, formatter);
-				return SqlTimestamp.fromLocalDateTime(LocalDateTime.of(ld, LocalTime.MIDNIGHT));
-			} else {
-				LocalDateTime ldt = LocalDateTime.parse(dateStr, formatter);
-				return SqlTimestamp.fromLocalDateTime(ldt);
-			}
+			TemporalAccessor accessor = formatter.parse(dateStr);
+			// complement year with 1970
+			int year = accessor.isSupported(YEAR) ? accessor.get(YEAR) : 1970;
+			// complement month with 1
+			int month = accessor.isSupported(MONTH_OF_YEAR) ? accessor.get(MONTH_OF_YEAR) : 1;
+			// complement day with 1
+			int day = accessor.isSupported(DAY_OF_MONTH) ? accessor.get(DAY_OF_MONTH) : 1;
+			// complement hour with 0
+			int hour = accessor.isSupported(HOUR_OF_DAY) ? accessor.get(HOUR_OF_DAY) : 0;
+			// complement minute with 0
+			int minute = accessor.isSupported(MINUTE_OF_HOUR) ? accessor.get(MINUTE_OF_HOUR) : 0;
+			// complement second with 0
+			int second = accessor.isSupported(SECOND_OF_MINUTE) ? accessor.get(SECOND_OF_MINUTE) : 0;
+			// complement nano_of_second with 0
+			int nanoOfSecond = accessor.isSupported(NANO_OF_SECOND) ? accessor.get(NANO_OF_SECOND) : 0;
+			LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second, nanoOfSecond);
+			return SqlTimestamp.fromLocalDateTime(ldt);
 		} catch (DateTimeParseException e) {
-			// fall back to support cases like '1999-9-10 05:20:10'
+			// fall back to support cases like '1999-9-10 05:20:10' or '1999-9-10'
 			try {
-				Timestamp ts = Timestamp.valueOf(dateStr);
-				return SqlTimestamp.fromTimestamp(ts);
+				dateStr = dateStr.trim();
+				int space = dateStr.indexOf(' ');
+				if (space >= 0) {
+					Timestamp ts = Timestamp.valueOf(dateStr);
+					return SqlTimestamp.fromTimestamp(ts);
+				} else {
+					java.sql.Date dt = java.sql.Date.valueOf(dateStr);
+					return SqlTimestamp.fromLocalDateTime(LocalDateTime.of(dt.toLocalDate(), LocalTime.MIDNIGHT));
+				}
 			} catch (IllegalArgumentException ie) {
 				return null;
 			}