diff --git a/docs/data/sql_functions.yml b/docs/data/sql_functions.yml index f2915c462bb70..a6ba3aad3bf92 100644 --- a/docs/data/sql_functions.yml +++ b/docs/data/sql_functions.yml @@ -681,13 +681,13 @@ temporal: description: Converts a date string string1 with format string2 (by default 'yyyy-MM-dd') to a date. - sql: TO_TIMESTAMP_LTZ(numeric[, precision]) table: toTimestampLtz(NUMERIC, PRECISION) - description: "Converts a epoch seconds or epoch milliseconds to a TIMESTAMP_LTZ, the valid precision is 0 or 3, the 0 represents TO_TIMESTAMP_LTZ(epochSeconds, 0), the 3 represents TO_TIMESTAMP_LTZ(epochMilliseconds, 3). If no precision is provided, the default precision is 3." + description: Converts an epoch seconds or epoch milliseconds to a TIMESTAMP_LTZ, the valid precision is 0 or 3, the 0 represents TO_TIMESTAMP_LTZ(epochSeconds, 0), the 3 represents TO_TIMESTAMP_LTZ(epochMilliseconds, 3). If no precision is provided, the default precision is 3. - sql: TO_TIMESTAMP_LTZ(string1[, string2]) table: toTimestampLtz(STRING1[, STRING2]) - description: "Converts a timestamp string string1 with format string2 (by default 'yyyy-MM-dd HH:mm:ss.SSS') to a TIMESTAMP_LTZ". - - sql: TO_TIMESTAMP_TZ(string1, string2, string3) + description: Converts a timestamp string string1 with format string2 (by default 'yyyy-MM-dd HH:mm:ss.SSS') to a TIMESTAMP_LTZ. + - sql: TO_TIMESTAMP_LTZ(string1, string2, string3) table: toTimestampLtz(STRING1, STRING2, STRING3) - description: "Converts a timestamp string string1 with format string2 to a TIMESTAMP_TZ in time zone string3." + description: Converts a timestamp string string1 with format string2 to a TIMESTAMP_LTZ in time zone string3. - sql: TO_TIMESTAMP(string1[, string2]) table: toTimestamp(STRING1[, STRING2]) description: "Converts date time string string1 with format string2 (by default: 'yyyy-MM-dd HH:mm:ss') to a timestamp, without time zone." diff --git a/docs/data/sql_functions_zh.yml b/docs/data/sql_functions_zh.yml index d0250acc6f615..af4d627e5d842 100644 --- a/docs/data/sql_functions_zh.yml +++ b/docs/data/sql_functions_zh.yml @@ -805,11 +805,15 @@ temporal: - sql: TO_DATE(string1[, string2]) table: toDate(STRING1[, STRING2]) description: 将格式为 string2(默认为 'yyyy-MM-dd')的字符串 string1 转换为日期。 - - sql: TO_TIMESTAMP_LTZ(numeric, precision) - table: toTimestampLtz(numeric, PRECISION) - description: | - 将纪元秒或纪元毫秒转换为 TIMESTAMP_LTZ,有效精度为 0 或 3,0 代表 `TO_TIMESTAMP_LTZ(epochSeconds, 0)`, - 3 代表` TO_TIMESTAMP_LTZ(epochMilliseconds, 3)`。 + - sql: TO_TIMESTAMP_LTZ(numeric[, precision]) + table: toTimestampLtz(NUMERIC, PRECISION) + description: Converts an epoch seconds or epoch milliseconds to a TIMESTAMP_LTZ, the valid precision is 0 or 3, the 0 represents TO_TIMESTAMP_LTZ(epochSeconds, 0), the 3 represents TO_TIMESTAMP_LTZ(epochMilliseconds, 3). If no precision is provided, the default precision is 3. + - sql: TO_TIMESTAMP_LTZ(string1[, string2]) + table: toTimestampLtz(STRING1[, STRING2]) + description: Converts a timestamp string string1 with format string2 (by default 'yyyy-MM-dd HH:mm:ss.SSS') to a TIMESTAMP_LTZ. + - sql: TO_TIMESTAMP_LTZ(string1, string2, string3) + table: toTimestampLtz(STRING1, STRING2, STRING3) + description: Converts a timestamp string string1 with format string2 to a TIMESTAMP_LTZ in time zone string3. - sql: TO_TIMESTAMP(string1[, string2]) table: toTimestamp(STRING1[, STRING2]) description: 将格式为 string2(默认为:'yyyy-MM-dd HH:mm:ss')的字符串 string1 转换为 timestamp,不带时区。 diff --git a/flink-python/pyflink/table/expressions.py b/flink-python/pyflink/table/expressions.py index 3b326cb4641bc..e848738bdf11e 100644 --- a/flink-python/pyflink/table/expressions.py +++ b/flink-python/pyflink/table/expressions.py @@ -306,19 +306,38 @@ def to_timestamp(timestamp_str: Union[str, Expression[str]], return _binary_op("toTimestamp", timestamp_str, format) -def to_timestamp_ltz(numeric_epoch_time, precision) -> Expression: +def to_timestamp_ltz(*args) -> Expression: """ - Converts a numeric type epoch time to TIMESTAMP_LTZ. + Converts a value to a timestamp with local time zone. - The supported precision is 0 or 3: - 0 means the numericEpochTime is in second. - 3 means the numericEpochTime is in millisecond. + Supported signatures: + 1. to_timestamp_ltz(numeric) -> timestamp_ltz + 2. to_timestamp_ltz(numeric, precision) -> timestamp_ltz + 3. to_timestamp_ltz(string) -> timestamp_ltz + 4. to_timestamp_ltz(string, format) -> timestamp_ltz + 5. to_timestamp_ltz(string, format, timezone) -> timestamp_ltz - :param numeric_epoch_time: The epoch time with numeric type - :param precision: The precision to indicate the epoch time is in second or millisecond - :return: The timestamp value with TIMESTAMP_LTZ type. + Example: + :: + + >>> table.select(to_timestamp_ltz(100)) # numeric with default precision + >>> table.select(to_timestamp_ltz(100, 0)) # numeric with second precision + >>> table.select(to_timestamp_ltz("2023-01-01 00:00:00")) # string with default format + >>> table.select(to_timestamp_ltz("01/01/2023", "MM/dd/yyyy")) # string with format + >>> table.select(to_timestamp_ltz("2023-01-01 00:00:00", + "yyyy-MM-dd HH:mm:ss", + "UTC")) # string with format and timezone """ - return _binary_op("toTimestampLtz", numeric_epoch_time, precision) + if len(args) == 1: + return _unary_op("toTimestampLtz", lit(args[0])) + + # For two arguments case (numeric + precision or string + format) + elif len(args) == 2: + return _binary_op("toTimestampLtz", lit(args[0]), lit(args[1])) + + # For three arguments case (string + format + timezone) + elif len(args) == 3: + return _ternary_op("toTimestampLtz", lit(args[0]), lit(args[1]), lit(args[2])) def temporal_overlaps(left_time_point, diff --git a/flink-python/pyflink/table/tests/test_expression.py b/flink-python/pyflink/table/tests/test_expression.py index 2ea4bbdffbda3..4d7c7bdde6a25 100644 --- a/flink-python/pyflink/table/tests/test_expression.py +++ b/flink-python/pyflink/table/tests/test_expression.py @@ -285,7 +285,14 @@ def test_expressions(self): self.assertEqual("toDate('2018-03-18')", str(to_date('2018-03-18'))) self.assertEqual("toDate('2018-03-18', 'yyyy-MM-dd')", str(to_date('2018-03-18', 'yyyy-MM-dd'))) - self.assertEqual('toTimestampLtz(123, 0)', str(to_timestamp_ltz(123, 0))) + self.assertEqual('toTimestampLtz(100)', str(to_timestamp_ltz(100))) + self.assertEqual("toTimestampLtz('2023-01-01 00:00:00')", + str(to_timestamp_ltz('2023-01-01 00:00:00'))) + self.assertEqual("toTimestampLtz('01/01/2023 00:00:00', 'MM/dd/yyyy HH:mm:ss')", + str(to_timestamp_ltz("01/01/2023 00:00:00", "MM/dd/yyyy HH:mm:ss"))) + self.assertEqual("toTimestampLtz('2023-01-01 00:00:00', 'yyyy-MM-dd HH:mm:ss', 'UTC')", + str(to_timestamp_ltz("2023-01-01 00:00:00", "yyyy-MM-dd HH:mm:ss", "UTC"))) + self.assertEqual("toTimestampLtz(123, 0)", str(to_timestamp_ltz(123, 0))) self.assertEqual("toTimestamp('1970-01-01 08:01:40')", str(to_timestamp('1970-01-01 08:01:40'))) self.assertEqual("toTimestamp('1970-01-01 08:01:40', 'yyyy-MM-dd HH:mm:ss')", diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java index 9803544e3168f..34e8df08c7309 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java @@ -2332,7 +2332,8 @@ ANY, and(logical(LogicalTypeRoot.BOOLEAN), LITERAL) public static final BuiltInFunctionDefinition TO_TIMESTAMP_LTZ = BuiltInFunctionDefinition.newBuilder() - .name("TO_TIMESTAMP_LTZ") + .name("toTimestampLtz") + .sqlName("TO_TIMESTAMP_LTZ") .kind(SCALAR) .inputTypeStrategy( or( diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ToTimestampLtzTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ToTimestampLtzTypeStrategy.java index 26bd99d0255c4..5a80d30f42edf 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ToTimestampLtzTypeStrategy.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ToTimestampLtzTypeStrategy.java @@ -20,9 +20,12 @@ import org.apache.flink.annotation.Internal; import org.apache.flink.table.api.DataTypes; +import org.apache.flink.table.api.ValidationException; import org.apache.flink.table.types.DataType; import org.apache.flink.table.types.inference.CallContext; import org.apache.flink.table.types.inference.TypeStrategy; +import org.apache.flink.table.types.logical.LogicalType; +import org.apache.flink.table.types.logical.LogicalTypeFamily; import org.apache.flink.table.types.logical.LogicalTypeRoot; import java.util.List; @@ -40,37 +43,56 @@ public Optional inferType(CallContext callContext) { int argCount = argumentTypes.size(); if (argCount < 1 || argCount > 3) { - throw new IllegalArgumentException( - "TO_TIMESTAMP_LTZ requires 1 to 3 arguments, but " + throw new ValidationException( + "Unsupported argument type. " + + "TO_TIMESTAMP_LTZ requires 1 to 3 arguments, but " + argCount - + "arguments were provided."); + + " were provided."); } + LogicalType firstType = argumentTypes.get(0).getLogicalType(); + LogicalTypeRoot firstTypeRoot = firstType.getTypeRoot(); + if (argCount == 1) { - // TO_TIMESTAMP_LTZ(numeric) - // TO_TIMESTAMP_LTZ(string) - return Optional.of(DataTypes.TIMESTAMP_LTZ(DEFAULT_PRECISION)); + if (!isCharacterType(firstTypeRoot) && !firstType.is(LogicalTypeFamily.NUMERIC)) { + throw new ValidationException( + "Unsupported argument type. " + + "When taking 1 argument, TO_TIMESTAMP_LTZ accepts or ."); + } } else if (argCount == 2) { - LogicalTypeRoot firstArgTypeRoot = argumentTypes.get(0).getLogicalType().getTypeRoot(); - boolean isFirstArgNumeric = - firstArgTypeRoot == LogicalTypeRoot.TINYINT - || firstArgTypeRoot == LogicalTypeRoot.SMALLINT - || firstArgTypeRoot == LogicalTypeRoot.INTEGER - || firstArgTypeRoot == LogicalTypeRoot.BIGINT - || firstArgTypeRoot == LogicalTypeRoot.FLOAT - || firstArgTypeRoot == LogicalTypeRoot.DOUBLE - || firstArgTypeRoot == LogicalTypeRoot.DECIMAL; - // TO_TIMESTAMP_LTZ(numeric, precision) - if (callContext.isArgumentLiteral(1) && isFirstArgNumeric) { - final int precision = callContext.getArgumentValue(1, Integer.class).get(); - return Optional.of(DataTypes.TIMESTAMP_LTZ(precision)); + LogicalType secondType = argumentTypes.get(1).getLogicalType(); + LogicalTypeRoot secondTypeRoot = secondType.getTypeRoot(); + if (firstType.is(LogicalTypeFamily.NUMERIC)) { + if (secondTypeRoot != LogicalTypeRoot.INTEGER) { + throw new ValidationException( + "Unsupported argument type. " + + "TO_TIMESTAMP_LTZ(, ) requires the second argument to be ."); + } + } else if (isCharacterType(firstTypeRoot)) { + if (!isCharacterType(secondTypeRoot)) { + throw new ValidationException( + "Unsupported argument type. " + + "TO_TIMESTAMP_LTZ(, ) requires the second argument to be ."); + } + } else { + throw new ValidationException( + "Unsupported argument type. " + + "When taking 2 arguments, TO_TIMESTAMP_LTZ requires the first argument to be or ."); + } + } else if (argCount == 3) { + if (!isCharacterType(firstTypeRoot) + || !isCharacterType(argumentTypes.get(1).getLogicalType().getTypeRoot()) + || !isCharacterType(argumentTypes.get(2).getLogicalType().getTypeRoot())) { + throw new ValidationException( + "Unsupported argument type. " + + "When taking 3 arguments, TO_TIMESTAMP_LTZ requires all three arguments to be of type ."); } - // TO_TIMESTAMP_LTZ(string, format) - return Optional.of(DataTypes.TIMESTAMP_LTZ(DEFAULT_PRECISION)); - } else { - // argCount == 3 - // TO_TIMESTAMP_LTZ(string, format, timezone) - return Optional.of(DataTypes.TIMESTAMP_LTZ(DEFAULT_PRECISION)); } + + return Optional.of(DataTypes.TIMESTAMP_LTZ(DEFAULT_PRECISION).nullable()); + } + + private boolean isCharacterType(LogicalTypeRoot typeRoot) { + return typeRoot == LogicalTypeRoot.CHAR || typeRoot == LogicalTypeRoot.VARCHAR; } } 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 abd5369c2c401..7eef0c538e198 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 @@ -59,6 +59,7 @@ 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; +import static org.apache.flink.table.types.logical.YearMonthIntervalType.DEFAULT_PRECISION; /** * Utility functions for datetime types: date, time, timestamp. @@ -142,6 +143,8 @@ public class DateTimeUtils { .optionalEnd() .toFormatter(); + private static final Integer DEFAULT_PRECISION = 3; + /** * A ThreadLocal cache map for SimpleDateFormat, because SimpleDateFormat is not thread-safe. * (string_format) => formatter @@ -385,6 +388,18 @@ public static TimestampData toTimestampData(int v, int precision) { } } + public static TimestampData toTimestampData(long epoch) { + return toTimestampData(epoch, DEFAULT_PRECISION); + } + + public static TimestampData toTimestampData(double epoch) { + return toTimestampData(epoch, DEFAULT_PRECISION); + } + + public static TimestampData toTimestampData(DecimalData epoch) { + return toTimestampData(epoch, DEFAULT_PRECISION); + } + public static TimestampData toTimestampData(DecimalData v, int precision) { long epochMills; switch (precision) { @@ -406,18 +421,6 @@ public static TimestampData toTimestampData(DecimalData v, int precision) { } } - public static TimestampData toTimestampData(long epoch) { - return toTimestampData(epoch, 3); - } - - public static TimestampData toTimestampData(double epoch) { - return toTimestampData(epoch, 3); - } - - public static TimestampData toTimestampData(DecimalData epoch) { - return toTimestampData(epoch, 3); - } - private static TimestampData timestampDataFromEpochMills(long epochMills) { if (MIN_EPOCH_MILLS <= epochMills && epochMills <= MAX_EPOCH_MILLS) { return TimestampData.fromEpochMillis(epochMills); @@ -453,7 +456,7 @@ public static TimestampData parseTimestampData(String dateStr, int precision, Ti .toInstant()); } - public static TimestampData toTimestampData(String dateStr, String format, String timezone) { + public static TimestampData parseTimestampData(String dateStr, String format, String timezone) { if (dateStr == null || format == null || timezone == null) { return null; } diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/ToTimestampLtzTypeStrategyTest.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/ToTimestampLtzTypeStrategyTest.java new file mode 100644 index 0000000000000..2f0591365497d --- /dev/null +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/ToTimestampLtzTypeStrategyTest.java @@ -0,0 +1,99 @@ +/* + * 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.flink.table.types.inference.strategies; + +import org.apache.flink.table.api.DataTypes; +import org.apache.flink.table.types.inference.TypeStrategiesTestBase; + +import java.util.stream.Stream; + +/** Tests for {@link ToTimestampLtzTypeStrategy}. */ +class ToTimestampLtzTypeStrategyTest extends TypeStrategiesTestBase { + + @Override + protected Stream testData() { + return Stream.of( + TestSpec.forStrategy( + "TO_TIMESTAMP_LTZ()", + SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.STRING()) + .expectDataType(DataTypes.TIMESTAMP_LTZ(3).nullable()), + TestSpec.forStrategy( + "TO_TIMESTAMP_LTZ()", + SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.BIGINT()) + .expectDataType(DataTypes.TIMESTAMP_LTZ(3).nullable()), + TestSpec.forStrategy( + "Invalid single argument type", + SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.BOOLEAN()) + .expectErrorMessage( + "Unsupported argument type. When taking 1 argument, TO_TIMESTAMP_LTZ accepts or ."), + TestSpec.forStrategy( + "TO_TIMESTAMP_LTZ(, )", + SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.DOUBLE(), DataTypes.INT()) + .expectDataType(DataTypes.TIMESTAMP_LTZ(3).nullable()), + TestSpec.forStrategy( + "TO_TIMESTAMP_LTZ(, )", + SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.STRING(), DataTypes.STRING()) + .expectDataType(DataTypes.TIMESTAMP_LTZ(3).nullable()), + TestSpec.forStrategy( + "Invalid second argument when the first argument is ", + SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.BIGINT(), DataTypes.STRING()) + .expectErrorMessage( + "Unsupported argument type. TO_TIMESTAMP_LTZ(, ) requires the second argument to be ."), + TestSpec.forStrategy( + "Invalid second argument when the first argument is ", + SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.STRING(), DataTypes.FLOAT()) + .expectErrorMessage( + "Unsupported argument type. TO_TIMESTAMP_LTZ(, ) requires the second argument to be ."), + TestSpec.forStrategy( + "Invalid first argument when taking 2 arguments", + SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.BOOLEAN(), DataTypes.FLOAT()) + .expectErrorMessage( + "Unsupported argument type. When taking 2 arguments, TO_TIMESTAMP_LTZ requires the first argument to be or ."), + TestSpec.forStrategy( + "TO_TIMESTAMP_LTZ(, , )", + SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.STRING(), DataTypes.STRING(), DataTypes.STRING()) + .expectDataType(DataTypes.TIMESTAMP_LTZ(3).nullable()), + TestSpec.forStrategy( + "Invalid three arguments", SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes(DataTypes.STRING(), DataTypes.INT(), DataTypes.STRING()) + .expectErrorMessage( + "Unsupported argument type. When taking 3 arguments, TO_TIMESTAMP_LTZ requires all three arguments to be of type ."), + TestSpec.forStrategy("No arguments", SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes() + .expectErrorMessage( + "Unsupported argument type. TO_TIMESTAMP_LTZ requires 1 to 3 arguments, but 0 were provided."), + TestSpec.forStrategy("Too many arguments", SpecificTypeStrategies.TO_TIMESTAMP_LTZ) + .inputTypes( + DataTypes.STRING(), + DataTypes.STRING(), + DataTypes.STRING(), + DataTypes.STRING()) + .expectErrorMessage( + "Unsupported argument type. TO_TIMESTAMP_LTZ requires 1 to 3 arguments, but 4 were provided.")); + } +} diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/sql/FlinkSqlOperatorTable.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/sql/FlinkSqlOperatorTable.java index a539e1f81b4b8..0bb102f72b3f6 100644 --- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/sql/FlinkSqlOperatorTable.java +++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/sql/FlinkSqlOperatorTable.java @@ -818,7 +818,15 @@ public SqlSyntax getSyntax() { ReturnTypes.explicit(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, 3), SqlTypeTransforms.FORCE_NULLABLE), null, - OperandTypes.family(SqlTypeFamily.NUMERIC, SqlTypeFamily.INTEGER), + OperandTypes.or( + OperandTypes.family(SqlTypeFamily.CHARACTER), + OperandTypes.family(SqlTypeFamily.NUMERIC), + OperandTypes.family(SqlTypeFamily.NUMERIC, SqlTypeFamily.INTEGER), + OperandTypes.family(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER), + OperandTypes.family( + SqlTypeFamily.CHARACTER, + SqlTypeFamily.CHARACTER, + SqlTypeFamily.CHARACTER)), SqlFunctionCategory.TIMEDATE); public static final SqlFunction TO_DATE = diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/BuiltInMethods.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/BuiltInMethods.scala index df70f7a8c97ec..231dacadb3703 100644 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/BuiltInMethods.scala +++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/BuiltInMethods.scala @@ -23,7 +23,6 @@ import org.apache.flink.table.data.binary.{BinaryStringData, BinaryStringDataUti import org.apache.flink.table.functions.SqlLikeUtils import org.apache.flink.table.runtime.functions._ import org.apache.flink.table.runtime.functions.SqlJsonUtils.JsonQueryReturnType -import org.apache.flink.table.types.logical.LogicalTypeRoot import org.apache.flink.table.utils.DateTimeUtils import org.apache.flink.table.utils.DateTimeUtils.TimeUnitRange @@ -338,6 +337,15 @@ object BuiltInMethods { classOf[DecimalData], classOf[Int]) + val LONG_TO_TIMESTAMP_LTZ = + Types.lookupMethod(classOf[DateTimeUtils], "toTimestampData", classOf[Long]) + + val DOUBLE_TO_TIMESTAMP_LTZ = + Types.lookupMethod(classOf[DateTimeUtils], "toTimestampData", classOf[Double]) + + val DECIMAL_TO_TIMESTAMP_LTZ = + Types.lookupMethod(classOf[DateTimeUtils], "toTimestampData", classOf[DecimalData]) + val STRING_TO_TIMESTAMP = Types.lookupMethod(classOf[DateTimeUtils], "parseTimestampData", classOf[String]) @@ -347,6 +355,23 @@ object BuiltInMethods { classOf[String], classOf[String]) + val STRING_TO_TIMESTAMP_LTZ = + Types.lookupMethod(classOf[DateTimeUtils], "parseTimestampData", classOf[String]) + + val STRING_TO_TIMESTAMP_LTZ_WITH_FORMAT = + Types.lookupMethod( + classOf[DateTimeUtils], + "parseTimestampData", + classOf[String], + classOf[String]) + + val STRING_TO_TIMESTAMP_LTZ_WITH_FORMAT_WITH_TIME_ZONE = Types.lookupMethod( + classOf[DateTimeUtils], + "parseTimestampData", + classOf[String], + classOf[String], + classOf[String]) + val TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_DATE = Types.lookupMethod( classOf[DateTimeUtils], "timestampWithLocalZoneToDate", diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/FunctionGenerator.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/FunctionGenerator.scala index 53db45fe7c2eb..bd94a5337f494 100644 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/FunctionGenerator.scala +++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/FunctionGenerator.scala @@ -408,6 +408,8 @@ class FunctionGenerator private (tableConfig: ReadableConfig) { TO_TIMESTAMP_LTZ, Seq(dt, INTEGER), BuiltInMethods.LONG_TO_TIMESTAMP_LTZ_WITH_PRECISION) + + addSqlFunctionMethod(TO_TIMESTAMP_LTZ, Seq(dt), BuiltInMethods.LONG_TO_TIMESTAMP_LTZ) }) FRACTIONAL_TYPES.foreach( @@ -416,6 +418,8 @@ class FunctionGenerator private (tableConfig: ReadableConfig) { TO_TIMESTAMP_LTZ, Seq(dt, INTEGER), BuiltInMethods.DOUBLE_TO_TIMESTAMP_LTZ_WITH_PRECISION) + + addSqlFunctionMethod(TO_TIMESTAMP_LTZ, Seq(dt), BuiltInMethods.DOUBLE_TO_TIMESTAMP_LTZ) }) addSqlFunctionMethod( @@ -423,6 +427,8 @@ class FunctionGenerator private (tableConfig: ReadableConfig) { Seq(DECIMAL, INTEGER), BuiltInMethods.DECIMAL_TO_TIMESTAMP_LTZ_WITH_PRECISION) + addSqlFunctionMethod(TO_TIMESTAMP_LTZ, Seq(DECIMAL), BuiltInMethods.DECIMAL_TO_TIMESTAMP_LTZ) + INTEGRAL_TYPES.foreach( dt => addSqlFunctionMethod(FROM_UNIXTIME, Seq(dt), BuiltInMethods.FROM_UNIXTIME)) diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/StringCallGen.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/StringCallGen.scala index 75f25eb610cf0..61f5e4dce15d7 100644 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/StringCallGen.scala +++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/StringCallGen.scala @@ -21,7 +21,7 @@ import org.apache.flink.table.api.DataTypes import org.apache.flink.table.data.util.DataFormatConverters import org.apache.flink.table.planner.codegen.{CodeGeneratorContext, GeneratedExpression} import org.apache.flink.table.planner.codegen.CodeGenUtils._ -import org.apache.flink.table.planner.codegen.GenerateUtils.{generateCallIfArgsNotNull, generateCallIfArgsNullable, generateNonNullField, generateNullLiteral, generateStringResultCallIfArgsNotNull} +import org.apache.flink.table.planner.codegen.GenerateUtils._ import org.apache.flink.table.planner.codegen.calls.ScalarOperatorGens._ import org.apache.flink.table.planner.functions.sql.FlinkSqlOperatorTable._ import org.apache.flink.table.planner.functions.sql.SqlDefaultOperator @@ -200,6 +200,22 @@ object StringCallGen { isCharacterString(operands(1).resultType) => fallibleMethodGen(BuiltInMethods.STRING_TO_TIMESTAMP_WITH_FORMAT) + case TO_TIMESTAMP_LTZ if operands.size == 1 && isCharacterString(operands.head.resultType) => + fallibleMethodGen(BuiltInMethods.STRING_TO_TIMESTAMP_LTZ) + + case TO_TIMESTAMP_LTZ + if operands.size == 2 && + isCharacterString(operands.head.resultType) && + isCharacterString(operands(1).resultType) => + fallibleMethodGen(BuiltInMethods.STRING_TO_TIMESTAMP_LTZ_WITH_FORMAT) + + case TO_TIMESTAMP_LTZ + if operands.size == 3 && + isCharacterString(operands.head.resultType) && + isCharacterString(operands(1).resultType) && + isCharacterString(operands(2).resultType) => + fallibleMethodGen(BuiltInMethods.STRING_TO_TIMESTAMP_LTZ_WITH_FORMAT_WITH_TIME_ZONE) + case UNIX_TIMESTAMP if operands.size == 1 && isCharacterString(operands.head.resultType) => methodGen(BuiltInMethods.UNIX_TIMESTAMP_STR) diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/TimeFunctionsITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/TimeFunctionsITCase.java index b53101913a99c..edc4af29d8ff6 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/TimeFunctionsITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/TimeFunctionsITCase.java @@ -38,6 +38,7 @@ import static org.apache.flink.table.api.DataTypes.DATE; import static org.apache.flink.table.api.DataTypes.DAY; import static org.apache.flink.table.api.DataTypes.DOUBLE; +import static org.apache.flink.table.api.DataTypes.FLOAT; import static org.apache.flink.table.api.DataTypes.HOUR; import static org.apache.flink.table.api.DataTypes.INT; import static org.apache.flink.table.api.DataTypes.INTERVAL; @@ -751,16 +752,22 @@ private Stream toTimestampLtzTestCases() { 1234, -100, null, - DecimalDataUtils.castFrom(-Double.MAX_VALUE, 38, 18)) + DecimalDataUtils.castFrom(-Double.MAX_VALUE, 38, 18), + 100.01) .andDataTypes( - DOUBLE(), BIGINT(), BIGINT(), BIGINT(), DataTypes.DECIMAL(38, 18)) - .testResult( - toTimestampLtz($("f0"), literal(0)), - "TO_TIMESTAMP_LTZ(f0, 0)", - LocalDateTime.of(1970, 1, 1, 0, 1, 40) + DOUBLE(), + BIGINT(), + BIGINT(), + BIGINT(), + DataTypes.DECIMAL(38, 18), + FLOAT()) + .testResult( + toTimestampLtz($("f0")), + "TO_TIMESTAMP_LTZ(f0)", + LocalDateTime.of(1970, 1, 1, 0, 0, 0, 100000000) .atZone(ZoneOffset.UTC) .toInstant(), - TIMESTAMP_LTZ(0).nullable()) + TIMESTAMP_LTZ(3).nullable()) .testResult( toTimestampLtz($("f1"), literal(3)), "TO_TIMESTAMP_LTZ(f1, 3)", @@ -774,7 +781,7 @@ private Stream toTimestampLtzTestCases() { LocalDateTime.of(1969, 12, 31, 23, 58, 20) .atZone(ZoneOffset.UTC) .toInstant(), - TIMESTAMP_LTZ(0).nullable()) + TIMESTAMP_LTZ(3).nullable()) .testResult( toTimestampLtz($("f3"), literal(3)), "TO_TIMESTAMP_LTZ(f3, 3)", @@ -783,8 +790,15 @@ private Stream toTimestampLtzTestCases() { .testResult( toTimestampLtz($("f4"), literal(0)), "TO_TIMESTAMP_LTZ(-" + Double.MAX_VALUE + ", 0)", - null, // expecting NULL result - TIMESTAMP_LTZ(0).nullable()) + null, + TIMESTAMP_LTZ(3).nullable()) + .testResult( + toTimestampLtz($("f5"), literal(3)), + "TO_TIMESTAMP_LTZ(f5, 3)", + LocalDateTime.of(1970, 1, 1, 0, 0, 0, 100000000) + .atZone(ZoneOffset.UTC) + .toInstant(), + TIMESTAMP_LTZ(3).nullable()) .testResult( toTimestampLtz("2023-01-01 00:00:00"), "TO_TIMESTAMP_LTZ('2023-01-01 00:00:00')", @@ -796,7 +810,7 @@ private Stream toTimestampLtzTestCases() { toTimestampLtz("01/01/2023 00:00:00", "dd/MM/yyyy HH:mm:ss"), "TO_TIMESTAMP_LTZ('01/01/2023 00:00:00', 'dd/MM/yyyy HH:mm:ss')", LocalDateTime.of(2023, 1, 1, 0, 0, 0) - .atZone(ZoneOffset.UTC) // why UTC? + .atZone(ZoneOffset.UTC) .toInstant(), TIMESTAMP_LTZ(3).nullable()) .testResult( @@ -862,6 +876,14 @@ private Stream toTimestampLtzTestCases() { LocalDateTime.of(2023, 1, 1, 0, 0, 0) .atZone(ZoneId.of("America/Los_Angeles")) .toInstant(), + TIMESTAMP_LTZ(3).nullable()) + .testResult( + toTimestampLtz( + "un-parsable timestamp", + literal("yyyy-MM-dd HH:mm:ss"), + literal("UTC")), + "TO_TIMESTAMP_LTZ('invalid timestamp', 'yyyy-MM-dd HH:mm:ss', 'UTC')", + null, TIMESTAMP_LTZ(3).nullable())); } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/TemporalTypesTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/TemporalTypesTest.scala index 39ced7abdb29b..a948e5f9cb536 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/TemporalTypesTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/TemporalTypesTest.scala @@ -1140,41 +1140,41 @@ class TemporalTypesTest extends ExpressionTestBase { tableConfig.setLocalTimeZone(ZoneId.of("Asia/Shanghai")) // INT -> TIMESTAMP_LTZ - testAllApis(toTimestampLtz(100, 0), "TO_TIMESTAMP_LTZ(100, 0)", "1970-01-01 08:01:40") + testAllApis(toTimestampLtz(100, 0), "TO_TIMESTAMP_LTZ(100, 0)", "1970-01-01 08:01:40.000") // TINYINT -> TIMESTAMP_LTZ testAllApis( toTimestampLtz(100.cast(DataTypes.TINYINT()), 0), "TO_TIMESTAMP_LTZ(CAST(100 AS TINYINT), 0)", - "1970-01-01 08:01:40") + "1970-01-01 08:01:40.000") // BIGINT -> TIMESTAMP_LTZ testAllApis( toTimestampLtz(100.cast(DataTypes.BIGINT()), 0), "TO_TIMESTAMP_LTZ(CAST(100 AS BIGINT), 0)", - "1970-01-01 08:01:40") + "1970-01-01 08:01:40.000") // FLOAT -> TIMESTAMP_LTZ testAllApis( toTimestampLtz(100.01.cast(DataTypes.FLOAT()), 0), "TO_TIMESTAMP_LTZ(CAST(100.01 AS FLOAT), 0)", - "1970-01-01 08:01:40") + "1970-01-01 08:01:40.010") // DOUBLE -> TIMESTAMP_LTZ testAllApis( toTimestampLtz(100.123.cast(DataTypes.DOUBLE()), 0), "TO_TIMESTAMP_LTZ(CAST(100.123 AS DOUBLE), 0)", - "1970-01-01 08:01:40") + "1970-01-01 08:01:40.123") // DECIMAL -> TIMESTAMP_LTZ testAllApis( toTimestampLtz(100.cast(DataTypes.DECIMAL(38, 18)), 0), "TO_TIMESTAMP_LTZ(100, 0)", - "1970-01-01 08:01:40") + "1970-01-01 08:01:40.000") testAllApis( toTimestampLtz(-100.cast(DataTypes.DECIMAL(38, 18)), 0), "TO_TIMESTAMP_LTZ(-100, 0)", - "1970-01-01 07:58:20") + "1970-01-01 07:58:20.000") // keep scale testAllApis(toTimestampLtz(1234, 3), "TO_TIMESTAMP_LTZ(1234, 3)", "1970-01-01 08:00:01.234") @@ -1185,13 +1185,13 @@ class TemporalTypesTest extends ExpressionTestBase { @Test def testToTimestampLtzUTC(): Unit = { tableConfig.setLocalTimeZone(ZoneId.of("UTC")) - testAllApis(toTimestampLtz(100, 0), "TO_TIMESTAMP_LTZ(100, 0)", "1970-01-01 00:01:40") + testAllApis(toTimestampLtz(100, 0), "TO_TIMESTAMP_LTZ(100, 0)", "1970-01-01 00:01:40.000") - testAllApis(toTimestampLtz(100, 0), "TO_TIMESTAMP_LTZ(100, 0)", "1970-01-01 00:01:40") + testAllApis(toTimestampLtz(100, 0), "TO_TIMESTAMP_LTZ(100, 0)", "1970-01-01 00:01:40.000") testAllApis(toTimestampLtz(1234, 3), "TO_TIMESTAMP_LTZ(1234, 3)", "1970-01-01 00:00:01.234") - testAllApis(toTimestampLtz(-100, 0), "TO_TIMESTAMP_LTZ(-100, 0)", "1969-12-31 23:58:20") + testAllApis(toTimestampLtz(-100, 0), "TO_TIMESTAMP_LTZ(-100, 0)", "1969-12-31 23:58:20.000") } @Test @@ -1202,21 +1202,21 @@ class TemporalTypesTest extends ExpressionTestBase { testAllApis( toTimestampLtz(JInt.MIN_VALUE.cast(DataTypes.INT()), 0), s"TO_TIMESTAMP_LTZ(CAST(${JInt.MIN_VALUE} AS INTEGER), 0)", - "1901-12-13 20:45:52") + "1901-12-13 20:45:52.000") testAllApis( toTimestampLtz(JInt.MAX_VALUE.cast(DataTypes.INT()), 0), s"TO_TIMESTAMP_LTZ(CAST(${JInt.MAX_VALUE} AS INTEGER), 0)", - "2038-01-19 03:14:07") + "2038-01-19 03:14:07.000") // TINYINT testAllApis( toTimestampLtz(-128.cast(DataTypes.TINYINT()), 0), s"TO_TIMESTAMP_LTZ(CAST(-128 AS TINYINT), 0)", - "1969-12-31 23:57:52") + "1969-12-31 23:57:52.000") testAllApis( toTimestampLtz(127.cast(DataTypes.TINYINT()), 0), s"TO_TIMESTAMP_LTZ(CAST(127 AS TINYINT), 0)", - "1970-01-01 00:02:07") + "1970-01-01 00:02:07.000") // BIGINT testAllApis( @@ -1282,11 +1282,6 @@ class TemporalTypesTest extends ExpressionTestBase { s"TO_TIMESTAMP_LTZ(253402300800000, 3)", "NULL") - // test invalid number of arguments - testExpectedSqlException( - "TO_TIMESTAMP_LTZ(123)", - "Invalid number of arguments to function 'TO_TIMESTAMP_LTZ'. Was expecting 2 arguments") - // invalid precision testExpectedAllApisException( toTimestampLtz(12, 1), @@ -1308,29 +1303,42 @@ class TemporalTypesTest extends ExpressionTestBase { // invalid type for the first input testExpectedSqlException( "TO_TIMESTAMP_LTZ('test_string_type', 0)", - "Cannot apply 'TO_TIMESTAMP_LTZ' to arguments of type" + - " 'TO_TIMESTAMP_LTZ(, )'. Supported form(s):" + - " 'TO_TIMESTAMP_LTZ(, )'", + "SQL validation failed. From line 1, column 8 to line 1, column 46: Cannot apply " + + "'TO_TIMESTAMP_LTZ' to arguments of type 'TO_TIMESTAMP_LTZ(, )'. " + + "Supported form(s): 'TO_TIMESTAMP_LTZ()'\n" + + "'TO_TIMESTAMP_LTZ()'\n" + + "'TO_TIMESTAMP_LTZ(, )'\n" + + "'TO_TIMESTAMP_LTZ(, )'\n" + + "'TO_TIMESTAMP_LTZ(, , )'", classOf[ValidationException] ) + testExpectedTableApiException( toTimestampLtz("test_string_type", 0), - "Unsupported argument type. " + - "Expected type of family 'NUMERIC' but actual type was 'CHAR(16) NOT NULL'" + "Invalid input arguments. Expected signatures are:\n" + + "toTimestampLtz()\n" + + "toTimestampLtz(, )\n" + + "toTimestampLtz(, , )\n" + + "toTimestampLtz()\n" + + "toTimestampLtz(, )" ) // invalid type for the second input testExpectedSqlException( "TO_TIMESTAMP_LTZ(123, 'test_string_type')", - "Cannot apply 'TO_TIMESTAMP_LTZ' to arguments of type" + - " 'TO_TIMESTAMP_LTZ(, )'. Supported form(s):" + - " 'TO_TIMESTAMP_LTZ(, )'" + "SQL validation failed. From line 1, column 8 to line 1, column 48: Cannot apply " + + "'TO_TIMESTAMP_LTZ' to arguments of type 'TO_TIMESTAMP_LTZ(, )'. " + + "Supported form(s): 'TO_TIMESTAMP_LTZ()'\n" + + "'TO_TIMESTAMP_LTZ()'\n" + + "'TO_TIMESTAMP_LTZ(, )'\n" + + "'TO_TIMESTAMP_LTZ(, )'\n" + + "'TO_TIMESTAMP_LTZ(, , )'" ) testExpectedTableApiException( toTimestampLtz(123, "test_string_type"), - "Unsupported argument type. " + - "Expected type of family 'INTEGER_NUMERIC' but actual type was 'CHAR(16) NOT NULL'" + "Invalid function call:\n" + + "toTimestampLtz(INT NOT NULL, CHAR(16) NOT NULL" ) } diff --git a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/ToTimestampLtzFunction.java b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/ToTimestampLtzFunction.java index edb662d97002d..344db3e040f45 100644 --- a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/ToTimestampLtzFunction.java +++ b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/ToTimestampLtzFunction.java @@ -26,22 +26,51 @@ import org.apache.flink.table.functions.SpecializedFunction; import org.apache.flink.table.utils.DateTimeUtils; +import javax.annotation.Nullable; + /** * Implementation of {@link BuiltInFunctionDefinitions#TO_TIMESTAMP_LTZ}. * - *

Supported function signatures: TO_TIMESTAMP_LTZ(numeric, precision) -> - * TIMESTAMP_LTZ(precision) TO_TIMESTAMP_LTZ(string) -> TIMESTAMP_LTZ(3) TO_TIMESTAMP_LTZ(string, - * format) -> TIMESTAMP_LTZ(3) TO_TIMESTAMP_LTZ(string, format, timezone) -> TIMESTAMP_LTZ(3) - * TO_TIMESTAMP_LTZ(numeric) -> TIMESTAMP_LTZ(3) + *

A function that converts various time formats to TIMESTAMP_LTZ type. + * + *

Supported function signatures: + * + *

    + *
  • {@code TO_TIMESTAMP_LTZ(numeric)} -> TIMESTAMP_LTZ(3)
    + * Converts numeric epoch time in milliseconds to timestamp with local timezone + *
  • {@code TO_TIMESTAMP_LTZ(numeric, precision)} -> TIMESTAMP_LTZ(precision)
    + * Converts numeric epoch time to timestamp with specified precision (0 as seconds, 3 as + * milliseconds) + *
  • {@code TO_TIMESTAMP_LTZ(timestamp)} -> TIMESTAMP_LTZ(3)
    + * Parses string timestamp using default format 'yyyy-MM-dd HH:mm:ss' + *
  • {@code TO_TIMESTAMP_LTZ(timestamp, format)} -> TIMESTAMP_LTZ(3)
    + * Parses string timestamp using input string of format + *
  • {@code TO_TIMESTAMP_LTZ(timestamp, format, timezone)} -> TIMESTAMP_LTZ(3)
    + * Parses string timestamp using input strings of format and timezone + *
+ * + *

Example: + * + *

{@code
+ * TO_TIMESTAMP_LTZ('2023-01-01 10:00:00')}  // Parses string using default format
+ * TO_TIMESTAMP_LTZ(1234567890123)}  // Converts epoch milliseconds
+ * TO_TIMESTAMP_LTZ(1234567890, 0)     // Converts epoch seconds
+ * TO_TIMESTAMP_LTZ(1234567890123, 3)  // Converts epoch milliseconds
+ * TO_TIMESTAMP_LTZ('2023-01-01 10:00:00')  // Parses string using default format
+ * TO_TIMESTAMP_LTZ('2023-01-01T10:00:00', 'yyyy-MM-dd\'T\'HH:mm:ss')} // Parses string using input format
+ * TO_TIMESTAMP_LTZ('2023-01-01 10:00:00', 'yyyy-MM-dd HH:mm:ss', 'UTC')} // Parses string using input format and timezone
+ * }
*/ @Internal public class ToTimestampLtzFunction extends BuiltInScalarFunction { + private static final int DEFAULT_PRECISION = 3; + public ToTimestampLtzFunction(SpecializedFunction.SpecializedContext context) { super(BuiltInFunctionDefinitions.TO_TIMESTAMP_LTZ, context); } - public TimestampData eval(Integer epoch, Integer precision) { + public @Nullable TimestampData eval(Integer epoch, Integer precision) { if (epoch == null || precision == null) { return null; } @@ -49,7 +78,7 @@ public TimestampData eval(Integer epoch, Integer precision) { return DateTimeUtils.toTimestampData(epoch, precision); } - public TimestampData eval(Long epoch, Integer precision) { + public @Nullable TimestampData eval(Long epoch, Integer precision) { if (epoch == null || precision == null) { return null; } @@ -57,7 +86,7 @@ public TimestampData eval(Long epoch, Integer precision) { return DateTimeUtils.toTimestampData(epoch, precision); } - public TimestampData eval(Double epoch, Integer precision) { + public @Nullable TimestampData eval(Double epoch, Integer precision) { if (epoch == null || precision == null) { return null; } @@ -65,21 +94,21 @@ public TimestampData eval(Double epoch, Integer precision) { return DateTimeUtils.toTimestampData(epoch, precision); } - public TimestampData eval(Float value, Integer precision) { + public @Nullable TimestampData eval(Float value, Integer precision) { if (value == null || precision == null) { return null; } - return DateTimeUtils.toTimestampData(value.longValue(), precision); + return DateTimeUtils.toTimestampData(value.doubleValue(), precision); } - public TimestampData eval(Byte value, Integer precision) { + public @Nullable TimestampData eval(Byte value, Integer precision) { if (value == null || precision == null) { return null; } return DateTimeUtils.toTimestampData(value.longValue(), precision); } - public TimestampData eval(DecimalData epoch, Integer precision) { + public @Nullable TimestampData eval(DecimalData epoch, Integer precision) { if (epoch == null || precision == null) { return null; } @@ -92,7 +121,7 @@ public TimestampData eval(Integer epoch) { return null; } - return eval(epoch, 3); + return DateTimeUtils.toTimestampData(epoch); } public TimestampData eval(Long epoch) { @@ -100,7 +129,7 @@ public TimestampData eval(Long epoch) { return null; } - return eval(epoch, 3); + return DateTimeUtils.toTimestampData(epoch); } public TimestampData eval(Float epoch) { @@ -108,7 +137,7 @@ public TimestampData eval(Float epoch) { return null; } - return eval(epoch, 3); + return DateTimeUtils.toTimestampData(epoch); } public TimestampData eval(Byte epoch) { @@ -116,7 +145,7 @@ public TimestampData eval(Byte epoch) { return null; } - return eval(epoch, 3); + return DateTimeUtils.toTimestampData(epoch); } public TimestampData eval(Double epoch) { @@ -124,7 +153,7 @@ public TimestampData eval(Double epoch) { return null; } - return eval(epoch, 3); + return DateTimeUtils.toTimestampData(epoch); } public TimestampData eval(DecimalData epoch) { @@ -132,11 +161,10 @@ public TimestampData eval(DecimalData epoch) { return null; } - return eval(epoch, 3); + return DateTimeUtils.toTimestampData(epoch); } - // Parse timestamp with default format - public TimestampData eval(StringData timestamp) { + public @Nullable TimestampData eval(StringData timestamp) { if (timestamp == null) { return null; } @@ -144,8 +172,7 @@ public TimestampData eval(StringData timestamp) { return DateTimeUtils.parseTimestampData(timestamp.toString()); } - // Parse timestamp with custom format - public TimestampData eval(StringData timestamp, StringData format) { + public @Nullable TimestampData eval(StringData timestamp, StringData format) { if (timestamp == null || format == null) { return null; } @@ -153,13 +180,13 @@ public TimestampData eval(StringData timestamp, StringData format) { return DateTimeUtils.parseTimestampData(timestamp.toString(), format.toString()); } - // Parse timestamp with format and timezone - public TimestampData eval(StringData timestamp, StringData format, StringData timezone) { + public @Nullable TimestampData eval( + StringData timestamp, StringData format, StringData timezone) { if (timestamp == null || format == null || timezone == null) { return null; } - return DateTimeUtils.toTimestampData( + return DateTimeUtils.parseTimestampData( timestamp.toString(), format.toString(), timezone.toString()); } }