diff --git a/common/utils/src/main/resources/error/error-conditions.json b/common/utils/src/main/resources/error/error-conditions.json index e1603a25b070d..9db49c6d7cf4f 100644 --- a/common/utils/src/main/resources/error/error-conditions.json +++ b/common/utils/src/main/resources/error/error-conditions.json @@ -3430,6 +3430,11 @@ "expects a string literal, but got ." ] }, + "TIMETRUNC_UNIT" : { + "message" : [ + "expects one of the units 'HOUR', 'MINUTE', 'SECOND', 'MILLISECOND', 'MICROSECOND', but got ''." + ] + }, "ZERO_INDEX" : { "message" : [ "expects %1$, %2$ and so on, but got %0$." diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionRegistry.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionRegistry.scala index 557c949ec112d..572165e87961f 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionRegistry.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionRegistry.scala @@ -675,6 +675,7 @@ object FunctionRegistry { expression[WindowTime]("window_time"), expression[MakeDate]("make_date"), expression[MakeTime]("make_time"), + expression[TimeTrunc]("time_trunc"), expressionBuilder("make_timestamp", MakeTimestampExpressionBuilder), expression[TryMakeTimestamp]("try_make_timestamp"), expression[MonthName]("monthname"), diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/timeExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/timeExpressions.scala index ce2bda0956872..0f868f4e98dcb 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/timeExpressions.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/timeExpressions.scala @@ -630,3 +630,56 @@ case class SubtractTimes(left: Expression, right: Expression) newLeft: Expression, newRight: Expression): SubtractTimes = copy(left = newLeft, right = newRight) } + +// scalastyle:off line.size.limit +@ExpressionDescription( + usage = """ + _FUNC_(unit, time) - Returns `time` truncated to the `unit`. + """, + arguments = """ + Arguments: + * unit - the unit to truncate to + - "HOUR" - zero out the minutes and seconds with fraction part + - "MINUTE" - zero out the seconds with fraction part + - "SECOND" - zero out the fraction part of seconds + - "MILLISECOND" - zero out the microseconds + - "MICROSECOND" - zero out the nanoseconds + * time - a TIME expression + """, + examples = """ + Examples: + > SELECT _FUNC_('HOUR', TIME'09:32:05.359'); + 09:00:00 + > SELECT _FUNC_('MILLISECOND', TIME'09:32:05.123456'); + 09:32:05.123 + """, + group = "datetime_funcs", + since = "4.1.0") +// scalastyle:on line.size.limit +case class TimeTrunc(unit: Expression, time: Expression) + extends BinaryExpression with RuntimeReplaceable with ImplicitCastInputTypes { + + override def left: Expression = unit + override def right: Expression = time + + override def inputTypes: Seq[AbstractDataType] = + Seq(StringTypeWithCollation(supportsTrimCollation = true), AnyTimeType) + + override def dataType: DataType = time.dataType + + override def prettyName: String = "time_trunc" + + override protected def withNewChildrenInternal( + newUnit: Expression, newTime: Expression): TimeTrunc = + copy(unit = newUnit, time = newTime) + + override def replacement: Expression = { + StaticInvoke( + classOf[DateTimeUtils.type], + dataType, + "timeTrunc", + Seq(unit, time), + Seq(unit.dataType, time.dataType) + ) + } +} diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala index 4c28217de68fb..60a3285eb4402 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala @@ -508,6 +508,30 @@ object DateTimeUtils extends SparkDateTimeUtils { } } + /** + * Returns time truncated to the unit specified by the level. + */ + private def parseTimeTruncLevel(level: UTF8String): ChronoUnit = { + assert(level != null, "Truncation level cannot be null") + level.toString.toUpperCase(Locale.ROOT) match { + case "HOUR" => ChronoUnit.HOURS + case "MINUTE" => ChronoUnit.MINUTES + case "SECOND" => ChronoUnit.SECONDS + case "MILLISECOND" => ChronoUnit.MILLIS + case "MICROSECOND" => ChronoUnit.MICROS + case _ => + throw QueryExecutionErrors.invalidTimeTruncUnitError("time_trunc", level.toString) + } + } + + /** + * Returns time truncated to the unit specified by the level. Trunc level is parsed directly to + * corresponding ChronoUnits. Note that only levels from 'MICROSECOND' to 'HOUR' are supported. + */ + def timeTrunc(level: UTF8String, nanos: Long): Long = { + localTimeToNanos(nanosToLocalTime(nanos).truncatedTo(parseTimeTruncLevel(level))) + } + /** * Returns the truncate level, could be from TRUNC_TO_MICROSECOND to TRUNC_TO_YEAR, * or TRUNC_INVALID, TRUNC_INVALID means unsupported truncate level. diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala index 680d7b4b172db..a320bb334e3ca 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala @@ -3067,6 +3067,21 @@ private[sql] object QueryExecutionErrors extends QueryErrorsBase with ExecutionE ) } + // Throws a SparkIllegalArgumentException when an invalid time truncation unit is specified. + // Note that the supported units are: HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND. + def invalidTimeTruncUnitError( + functionName: String, + invalidValue: String): Throwable = { + new SparkIllegalArgumentException( + errorClass = "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + messageParameters = Map( + "functionName" -> toSQLId(functionName), + "parameter" -> toSQLId("unit"), + "invalidValue" -> toSQLValue(invalidValue) + ) + ) + } + // Throws a SparkRuntimeException when a CHECK constraint is violated, including details of the // violation. This is a Java-friendly version of the above method. def checkViolationJava( diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/TimeExpressionsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/TimeExpressionsSuite.scala index 05288dcb98ccf..44dacccf1074c 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/TimeExpressionsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/TimeExpressionsSuite.scala @@ -19,11 +19,12 @@ package org.apache.spark.sql.catalyst.expressions import java.time.{Duration, LocalTime} -import org.apache.spark.{SPARK_DOC_ROOT, SparkDateTimeException, SparkFunSuite} +import org.apache.spark.{SPARK_DOC_ROOT, SparkDateTimeException, SparkFunSuite, SparkIllegalArgumentException} import org.apache.spark.sql.AnalysisException import org.apache.spark.sql.catalyst.analysis.TypeCheckResult.{DataTypeMismatch, TypeCheckSuccess} import org.apache.spark.sql.catalyst.expressions.Cast.{toSQLId, toSQLValue} import org.apache.spark.sql.catalyst.util.DateTimeTestUtils._ +import org.apache.spark.sql.catalyst.util.SparkDateTimeUtils.localTimeToNanos import org.apache.spark.sql.types.{DayTimeIntervalType, Decimal, DecimalType, IntegerType, StringType, TimeType} import org.apache.spark.sql.types.DayTimeIntervalType.{DAY, HOUR, SECOND} @@ -418,4 +419,108 @@ class TimeExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper { } } } + + test("SPARK-51554: TimeTrunc") { + // Test cases for different truncation units - 09:32:05.359123. + val testTime = localTime(9, 32, 5, 359123) + + // Test HOUR truncation. + checkEvaluation( + TimeTrunc(Literal("HOUR"), Literal(testTime, TimeType())), + localTime(9, 0, 0, 0) + ) + // Test MINUTE truncation. + checkEvaluation( + TimeTrunc(Literal("MINUTE"), Literal(testTime, TimeType())), + localTime(9, 32, 0, 0) + ) + // Test SECOND truncation. + checkEvaluation( + TimeTrunc(Literal("SECOND"), Literal(testTime, TimeType())), + localTime(9, 32, 5, 0) + ) + // Test MILLISECOND truncation. + checkEvaluation( + TimeTrunc(Literal("MILLISECOND"), Literal(testTime, TimeType())), + localTime(9, 32, 5, 359000) + ) + // Test MICROSECOND truncation. + checkEvaluation( + TimeTrunc(Literal("MICROSECOND"), Literal(testTime, TimeType())), + testTime + ) + + // Test case-insensitive units. + checkEvaluation( + TimeTrunc(Literal("hour"), Literal(testTime, TimeType())), + localTime(9, 0, 0, 0) + ) + checkEvaluation( + TimeTrunc(Literal("Hour"), Literal(testTime, TimeType())), + localTime(9, 0, 0, 0) + ) + checkEvaluation( + TimeTrunc(Literal("hoUR"), Literal(testTime, TimeType())), + localTime(9, 0, 0, 0) + ) + + // Test invalid units. + val invalidUnits: Seq[String] = Seq("MS", "INVALID", "ABC", "XYZ", " ", "") + invalidUnits.foreach { unit => + checkError( + exception = intercept[SparkIllegalArgumentException] { + TimeTrunc(Literal(unit), Literal(testTime, TimeType())).eval() + }, + condition = "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + parameters = Map( + "functionName" -> "`time_trunc`", + "parameter" -> "`unit`", + "invalidValue" -> s"'$unit'" + ) + ) + } + + // Test null inputs. + checkEvaluation( + TimeTrunc(Literal.create(null, StringType), Literal(testTime, TimeType())), + null + ) + checkEvaluation( + TimeTrunc(Literal("HOUR"), Literal.create(null, TimeType())), + null + ) + checkEvaluation( + TimeTrunc(Literal.create(null, StringType), Literal.create(null, TimeType())), + null + ) + + // Test edge cases. + val midnightTime = localTime(0, 0, 0, 0) + val supportedUnits: Seq[String] = Seq("HOUR", "MINUTE", "SECOND", "MILLISECOND", "MICROSECOND") + supportedUnits.foreach { unit => + checkEvaluation( + TimeTrunc(Literal(unit), Literal(midnightTime, TimeType())), + midnightTime + ) + } + + val maxTime = localTimeToNanos(LocalTime.of(23, 59, 59, 999999999)) + checkEvaluation( + TimeTrunc(Literal("HOUR"), Literal(maxTime, TimeType())), + localTime(23, 0, 0, 0) + ) + checkEvaluation( + TimeTrunc(Literal("MICROSECOND"), Literal(maxTime, TimeType())), + localTimeToNanos(LocalTime.of(23, 59, 59, 999999000)) + ) + + // Test precision loss. + val timeWithMicroPrecision = localTime(15, 30, 45, 123456) + val timeTruncMin = TimeTrunc(Literal("MINUTE"), Literal(timeWithMicroPrecision, TimeType(3))) + assert(timeTruncMin.dataType == TimeType(3)) + checkEvaluation(timeTruncMin, localTime(15, 30, 0, 0)) + val timeTruncSec = TimeTrunc(Literal("SECOND"), Literal(timeWithMicroPrecision, TimeType(3))) + assert(timeTruncSec.dataType == TimeType(3)) + checkEvaluation(timeTruncSec, localTime(15, 30, 45, 0)) + } } diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala index 13e6a7d79ad22..5862d394653fa 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala @@ -766,6 +766,43 @@ class DateTimeUtilsSuite extends SparkFunSuite with Matchers with SQLHelper { } } + test("SPARK-51554: time truncation using timeTrunc") { + // 01:02:03.400500600 + val input = localTimeToNanos(LocalTime.of(1, 2, 3, 400500600)) + // Truncate the minutes, seconds, and fractions of seconds. Result is: 01:00:00. + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("HOUR"), input) === 3600000000000L) + // Truncate the seconds and fractions of seconds. Result is: 01:02:00. + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("MINUTE"), input) === 3720000000000L) + // Truncate the fractions of seconds. Result is: 01:02:03. + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("SECOND"), input) === 3723000000000L) + // Truncate the milliseconds. Result is: 01:02:03.400. + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("MILLISECOND"), input) === 3723400000000L) + // Truncate the microseconds. Result is: 01:02:03.400500. + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("MICROSECOND"), input) === 3723400500000L) + + // 00:00:00 + val midnight = localTimeToNanos(LocalTime.MIDNIGHT) + // Midnight time remains the same for any truncation. + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("HOUR"), midnight) === 0) + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("MINUTE"), midnight) === 0) + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("SECOND"), midnight) === 0) + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("MILLISECOND"), midnight) === 0) + assert(DateTimeUtils.timeTrunc(UTF8String.fromString("MICROSECOND"), midnight) === 0) + + // Unsupported truncation levels. + Seq("DAY", "WEEK", "MONTH", "QUARTER", "YEAR", "INVALID", "ABC", "XYZ", "MS", " ", ""). + map(UTF8String.fromString).foreach { level => + intercept[IllegalArgumentException] { + DateTimeUtils.timeTrunc(level, input) + DateTimeUtils.timeTrunc(level, midnight) + } + } + // Null truncation level is not allowed. + intercept[AssertionError] { + DateTimeUtils.timeTrunc(null, input) + } + } + test("SPARK-35664: microseconds to LocalDateTime") { assert(microsToLocalDateTime(0) == LocalDateTime.parse("1970-01-01T00:00:00")) assert(microsToLocalDateTime(100) == LocalDateTime.parse("1970-01-01T00:00:00.0001")) diff --git a/sql/core/src/test/resources/sql-functions/sql-expression-schema.md b/sql/core/src/test/resources/sql-functions/sql-expression-schema.md index af2181fbbba73..7b5c2c8264708 100644 --- a/sql/core/src/test/resources/sql-functions/sql-expression-schema.md +++ b/sql/core/src/test/resources/sql-functions/sql-expression-schema.md @@ -342,6 +342,7 @@ | org.apache.spark.sql.catalyst.expressions.Subtract | - | SELECT 2 - 1 | struct<(2 - 1):int> | | org.apache.spark.sql.catalyst.expressions.Tan | tan | SELECT tan(0) | struct | | org.apache.spark.sql.catalyst.expressions.Tanh | tanh | SELECT tanh(0) | struct | +| org.apache.spark.sql.catalyst.expressions.TimeTrunc | time_trunc | SELECT time_trunc('HOUR', TIME'09:32:05.359') | struct | | org.apache.spark.sql.catalyst.expressions.TimeWindow | window | SELECT a, window.start, window.end, count(*) as cnt FROM VALUES ('A1', '2021-01-01 00:00:00'), ('A1', '2021-01-01 00:04:30'), ('A1', '2021-01-01 00:06:00'), ('A2', '2021-01-01 00:01:00') AS tab(a, b) GROUP by a, window(b, '5 minutes') ORDER BY a, start | struct | | org.apache.spark.sql.catalyst.expressions.ToBinary | to_binary | SELECT to_binary('abc', 'utf-8') | struct | | org.apache.spark.sql.catalyst.expressions.ToCharacterBuilder | to_char | SELECT to_char(454, '999') | struct | diff --git a/sql/core/src/test/resources/sql-tests/analyzer-results/time.sql.out b/sql/core/src/test/resources/sql-tests/analyzer-results/time.sql.out index df6cef2f62f8c..ffaead8ba78de 100644 --- a/sql/core/src/test/resources/sql-tests/analyzer-results/time.sql.out +++ b/sql/core/src/test/resources/sql-tests/analyzer-results/time.sql.out @@ -7,6 +7,14 @@ CreateViewCommand `time_view`, select '11:53:26.038344' time_str, 'HH:mm:ss.SSSS +- OneRowRelation +-- !query +create temporary view trunc_time_view as select time'11:53:26.038344' time_val, 'MINUTE' unit +-- !query analysis +CreateViewCommand `trunc_time_view`, select time'11:53:26.038344' time_val, 'MINUTE' unit, false, false, LocalTempView, UNSUPPORTED, true + +- Project [11:53:26.038344 AS time_val#x, MINUTE AS unit#x] + +- OneRowRelation + + -- !query select time '16:39:45\t' -- !query analysis @@ -450,6 +458,403 @@ Project [cast(11:59:59.999999 as time(6)) AS CAST(TIME '11:59:59.999999' AS TIME +- OneRowRelation +-- !query +SELECT time_trunc('HOUR', time'12:34:56') +-- !query analysis +Project [time_trunc(HOUR, 12:34:56) AS time_trunc(HOUR, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MINUTE', time'12:34:56') +-- !query analysis +Project [time_trunc(MINUTE, 12:34:56) AS time_trunc(MINUTE, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('SECOND', time'12:34:56') +-- !query analysis +Project [time_trunc(SECOND, 12:34:56) AS time_trunc(SECOND, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MILLISECOND', time'12:34:56') +-- !query analysis +Project [time_trunc(MILLISECOND, 12:34:56) AS time_trunc(MILLISECOND, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MICROSECOND', time'12:34:56') +-- !query analysis +Project [time_trunc(MICROSECOND, 12:34:56) AS time_trunc(MICROSECOND, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('HOUR', time'12:34:56.1') +-- !query analysis +Project [time_trunc(HOUR, 12:34:56.1) AS time_trunc(HOUR, TIME '12:34:56.1')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MINUTE', time'12:34:56.1') +-- !query analysis +Project [time_trunc(MINUTE, 12:34:56.1) AS time_trunc(MINUTE, TIME '12:34:56.1')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('SECOND', time'12:34:56.1') +-- !query analysis +Project [time_trunc(SECOND, 12:34:56.1) AS time_trunc(SECOND, TIME '12:34:56.1')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MILLISECOND', time'12:34:56.1') +-- !query analysis +Project [time_trunc(MILLISECOND, 12:34:56.1) AS time_trunc(MILLISECOND, TIME '12:34:56.1')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MICROSECOND', time'12:34:56.1') +-- !query analysis +Project [time_trunc(MICROSECOND, 12:34:56.1) AS time_trunc(MICROSECOND, TIME '12:34:56.1')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('HOUR', time'12:34:56.123456') +-- !query analysis +Project [time_trunc(HOUR, 12:34:56.123456) AS time_trunc(HOUR, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MINUTE', time'12:34:56.123456') +-- !query analysis +Project [time_trunc(MINUTE, 12:34:56.123456) AS time_trunc(MINUTE, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('SECOND', time'12:34:56.123456') +-- !query analysis +Project [time_trunc(SECOND, 12:34:56.123456) AS time_trunc(SECOND, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MILLISECOND', time'12:34:56.123456') +-- !query analysis +Project [time_trunc(MILLISECOND, 12:34:56.123456) AS time_trunc(MILLISECOND, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MICROSECOND', time'12:34:56.123456') +-- !query analysis +Project [time_trunc(MICROSECOND, 12:34:56.123456) AS time_trunc(MICROSECOND, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('HOUR', time'12:34:56.123456789') +-- !query analysis +Project [time_trunc(HOUR, 12:34:56.123456) AS time_trunc(HOUR, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MINUTE', time'12:34:56.123456789') +-- !query analysis +Project [time_trunc(MINUTE, 12:34:56.123456) AS time_trunc(MINUTE, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('SECOND', time'12:34:56.123456789') +-- !query analysis +Project [time_trunc(SECOND, 12:34:56.123456) AS time_trunc(SECOND, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MILLISECOND', time'12:34:56.123456789') +-- !query analysis +Project [time_trunc(MILLISECOND, 12:34:56.123456) AS time_trunc(MILLISECOND, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MICROSECOND', time'12:34:56.123456789') +-- !query analysis +Project [time_trunc(MICROSECOND, 12:34:56.123456) AS time_trunc(MICROSECOND, TIME '12:34:56.123456')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('hour', time'12:34:56') +-- !query analysis +Project [time_trunc(hour, 12:34:56) AS time_trunc(hour, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MiNuTe', time'12:34:56') +-- !query analysis +Project [time_trunc(MiNuTe, 12:34:56) AS time_trunc(MiNuTe, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('sEcOnD', time'12:34:56') +-- !query analysis +Project [time_trunc(sEcOnD, 12:34:56) AS time_trunc(sEcOnD, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('Millisecond', time'12:34:56') +-- !query analysis +Project [time_trunc(Millisecond, 12:34:56) AS time_trunc(Millisecond, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('microseconD', time'12:34:56') +-- !query analysis +Project [time_trunc(microseconD, 12:34:56) AS time_trunc(microseconD, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('HOUR', time'00:00:00') +-- !query analysis +Project [time_trunc(HOUR, 00:00:00) AS time_trunc(HOUR, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MINUTE', time'00:00:00') +-- !query analysis +Project [time_trunc(MINUTE, 00:00:00) AS time_trunc(MINUTE, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('SECOND', time'00:00:00') +-- !query analysis +Project [time_trunc(SECOND, 00:00:00) AS time_trunc(SECOND, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MILLISECOND', time'00:00:00') +-- !query analysis +Project [time_trunc(MILLISECOND, 00:00:00) AS time_trunc(MILLISECOND, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MICROSECOND', time'00:00:00') +-- !query analysis +Project [time_trunc(MICROSECOND, 00:00:00) AS time_trunc(MICROSECOND, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('HOUR', time'00:00:00.000000001') +-- !query analysis +Project [time_trunc(HOUR, 00:00:00) AS time_trunc(HOUR, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MINUTE', time'00:00:00.000000001') +-- !query analysis +Project [time_trunc(MINUTE, 00:00:00) AS time_trunc(MINUTE, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('SECOND', time'00:00:00.000000001') +-- !query analysis +Project [time_trunc(SECOND, 00:00:00) AS time_trunc(SECOND, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MILLISECOND', time'00:00:00.000000001') +-- !query analysis +Project [time_trunc(MILLISECOND, 00:00:00) AS time_trunc(MILLISECOND, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MICROSECOND', time'00:00:00.000000001') +-- !query analysis +Project [time_trunc(MICROSECOND, 00:00:00) AS time_trunc(MICROSECOND, TIME '00:00:00')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('HOUR', time'23:59:59.999999999') +-- !query analysis +Project [time_trunc(HOUR, 23:59:59.999999) AS time_trunc(HOUR, TIME '23:59:59.999999')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MINUTE', time'23:59:59.999999999') +-- !query analysis +Project [time_trunc(MINUTE, 23:59:59.999999) AS time_trunc(MINUTE, TIME '23:59:59.999999')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('SECOND', time'23:59:59.999999999') +-- !query analysis +Project [time_trunc(SECOND, 23:59:59.999999) AS time_trunc(SECOND, TIME '23:59:59.999999')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MILLISECOND', time'23:59:59.999999999') +-- !query analysis +Project [time_trunc(MILLISECOND, 23:59:59.999999) AS time_trunc(MILLISECOND, TIME '23:59:59.999999')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MICROSECOND', time'23:59:59.999999999') +-- !query analysis +Project [time_trunc(MICROSECOND, 23:59:59.999999) AS time_trunc(MICROSECOND, TIME '23:59:59.999999')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('', time'12:34:56') +-- !query analysis +Project [time_trunc(, 12:34:56) AS time_trunc(, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc(' ', time'12:34:56') +-- !query analysis +Project [time_trunc( , 12:34:56) AS time_trunc( , TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('MS', time'12:34:56') +-- !query analysis +Project [time_trunc(MS, 12:34:56) AS time_trunc(MS, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('DAY', time'12:34:56') +-- !query analysis +Project [time_trunc(DAY, 12:34:56) AS time_trunc(DAY, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('WEEK', time'12:34:56') +-- !query analysis +Project [time_trunc(WEEK, 12:34:56) AS time_trunc(WEEK, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('ABCD', time'12:34:56') +-- !query analysis +Project [time_trunc(ABCD, 12:34:56) AS time_trunc(ABCD, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('QUARTER', time'12:34:56') +-- !query analysis +Project [time_trunc(QUARTER, 12:34:56) AS time_trunc(QUARTER, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('INVALID', time'12:34:56') +-- !query analysis +Project [time_trunc(INVALID, 12:34:56) AS time_trunc(INVALID, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('INVALID_UNIT', time'12:34:56') +-- !query analysis +Project [time_trunc(INVALID_UNIT, 12:34:56) AS time_trunc(INVALID_UNIT, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('HOUR', NULL) +-- !query analysis +Project [time_trunc(HOUR, cast(null as time(6))) AS time_trunc(HOUR, NULL)#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc(NULL, time'12:34:56') +-- !query analysis +Project [time_trunc(cast(null as string), 12:34:56) AS time_trunc(NULL, TIME '12:34:56')#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc(NULL, NULL) +-- !query analysis +Project [time_trunc(cast(null as string), cast(null as time(6))) AS time_trunc(NULL, NULL)#x] ++- OneRowRelation + + +-- !query +SELECT time_trunc('HOUR', time_val) FROM trunc_time_view +-- !query analysis +Project [time_trunc(HOUR, time_val#x) AS time_trunc(HOUR, time_val)#x] ++- SubqueryAlias trunc_time_view + +- View (`trunc_time_view`, [time_val#x, unit#x]) + +- Project [cast(time_val#x as time(6)) AS time_val#x, cast(unit#x as string) AS unit#x] + +- Project [11:53:26.038344 AS time_val#x, MINUTE AS unit#x] + +- OneRowRelation + + +-- !query +SELECT time_trunc(unit, time'12:34:56') FROM trunc_time_view +-- !query analysis +Project [time_trunc(unit#x, 12:34:56) AS time_trunc(unit, TIME '12:34:56')#x] ++- SubqueryAlias trunc_time_view + +- View (`trunc_time_view`, [time_val#x, unit#x]) + +- Project [cast(time_val#x as time(6)) AS time_val#x, cast(unit#x as string) AS unit#x] + +- Project [11:53:26.038344 AS time_val#x, MINUTE AS unit#x] + +- OneRowRelation + + +-- !query +SELECT time_trunc(unit, time_val) FROM trunc_time_view +-- !query analysis +Project [time_trunc(unit#x, time_val#x) AS time_trunc(unit, time_val)#x] ++- SubqueryAlias trunc_time_view + +- View (`trunc_time_view`, [time_val#x, unit#x]) + +- Project [cast(time_val#x as time(6)) AS time_val#x, cast(unit#x as string) AS unit#x] + +- Project [11:53:26.038344 AS time_val#x, MINUTE AS unit#x] + +- OneRowRelation + + -- !query SELECT time("12:34:56") -- !query analysis diff --git a/sql/core/src/test/resources/sql-tests/inputs/time.sql b/sql/core/src/test/resources/sql-tests/inputs/time.sql index 6fced5cc2f514..44d44e2b4a426 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/time.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/time.sql @@ -2,6 +2,8 @@ create temporary view time_view as select '11:53:26.038344' time_str, 'HH:mm:ss.SSSSSS' fmt_str; +create temporary view trunc_time_view as select time'11:53:26.038344' time_val, 'MINUTE' unit; + select time '16:39:45\t'; select to_time(null), to_time('01:02:03'), to_time('23-59-59.999999', 'HH-mm-ss.SSSSSS'); @@ -79,6 +81,77 @@ SELECT cast(cast('12:00' as time(0)) as time(2)); SELECT cast(('23:59:59.001001' :: time(6)) as time(4)); SELECT cast(time'11:59:59.999999' as time without time zone); +-- SPARK-51554: time truncation. +SELECT time_trunc('HOUR', time'12:34:56'); +SELECT time_trunc('MINUTE', time'12:34:56'); +SELECT time_trunc('SECOND', time'12:34:56'); +SELECT time_trunc('MILLISECOND', time'12:34:56'); +SELECT time_trunc('MICROSECOND', time'12:34:56'); + +-- SPARK-51554: time truncation with various time precisions. +SELECT time_trunc('HOUR', time'12:34:56.1'); +SELECT time_trunc('MINUTE', time'12:34:56.1'); +SELECT time_trunc('SECOND', time'12:34:56.1'); +SELECT time_trunc('MILLISECOND', time'12:34:56.1'); +SELECT time_trunc('MICROSECOND', time'12:34:56.1'); +SELECT time_trunc('HOUR', time'12:34:56.123456'); +SELECT time_trunc('MINUTE', time'12:34:56.123456'); +SELECT time_trunc('SECOND', time'12:34:56.123456'); +SELECT time_trunc('MILLISECOND', time'12:34:56.123456'); +SELECT time_trunc('MICROSECOND', time'12:34:56.123456'); +SELECT time_trunc('HOUR', time'12:34:56.123456789'); +SELECT time_trunc('MINUTE', time'12:34:56.123456789'); +SELECT time_trunc('SECOND', time'12:34:56.123456789'); +SELECT time_trunc('MILLISECOND', time'12:34:56.123456789'); +SELECT time_trunc('MICROSECOND', time'12:34:56.123456789'); + +-- SPARK-51554: time truncation with various unit cases. +SELECT time_trunc('hour', time'12:34:56'); +SELECT time_trunc('MiNuTe', time'12:34:56'); +SELECT time_trunc('sEcOnD', time'12:34:56'); +SELECT time_trunc('Millisecond', time'12:34:56'); +SELECT time_trunc('microseconD', time'12:34:56'); + +-- SPARK-51554: time truncation with zero time. +SELECT time_trunc('HOUR', time'00:00:00'); +SELECT time_trunc('MINUTE', time'00:00:00'); +SELECT time_trunc('SECOND', time'00:00:00'); +SELECT time_trunc('MILLISECOND', time'00:00:00'); +SELECT time_trunc('MICROSECOND', time'00:00:00'); +-- SPARK-51554: time truncation with small time. +SELECT time_trunc('HOUR', time'00:00:00.000000001'); +SELECT time_trunc('MINUTE', time'00:00:00.000000001'); +SELECT time_trunc('SECOND', time'00:00:00.000000001'); +SELECT time_trunc('MILLISECOND', time'00:00:00.000000001'); +SELECT time_trunc('MICROSECOND', time'00:00:00.000000001'); +-- SPARK-51554: time truncation with max time. +SELECT time_trunc('HOUR', time'23:59:59.999999999'); +SELECT time_trunc('MINUTE', time'23:59:59.999999999'); +SELECT time_trunc('SECOND', time'23:59:59.999999999'); +SELECT time_trunc('MILLISECOND', time'23:59:59.999999999'); +SELECT time_trunc('MICROSECOND', time'23:59:59.999999999'); + +-- SPARK-51554: time truncation with invalid unit. +SELECT time_trunc('', time'12:34:56'); +SELECT time_trunc(' ', time'12:34:56'); +SELECT time_trunc('MS', time'12:34:56'); +SELECT time_trunc('DAY', time'12:34:56'); +SELECT time_trunc('WEEK', time'12:34:56'); +SELECT time_trunc('ABCD', time'12:34:56'); +SELECT time_trunc('QUARTER', time'12:34:56'); +SELECT time_trunc('INVALID', time'12:34:56'); +SELECT time_trunc('INVALID_UNIT', time'12:34:56'); + +-- SPARK-51554: time truncation with null inputs. +SELECT time_trunc('HOUR', NULL); +SELECT time_trunc(NULL, time'12:34:56'); +SELECT time_trunc(NULL, NULL); + +-- SPARK-51554: time truncation with table columns. +SELECT time_trunc('HOUR', time_val) FROM trunc_time_view; +SELECT time_trunc(unit, time'12:34:56') FROM trunc_time_view; +SELECT time_trunc(unit, time_val) FROM trunc_time_view; + -- SPARK-51562: test time function (i.e. alias for casting to time type). SELECT time("12:34:56"); SELECT time("12:34:56.789"); diff --git a/sql/core/src/test/resources/sql-tests/results/time.sql.out b/sql/core/src/test/resources/sql-tests/results/time.sql.out index 4a87c026e6df1..bad832c0c7263 100644 --- a/sql/core/src/test/resources/sql-tests/results/time.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/time.sql.out @@ -7,6 +7,14 @@ struct<> +-- !query +create temporary view trunc_time_view as select time'11:53:26.038344' time_val, 'MINUTE' unit +-- !query schema +struct<> +-- !query output + + + -- !query select time '16:39:45\t' -- !query schema @@ -556,6 +564,527 @@ struct 11:59:59.999999 +-- !query +SELECT time_trunc('HOUR', time'12:34:56') +-- !query schema +struct +-- !query output +12:00:00 + + +-- !query +SELECT time_trunc('MINUTE', time'12:34:56') +-- !query schema +struct +-- !query output +12:34:00 + + +-- !query +SELECT time_trunc('SECOND', time'12:34:56') +-- !query schema +struct +-- !query output +12:34:56 + + +-- !query +SELECT time_trunc('MILLISECOND', time'12:34:56') +-- !query schema +struct +-- !query output +12:34:56 + + +-- !query +SELECT time_trunc('MICROSECOND', time'12:34:56') +-- !query schema +struct +-- !query output +12:34:56 + + +-- !query +SELECT time_trunc('HOUR', time'12:34:56.1') +-- !query schema +struct +-- !query output +12:00:00 + + +-- !query +SELECT time_trunc('MINUTE', time'12:34:56.1') +-- !query schema +struct +-- !query output +12:34:00 + + +-- !query +SELECT time_trunc('SECOND', time'12:34:56.1') +-- !query schema +struct +-- !query output +12:34:56 + + +-- !query +SELECT time_trunc('MILLISECOND', time'12:34:56.1') +-- !query schema +struct +-- !query output +12:34:56.1 + + +-- !query +SELECT time_trunc('MICROSECOND', time'12:34:56.1') +-- !query schema +struct +-- !query output +12:34:56.1 + + +-- !query +SELECT time_trunc('HOUR', time'12:34:56.123456') +-- !query schema +struct +-- !query output +12:00:00 + + +-- !query +SELECT time_trunc('MINUTE', time'12:34:56.123456') +-- !query schema +struct +-- !query output +12:34:00 + + +-- !query +SELECT time_trunc('SECOND', time'12:34:56.123456') +-- !query schema +struct +-- !query output +12:34:56 + + +-- !query +SELECT time_trunc('MILLISECOND', time'12:34:56.123456') +-- !query schema +struct +-- !query output +12:34:56.123 + + +-- !query +SELECT time_trunc('MICROSECOND', time'12:34:56.123456') +-- !query schema +struct +-- !query output +12:34:56.123456 + + +-- !query +SELECT time_trunc('HOUR', time'12:34:56.123456789') +-- !query schema +struct +-- !query output +12:00:00 + + +-- !query +SELECT time_trunc('MINUTE', time'12:34:56.123456789') +-- !query schema +struct +-- !query output +12:34:00 + + +-- !query +SELECT time_trunc('SECOND', time'12:34:56.123456789') +-- !query schema +struct +-- !query output +12:34:56 + + +-- !query +SELECT time_trunc('MILLISECOND', time'12:34:56.123456789') +-- !query schema +struct +-- !query output +12:34:56.123 + + +-- !query +SELECT time_trunc('MICROSECOND', time'12:34:56.123456789') +-- !query schema +struct +-- !query output +12:34:56.123456 + + +-- !query +SELECT time_trunc('hour', time'12:34:56') +-- !query schema +struct +-- !query output +12:00:00 + + +-- !query +SELECT time_trunc('MiNuTe', time'12:34:56') +-- !query schema +struct +-- !query output +12:34:00 + + +-- !query +SELECT time_trunc('sEcOnD', time'12:34:56') +-- !query schema +struct +-- !query output +12:34:56 + + +-- !query +SELECT time_trunc('Millisecond', time'12:34:56') +-- !query schema +struct +-- !query output +12:34:56 + + +-- !query +SELECT time_trunc('microseconD', time'12:34:56') +-- !query schema +struct +-- !query output +12:34:56 + + +-- !query +SELECT time_trunc('HOUR', time'00:00:00') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('MINUTE', time'00:00:00') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('SECOND', time'00:00:00') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('MILLISECOND', time'00:00:00') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('MICROSECOND', time'00:00:00') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('HOUR', time'00:00:00.000000001') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('MINUTE', time'00:00:00.000000001') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('SECOND', time'00:00:00.000000001') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('MILLISECOND', time'00:00:00.000000001') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('MICROSECOND', time'00:00:00.000000001') +-- !query schema +struct +-- !query output +00:00:00 + + +-- !query +SELECT time_trunc('HOUR', time'23:59:59.999999999') +-- !query schema +struct +-- !query output +23:00:00 + + +-- !query +SELECT time_trunc('MINUTE', time'23:59:59.999999999') +-- !query schema +struct +-- !query output +23:59:00 + + +-- !query +SELECT time_trunc('SECOND', time'23:59:59.999999999') +-- !query schema +struct +-- !query output +23:59:59 + + +-- !query +SELECT time_trunc('MILLISECOND', time'23:59:59.999999999') +-- !query schema +struct +-- !query output +23:59:59.999 + + +-- !query +SELECT time_trunc('MICROSECOND', time'23:59:59.999999999') +-- !query schema +struct +-- !query output +23:59:59.999999 + + +-- !query +SELECT time_trunc('', time'12:34:56') +-- !query schema +struct<> +-- !query output +org.apache.spark.SparkIllegalArgumentException +{ + "errorClass" : "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + "sqlState" : "22023", + "messageParameters" : { + "functionName" : "`time_trunc`", + "invalidValue" : "''", + "parameter" : "`unit`" + } +} + + +-- !query +SELECT time_trunc(' ', time'12:34:56') +-- !query schema +struct<> +-- !query output +org.apache.spark.SparkIllegalArgumentException +{ + "errorClass" : "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + "sqlState" : "22023", + "messageParameters" : { + "functionName" : "`time_trunc`", + "invalidValue" : "' '", + "parameter" : "`unit`" + } +} + + +-- !query +SELECT time_trunc('MS', time'12:34:56') +-- !query schema +struct<> +-- !query output +org.apache.spark.SparkIllegalArgumentException +{ + "errorClass" : "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + "sqlState" : "22023", + "messageParameters" : { + "functionName" : "`time_trunc`", + "invalidValue" : "'MS'", + "parameter" : "`unit`" + } +} + + +-- !query +SELECT time_trunc('DAY', time'12:34:56') +-- !query schema +struct<> +-- !query output +org.apache.spark.SparkIllegalArgumentException +{ + "errorClass" : "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + "sqlState" : "22023", + "messageParameters" : { + "functionName" : "`time_trunc`", + "invalidValue" : "'DAY'", + "parameter" : "`unit`" + } +} + + +-- !query +SELECT time_trunc('WEEK', time'12:34:56') +-- !query schema +struct<> +-- !query output +org.apache.spark.SparkIllegalArgumentException +{ + "errorClass" : "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + "sqlState" : "22023", + "messageParameters" : { + "functionName" : "`time_trunc`", + "invalidValue" : "'WEEK'", + "parameter" : "`unit`" + } +} + + +-- !query +SELECT time_trunc('ABCD', time'12:34:56') +-- !query schema +struct<> +-- !query output +org.apache.spark.SparkIllegalArgumentException +{ + "errorClass" : "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + "sqlState" : "22023", + "messageParameters" : { + "functionName" : "`time_trunc`", + "invalidValue" : "'ABCD'", + "parameter" : "`unit`" + } +} + + +-- !query +SELECT time_trunc('QUARTER', time'12:34:56') +-- !query schema +struct<> +-- !query output +org.apache.spark.SparkIllegalArgumentException +{ + "errorClass" : "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + "sqlState" : "22023", + "messageParameters" : { + "functionName" : "`time_trunc`", + "invalidValue" : "'QUARTER'", + "parameter" : "`unit`" + } +} + + +-- !query +SELECT time_trunc('INVALID', time'12:34:56') +-- !query schema +struct<> +-- !query output +org.apache.spark.SparkIllegalArgumentException +{ + "errorClass" : "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + "sqlState" : "22023", + "messageParameters" : { + "functionName" : "`time_trunc`", + "invalidValue" : "'INVALID'", + "parameter" : "`unit`" + } +} + + +-- !query +SELECT time_trunc('INVALID_UNIT', time'12:34:56') +-- !query schema +struct<> +-- !query output +org.apache.spark.SparkIllegalArgumentException +{ + "errorClass" : "INVALID_PARAMETER_VALUE.TIMETRUNC_UNIT", + "sqlState" : "22023", + "messageParameters" : { + "functionName" : "`time_trunc`", + "invalidValue" : "'INVALID_UNIT'", + "parameter" : "`unit`" + } +} + + +-- !query +SELECT time_trunc('HOUR', NULL) +-- !query schema +struct +-- !query output +NULL + + +-- !query +SELECT time_trunc(NULL, time'12:34:56') +-- !query schema +struct +-- !query output +NULL + + +-- !query +SELECT time_trunc(NULL, NULL) +-- !query schema +struct +-- !query output +NULL + + +-- !query +SELECT time_trunc('HOUR', time_val) FROM trunc_time_view +-- !query schema +struct +-- !query output +11:00:00 + + +-- !query +SELECT time_trunc(unit, time'12:34:56') FROM trunc_time_view +-- !query schema +struct +-- !query output +12:34:00 + + +-- !query +SELECT time_trunc(unit, time_val) FROM trunc_time_view +-- !query schema +struct +-- !query output +11:53:00 + + -- !query SELECT time("12:34:56") -- !query schema diff --git a/sql/core/src/test/scala/org/apache/spark/sql/collation/CollationExpressionWalkerSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/collation/CollationExpressionWalkerSuite.scala index ba1be1b03ef84..71961988b2f36 100644 --- a/sql/core/src/test/scala/org/apache/spark/sql/collation/CollationExpressionWalkerSuite.scala +++ b/sql/core/src/test/scala/org/apache/spark/sql/collation/CollationExpressionWalkerSuite.scala @@ -384,7 +384,8 @@ class CollationExpressionWalkerSuite extends SparkFunSuite with SharedSparkSessi "sha2", "sha", "crc32", - "ascii" + "ascii", + "time_trunc" ) logInfo("Total number of expression: " + expressionCounter)