You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by we...@apache.org on 2026/08/19 17:59:47 UTC
(spark) branch master updated: [SPARK-57818][SQL] Support nanosecond-precision NTZ timestamps in convert_timezone
This is an automated email from the ASF dual-hosted git repository.
cloud-fan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/master by this push:
new 31f5f5abb8d7 [SPARK-57818][SQL] Support nanosecond-precision NTZ timestamps in convert_timezone
31f5f5abb8d7 is described below
commit 31f5f5abb8d7ba5b75338df6e9810ee8677d3ff1
Author: Rakesh Raushan <ra...@gmail.com>
AuthorDate: Thu Aug 20 01:59:31 2026 +0800
[SPARK-57818][SQL] Support nanosecond-precision NTZ timestamps in convert_timezone
### What changes were proposed in this pull request?
Extends ConvertTimezone (convert_timezone()) to accept nanosecond-precision timestamps (TIMESTAMP_NTZ(p), p in [7, 9]) as input, alongside the existing microsecond TimestampType inputs. ConvertTimezone would returns a nanosecond NTZ value at the same precision with the remainder preserved in case of TIMESTAMP_NTZ(p). Existing microsecond computation won't be affected by this change.
### Why are the changes needed?
ConvertTimezone types its source as TimestampNTZType only and returns TimestampNTZType, using convertTimestampNtzToAnotherTz(Long, ...). It rejects all nanosecond timestamp types. We should allow TIMESTAMP_NTZ(p).
### Does this PR introduce _any_ user-facing change?
Yes, convert_timezone function would accept TIMESTAMP_NTZ(p) and return NTZ value at same precision with remainder preserved.
### How was this patch tested?
Added unit tests in DateExpressionsSuite.scala and also generated golden files.
### Was this patch authored or co-authored using generative AI tooling?
Used claude code for writing UTs and generating golden files.
Closes #57008 from iRakson/SPARK-57818.
Authored-by: Rakesh Raushan <ra...@gmail.com>
Signed-off-by: Wenchen Fan <we...@databricks.com>
---
.../catalyst/expressions/datetimeExpressions.scala | 58 +++++++++++--
.../expressions/DateExpressionsSuite.scala | 98 ++++++++++++++++++++++
.../analyzer-results/timestamp-ltz-nanos.sql.out | 25 ++++++
.../analyzer-results/timestamp-ntz-nanos.sql.out | 23 +++++
.../sql-tests/inputs/timestamp-ltz-nanos.sql | 6 ++
.../sql-tests/inputs/timestamp-ntz-nanos.sql | 10 +++
.../sql-tests/results/timestamp-ltz-nanos.sql.out | 27 ++++++
.../sql-tests/results/timestamp-ntz-nanos.sql.out | 26 ++++++
8 files changed, 266 insertions(+), 7 deletions(-)
diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/datetimeExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/datetimeExpressions.scala
index ed895f909d3b..fea6a813c292 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/datetimeExpressions.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/datetimeExpressions.scala
@@ -4894,20 +4894,64 @@ case class ConvertTimezone(
Seq(
StringTypeWithCollation(supportsTrimCollation = true),
StringTypeWithCollation(supportsTrimCollation = true),
- TimestampNTZType)
- override def dataType: DataType = TimestampNTZType
-
- override def nullSafeEval(srcTz: Any, tgtTz: Any, micros: Any): Any = {
- DateTimeUtils.convertTimestampNtzToAnotherTz(
+ TypeCollection(TimestampNTZType, AnyTimestampNanoType))
+
+ // sourceTs's requiredType, as actually enforced by this method: TypeCollection includes
+ // AnyTimestampNanoType (rather than an NTZ-only nanos type) only so that an LTZ(p) input is
+ // accepted here and rejected below with the friendly message, instead of being silently
+ // widened to TimestampNTZType by the generic datetime-to-datetime implicit cast rule. That
+ // makes AnyTimestampNanoType.simpleString (which lists timestamp_ltz(p)) leak into the
+ // generic super.checkInputDataTypes() mismatch message for this param when sourceTs is some
+ // unrelated type (e.g. an int), even though LTZ(p) is never actually accepted. Route both the
+ // generic mismatch and the explicit LTZ rejection through the same message so they agree.
+ private def wrongSourceTsType: DataTypeMismatch = DataTypeMismatch(
+ errorSubClass = "UNEXPECTED_INPUT_TYPE",
+ messageParameters = Map(
+ "paramIndex" -> ordinalNumber(2),
+ "requiredType" -> toSQLType("(timestamp_ntz or timestamp_ntz(p) with p in [7, 9])"),
+ "inputSql" -> toSQLExpr(sourceTs),
+ "inputType" -> toSQLType(sourceTs.dataType)))
+
+ override def checkInputDataTypes(): TypeCheckResult = super.checkInputDataTypes() match {
+ case TypeCheckSuccess if sourceTs.dataType.isInstanceOf[TimestampLTZNanosType] =>
+ wrongSourceTsType
+ case DataTypeMismatch("UNEXPECTED_INPUT_TYPE", params)
+ if params.get("paramIndex").contains(ordinalNumber(2)) =>
+ wrongSourceTsType
+ case result => result
+ }
+
+ private def isTsNanos: Boolean = sourceTs.dataType.isInstanceOf[AnyTimestampNanoType]
+
+ // Preserves the exact source precision (7/8/9); AnyTimestampNanoType.defaultConcreteType would
+ // always widen the result to precision 9.
+ override def dataType: DataType = if (isTsNanos) sourceTs.dataType else TimestampNTZType
+
+ override def nullSafeEval(srcTz: Any, tgtTz: Any, ts: Any): Any = {
+ val micros = if (isTsNanos) ts.asInstanceOf[TimestampNanosVal].epochMicros else ts
+ val convertedTs = DateTimeUtils.convertTimestampNtzToAnotherTz(
srcTz.asInstanceOf[UTF8String].toString,
tgtTz.asInstanceOf[UTF8String].toString,
micros.asInstanceOf[Long])
+ if (isTsNanos) {
+ TimestampNanosVal.fromParts(
+ convertedTs, ts.asInstanceOf[TimestampNanosVal].nanosWithinMicro)
+ } else convertedTs
}
override def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
- defineCodeGen(ctx, ev, (srcTz, tgtTz, micros) =>
- s"""$dtu.convertTimestampNtzToAnotherTz($srcTz.toString(), $tgtTz.toString(), $micros)""")
+ if (isTsNanos) {
+ defineCodeGen(ctx, ev, (srcTz, tgtTz, ts) => {
+ val convertedMicros = s"$dtu.convertTimestampNtzToAnotherTz(" +
+ s"$srcTz.toString(), $tgtTz.toString(), $ts.epochMicros)"
+ s"org.apache.spark.unsafe.types.TimestampNanosVal.fromParts(" +
+ s"$convertedMicros, $ts.nanosWithinMicro)"
+ })
+ } else {
+ defineCodeGen(ctx, ev, (srcTz, tgtTz, micros) =>
+ s"""$dtu.convertTimestampNtzToAnotherTz($srcTz.toString(), $tgtTz.toString(), $micros)""")
+ }
}
override def prettyName: String = "convert_timezone"
diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala
index 691263e5b77d..ef90fa5d68a5 100644
--- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala
+++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala
@@ -2533,6 +2533,104 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
}
}
+ test("SPARK-57818: convert_timezone over nanosecond-precision timestamps") {
+ val ntzType9 = TimestampNTZNanosType(9)
+
+ // The nanosWithinMicro remainder is carried through unchanged by a zone conversion; only the
+ // whole-microsecond part shifts with the zone offset.
+ val srcNanos = DateTimeUtils.localDateTimeToTimestampNanos(
+ LocalDateTime.parse("2022-03-27T03:00:00.123456789"), 9)
+ val expectedNanos = DateTimeUtils.localDateTimeToTimestampNanos(
+ LocalDateTime.parse("2022-03-27T04:00:00.123456789"), 9)
+ assert(srcNanos.nanosWithinMicro == expectedNanos.nanosWithinMicro)
+ checkEvaluation(
+ ConvertTimezone(
+ Literal("Europe/Brussels"),
+ Literal("Europe/Moscow"),
+ Literal.create(srcNanos, ntzType9)),
+ expectedNanos)
+
+ // Pre-epoch values exercise the negative-epoch path. The expected epochMicros is derived from
+ // the already-verified micros-only conversion (SPARK-37552 tests that path); this only checks
+ // that the nanos wiring delegates to it correctly and carries the remainder through unchanged.
+ val preEpochSrc = DateTimeUtils.localDateTimeToTimestampNanos(
+ LocalDateTime.parse("1960-01-01T00:00:00.000000001"), 9)
+ val preEpochExpectedMicros = DateTimeUtils.convertTimestampNtzToAnotherTz(
+ "Europe/Moscow", "Europe/Brussels", preEpochSrc.epochMicros)
+ checkEvaluation(
+ ConvertTimezone(
+ Literal("Europe/Moscow"),
+ Literal("Europe/Brussels"),
+ Literal.create(preEpochSrc, ntzType9)),
+ TimestampNanosVal.fromParts(preEpochExpectedMicros, preEpochSrc.nanosWithinMicro))
+
+ // Precision (7/8/9) is preserved on the result; AnyTimestampNanoType.defaultConcreteType
+ // would incorrectly always widen it to 9.
+ Seq(7, 8, 9).foreach { precision =>
+ val ntzType = TimestampNTZNanosType(precision)
+ val src = DateTimeUtils.localDateTimeToTimestampNanos(
+ LocalDateTime.parse("2022-03-27T03:00:00.123456789"), precision)
+ val convertExpr = ConvertTimezone(
+ Literal("Europe/Brussels"), Literal("Europe/Moscow"), Literal.create(src, ntzType))
+ assert(convertExpr.dataType === ntzType)
+ }
+
+ // LTZ(p) nanos values are rejected: this function is NTZ-only, matching the existing
+ // TimestampNTZType-only micro path. Unlike that micro path -- which implicitly casts a
+ // plain LTZ TimestampType argument down to TimestampNTZType -- a nanos source must not be
+ // silently reinterpreted from LTZ to NTZ, since that would drop the source time zone
+ // information without the user asking for it.
+ val ltzNanos = DateTimeUtils.instantToTimestampNanos(Instant.parse("2022-03-27T03:00:00Z"), 9)
+ val ltzMismatch = ConvertTimezone(
+ Literal("Europe/Brussels"), Literal("Europe/Moscow"),
+ Literal.create(ltzNanos, TimestampLTZNanosType(9)))
+ .checkInputDataTypes().asInstanceOf[DataTypeMismatch]
+ assert(ltzMismatch.errorSubClass == "UNEXPECTED_INPUT_TYPE")
+
+ // A wholly invalid source type (not any kind of timestamp) hits the generic type check
+ // instead of the explicit LTZ guard above; both paths must report the same requiredType,
+ // since neither actually accepts an LTZ(p) source.
+ val wrongTypeMismatch = ConvertTimezone(
+ Literal("Europe/Brussels"), Literal("Europe/Moscow"), Literal(1))
+ .checkInputDataTypes().asInstanceOf[DataTypeMismatch]
+ assert(wrongTypeMismatch.errorSubClass == "UNEXPECTED_INPUT_TYPE")
+ assert(wrongTypeMismatch.messageParameters("requiredType") ===
+ ltzMismatch.messageParameters("requiredType"))
+
+ // NULL handling: a NULL nanosecond timestamp, and NULL zone arguments with a non-NULL
+ // nanosecond timestamp.
+ checkEvaluation(
+ ConvertTimezone(
+ Literal("America/Los_Angeles"),
+ Literal("UTC"),
+ Literal.create(null, ntzType9)),
+ null)
+ checkEvaluation(
+ ConvertTimezone(
+ Literal.create(null, StringType),
+ Literal("UTC"),
+ Literal.create(srcNanos, ntzType9)),
+ null)
+ checkEvaluation(
+ ConvertTimezone(
+ Literal("America/Los_Angeles"),
+ Literal.create(null, StringType),
+ Literal.create(srcNanos, ntzType9)),
+ null)
+
+ outstandingTimezonesIds.foreach { sourceTz =>
+ outstandingTimezonesIds.foreach { targetTz =>
+ checkConsistencyBetweenInterpretedAndCodegen(
+ (_: Expression, _: Expression, sourceTs: Expression) =>
+ ConvertTimezone(
+ Literal(sourceTz),
+ Literal(targetTz),
+ sourceTs),
+ StringType, StringType, ntzType9)
+ }
+ }
+ }
+
test("SPARK-38195: add a quantity of interval units to a timestamp") {
// Check case-insensitivity
checkEvaluation(
diff --git a/sql/core/src/test/resources/sql-tests/analyzer-results/timestamp-ltz-nanos.sql.out b/sql/core/src/test/resources/sql-tests/analyzer-results/timestamp-ltz-nanos.sql.out
index 6c028a1c3127..368ddf703b92 100644
--- a/sql/core/src/test/resources/sql-tests/analyzer-results/timestamp-ltz-nanos.sql.out
+++ b/sql/core/src/test/resources/sql-tests/analyzer-results/timestamp-ltz-nanos.sql.out
@@ -716,6 +716,31 @@ Project [(2020-01-01 19:04:05.123456789 - cast(null as timestamp_ltz(9))) AS (TI
+- OneRowRelation
+-- !query
+SELECT convert_timezone('Europe/Brussels', 'Europe/Moscow',
+ '2022-03-27 03:00:00.123456789 UTC' :: timestamp_ltz(9))
+-- !query analysis
+org.apache.spark.sql.catalyst.ExtendedAnalysisException
+{
+ "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE",
+ "sqlState" : "42K09",
+ "messageParameters" : {
+ "inputSql" : "\"CAST(2022-03-27 03:00:00.123456789 UTC AS TIMESTAMP_LTZ(9))\"",
+ "inputType" : "\"TIMESTAMP_LTZ(9)\"",
+ "paramIndex" : "third",
+ "requiredType" : "\"(TIMESTAMP_NTZ OR TIMESTAMP_NTZ(P) WITH P IN [7, 9])\"",
+ "sqlExpr" : "\"convert_timezone(Europe/Brussels, Europe/Moscow, CAST(2022-03-27 03:00:00.123456789 UTC AS TIMESTAMP_LTZ(9)))\""
+ },
+ "queryContext" : [ {
+ "objectType" : "",
+ "objectName" : "",
+ "startIndex" : 8,
+ "stopIndex" : 120,
+ "fragment" : "convert_timezone('Europe/Brussels', 'Europe/Moscow',\n '2022-03-27 03:00:00.123456789 UTC' :: timestamp_ltz(9))"
+ } ]
+}
+
+
-- !query
SELECT max(c), min(c) FROM VALUES
(TIMESTAMP_LTZ '2020-01-01 00:00:00.000000001 UTC'),
diff --git a/sql/core/src/test/resources/sql-tests/analyzer-results/timestamp-ntz-nanos.sql.out b/sql/core/src/test/resources/sql-tests/analyzer-results/timestamp-ntz-nanos.sql.out
index c64be7281b47..8fa51bf5ff5c 100644
--- a/sql/core/src/test/resources/sql-tests/analyzer-results/timestamp-ntz-nanos.sql.out
+++ b/sql/core/src/test/resources/sql-tests/analyzer-results/timestamp-ntz-nanos.sql.out
@@ -644,6 +644,29 @@ Project [(2020-01-02 03:04:05.123456789 - cast(null as timestamp_ntz(9))) AS (TI
+- OneRowRelation
+-- !query
+SELECT convert_timezone('Europe/Brussels', 'Europe/Moscow',
+ TIMESTAMP_NTZ '2022-03-27 03:00:00.123456789')
+-- !query analysis
+Project [convert_timezone(Europe/Brussels, Europe/Moscow, 2022-03-27 03:00:00.123456789) AS convert_timezone(Europe/Brussels, Europe/Moscow, TIMESTAMP_NTZ '2022-03-27 03:00:00.123456789')#x]
++- OneRowRelation
+
+
+-- !query
+SELECT typeof(convert_timezone('Europe/Brussels', 'Europe/Moscow',
+ '2022-03-27 03:00:00.1234567' :: timestamp_ntz(7)))
+-- !query analysis
+Project [typeof(convert_timezone(Europe/Brussels, Europe/Moscow, cast(2022-03-27 03:00:00.1234567 as timestamp_ntz(7)))) AS typeof(convert_timezone(Europe/Brussels, Europe/Moscow, CAST(2022-03-27 03:00:00.1234567 AS TIMESTAMP_NTZ(7))))#x]
++- OneRowRelation
+
+
+-- !query
+SELECT convert_timezone('America/Los_Angeles', 'UTC', CAST(NULL AS timestamp_ntz(9)))
+-- !query analysis
+Project [convert_timezone(America/Los_Angeles, UTC, cast(null as timestamp_ntz(9))) AS convert_timezone(America/Los_Angeles, UTC, CAST(NULL AS TIMESTAMP_NTZ(9)))#x]
++- OneRowRelation
+
+
-- !query
SELECT max(c), min(c) FROM VALUES
(TIMESTAMP_NTZ '2020-01-01 00:00:00.000000001'),
diff --git a/sql/core/src/test/resources/sql-tests/inputs/timestamp-ltz-nanos.sql b/sql/core/src/test/resources/sql-tests/inputs/timestamp-ltz-nanos.sql
index 0f18303867ba..1eba70d4f4b2 100644
--- a/sql/core/src/test/resources/sql-tests/inputs/timestamp-ltz-nanos.sql
+++ b/sql/core/src/test/resources/sql-tests/inputs/timestamp-ltz-nanos.sql
@@ -205,6 +205,12 @@ SELECT TIMESTAMP_LTZ '2020-01-01 00:00:00.123456789 UTC' - TIMESTAMP_LTZ '1960-0
-- NULL operand propagates.
SELECT TIMESTAMP_LTZ '2020-01-02 03:04:05.123456789 UTC' - CAST(NULL AS timestamp_ltz(9));
+-- SPARK-57818: convert_timezone is NTZ-only, so a nanosecond LTZ(p) source is rejected rather
+-- than silently reinterpreted as NTZ (the positive TIMESTAMP_NTZ(p) path is covered in
+-- timestamp-ntz-nanos.sql).
+SELECT convert_timezone('Europe/Brussels', 'Europe/Moscow',
+ '2022-03-27 03:00:00.123456789 UTC' :: timestamp_ltz(9));
+
-- SPARK-57103: MAX / MIN over nanosecond-precision TIMESTAMP_LTZ. The aggregate preserves the
-- nanosecond type and orders by the sub-microsecond remainder; NULLs are ignored. Values are
-- rendered in the session time zone (America/Los_Angeles).
diff --git a/sql/core/src/test/resources/sql-tests/inputs/timestamp-ntz-nanos.sql b/sql/core/src/test/resources/sql-tests/inputs/timestamp-ntz-nanos.sql
index 478356098d8c..266829fe7ab2 100644
--- a/sql/core/src/test/resources/sql-tests/inputs/timestamp-ntz-nanos.sql
+++ b/sql/core/src/test/resources/sql-tests/inputs/timestamp-ntz-nanos.sql
@@ -178,6 +178,16 @@ SELECT TIMESTAMP_NTZ '2020-01-01 00:00:00.123456789' - TIMESTAMP_NTZ '1960-01-01
-- NULL operand propagates.
SELECT TIMESTAMP_NTZ '2020-01-02 03:04:05.123456789' - CAST(NULL AS timestamp_ntz(9));
+-- SPARK-57818: convert_timezone over nanosecond-precision TIMESTAMP_NTZ. The sub-microsecond
+-- remainder is carried through unchanged; only the whole-microsecond part shifts with the zone
+-- offset, and the result keeps the source's exact precision.
+SELECT convert_timezone('Europe/Brussels', 'Europe/Moscow',
+ TIMESTAMP_NTZ '2022-03-27 03:00:00.123456789');
+SELECT typeof(convert_timezone('Europe/Brussels', 'Europe/Moscow',
+ '2022-03-27 03:00:00.1234567' :: timestamp_ntz(7)));
+-- NULL nanosecond timestamp.
+SELECT convert_timezone('America/Los_Angeles', 'UTC', CAST(NULL AS timestamp_ntz(9)));
+
-- SPARK-57103: MAX / MIN over nanosecond-precision TIMESTAMP_NTZ. The aggregate preserves the
-- nanosecond type and orders by the sub-microsecond remainder (two values share the same
-- microsecond and differ only within it); NULLs are ignored.
diff --git a/sql/core/src/test/resources/sql-tests/results/timestamp-ltz-nanos.sql.out b/sql/core/src/test/resources/sql-tests/results/timestamp-ltz-nanos.sql.out
index 2d1ae31273c1..5fae7cc5041e 100644
--- a/sql/core/src/test/resources/sql-tests/results/timestamp-ltz-nanos.sql.out
+++ b/sql/core/src/test/resources/sql-tests/results/timestamp-ltz-nanos.sql.out
@@ -808,6 +808,33 @@ struct<(TIMESTAMP_LTZ '2020-01-01 19:04:05.123456789' - CAST(NULL AS TIMESTAMP_L
NULL
+-- !query
+SELECT convert_timezone('Europe/Brussels', 'Europe/Moscow',
+ '2022-03-27 03:00:00.123456789 UTC' :: timestamp_ltz(9))
+-- !query schema
+struct<>
+-- !query output
+org.apache.spark.sql.catalyst.ExtendedAnalysisException
+{
+ "errorClass" : "DATATYPE_MISMATCH.UNEXPECTED_INPUT_TYPE",
+ "sqlState" : "42K09",
+ "messageParameters" : {
+ "inputSql" : "\"CAST(2022-03-27 03:00:00.123456789 UTC AS TIMESTAMP_LTZ(9))\"",
+ "inputType" : "\"TIMESTAMP_LTZ(9)\"",
+ "paramIndex" : "third",
+ "requiredType" : "\"(TIMESTAMP_NTZ OR TIMESTAMP_NTZ(P) WITH P IN [7, 9])\"",
+ "sqlExpr" : "\"convert_timezone(Europe/Brussels, Europe/Moscow, CAST(2022-03-27 03:00:00.123456789 UTC AS TIMESTAMP_LTZ(9)))\""
+ },
+ "queryContext" : [ {
+ "objectType" : "",
+ "objectName" : "",
+ "startIndex" : 8,
+ "stopIndex" : 120,
+ "fragment" : "convert_timezone('Europe/Brussels', 'Europe/Moscow',\n '2022-03-27 03:00:00.123456789 UTC' :: timestamp_ltz(9))"
+ } ]
+}
+
+
-- !query
SELECT max(c), min(c) FROM VALUES
(TIMESTAMP_LTZ '2020-01-01 00:00:00.000000001 UTC'),
diff --git a/sql/core/src/test/resources/sql-tests/results/timestamp-ntz-nanos.sql.out b/sql/core/src/test/resources/sql-tests/results/timestamp-ntz-nanos.sql.out
index 709a64321fec..46fb523e378a 100644
--- a/sql/core/src/test/resources/sql-tests/results/timestamp-ntz-nanos.sql.out
+++ b/sql/core/src/test/resources/sql-tests/results/timestamp-ntz-nanos.sql.out
@@ -726,6 +726,32 @@ struct<(TIMESTAMP_NTZ '2020-01-02 03:04:05.123456789' - CAST(NULL AS TIMESTAMP_N
NULL
+-- !query
+SELECT convert_timezone('Europe/Brussels', 'Europe/Moscow',
+ TIMESTAMP_NTZ '2022-03-27 03:00:00.123456789')
+-- !query schema
+struct<convert_timezone(Europe/Brussels, Europe/Moscow, TIMESTAMP_NTZ '2022-03-27 03:00:00.123456789'):timestamp_ntz(9)>
+-- !query output
+2022-03-27 04:00:00.123456789
+
+
+-- !query
+SELECT typeof(convert_timezone('Europe/Brussels', 'Europe/Moscow',
+ '2022-03-27 03:00:00.1234567' :: timestamp_ntz(7)))
+-- !query schema
+struct<typeof(convert_timezone(Europe/Brussels, Europe/Moscow, CAST(2022-03-27 03:00:00.1234567 AS TIMESTAMP_NTZ(7)))):string>
+-- !query output
+timestamp_ntz(7)
+
+
+-- !query
+SELECT convert_timezone('America/Los_Angeles', 'UTC', CAST(NULL AS timestamp_ntz(9)))
+-- !query schema
+struct<convert_timezone(America/Los_Angeles, UTC, CAST(NULL AS TIMESTAMP_NTZ(9))):timestamp_ntz(9)>
+-- !query output
+NULL
+
+
-- !query
SELECT max(c), min(c) FROM VALUES
(TIMESTAMP_NTZ '2020-01-01 00:00:00.000000001'),
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org