You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by ur...@apache.org on 2026/09/10 10:48:04 UTC
(spark) branch master updated: [SPARK-59300][SQL] Support nanosecond-precision timestamps in from_utc_timestamp and to_utc_timestamp
This is an automated email from the ASF dual-hosted git repository.
uros-b 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 1db290ce3678 [SPARK-59300][SQL] Support nanosecond-precision timestamps in from_utc_timestamp and to_utc_timestamp
1db290ce3678 is described below
commit 1db290ce3678fac9e160ab51e2fc6776f2ddf15d
Author: Stevo Mitric <st...@gmail.com>
AuthorDate: Thu Sep 10 10:47:48 2026 +0000
[SPARK-59300][SQL] Support nanosecond-precision timestamps in from_utc_timestamp and to_utc_timestamp
### What changes were proposed in this pull request?
Extend from_utc_timestamp / to_utc_timestamp (the shared UTCTimestamp trait) to accept nanosecond-precision timestamps (TIMESTAMP_LTZ(p) / TIMESTAMP_NTZ(p), p in [7, 9]) alongside microsecond TimestampType. The result keeps the source's exact nanosecond type/family; a zone conversion shifts only epochMicros (offsets are whole seconds), so the sub-microsecond nanosWithinMicro remainder is preserved. Micros path unchanged. Follows date_trunc (SPARK-57821) and convert_timezone (SPARK-578 [...]
### Why are the changes needed?
These functions typed their argument as TimestampType only, narrowing a nanosecond input to microseconds and dropping the sub-microsecond digits. Nanosecond support is a P1 expression item.
### Does this PR introduce _any_ user-facing change?
Yes, behind the preview flag spark.sql.timestampNanosTypes.enabled (unreleased feature; no released version affected). The functions now return the same nanosecond type with the remainder preserved.
### How was this patch tested?
New DateExpressionsSuite case (both functions x NTZ/LTZ x p in {7,8,9}; interpreted + both codegen branches
### Was this patch authored or co-authored using generative AI tooling?
Co-authored-by: Claude Code 4.8
Closes #58576 from stevomitric/stevomitric/nanos-from-to-utc-timestamp.
Authored-by: Stevo Mitric <st...@gmail.com>
Signed-off-by: Uros Bojanic <22...@users.noreply.github.com>
---
.../catalyst/expressions/datetimeExpressions.scala | 54 +++++++++++++++++---
.../expressions/DateExpressionsSuite.scala | 58 ++++++++++++++++++++++
.../analyzer-results/timestamp-ltz-nanos.sql.out | 36 ++++++++++++++
.../analyzer-results/timestamp-ntz-nanos.sql.out | 36 ++++++++++++++
.../sql-tests/inputs/timestamp-ltz-nanos.sql | 12 +++++
.../sql-tests/inputs/timestamp-ntz-nanos.sql | 11 ++++
.../sql-tests/results/timestamp-ltz-nanos.sql.out | 41 +++++++++++++++
.../sql-tests/results/timestamp-ntz-nanos.sql.out | 41 +++++++++++++++
8 files changed, 282 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 fea6a813c292..70eed59fbdb0 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
@@ -2737,25 +2737,53 @@ sealed trait UTCTimestamp extends BinaryExpression with ImplicitCastInputTypes {
val funcName: String
override def inputTypes: Seq[AbstractDataType] =
- Seq(TimestampType, StringTypeWithCollation(supportsTrimCollation = true))
- override def dataType: DataType = TimestampType
+ Seq(
+ TypeCollection(TimestampType, AnyTimestampNanoType),
+ StringTypeWithCollation(supportsTrimCollation = true))
+
+ // Preserves the exact input type: a nanosecond source (TIMESTAMP_LTZ(p)/TIMESTAMP_NTZ(p),
+ // p in [7, 9]) yields the same nanosecond type/family, while the microsecond TimestampType path
+ // is unchanged. Matching left.dataType (rather than AnyTimestampNanoType.defaultConcreteType) is
+ // deliberate: defaultConcreteType is TimestampNTZNanosType(9), which would both widen the
+ // precision to 9 and flip a TimestampLTZNanosType source to the NTZ family. Preserving an NTZ(p)
+ // source as NTZ(p) -- while a microsecond TimestampNTZType argument is still implicitly cast to
+ // LTZ TimestampType before it reaches here -- matches date_trunc (SPARK-57821), the established
+ // convention for these TimestampType-typed functions gaining nanosecond support.
+ override def dataType: DataType = left.dataType match {
+ case _: AnyTimestampNanoType => left.dataType
+ case _ => TimestampType
+ }
+
+ // A zone shift moves only the whole-microsecond instant -- zone offsets are whole seconds -- so
+ // a nanosecond value converts its epochMicros with the exact same code as a microsecond value
+ // and carries nanosWithinMicro through unchanged. NTZ vs LTZ needs no special casing here: the
+ // shift is a pure arithmetic offset on epochMicros, independent of the zone family.
+ private def isTsNanos: Boolean = left.dataType.isInstanceOf[AnyTimestampNanoType]
override def nullSafeEval(time: Any, timezone: Any): Any = {
- func(time.asInstanceOf[Long], timezone.asInstanceOf[UTF8String].toString)
+ val tz = timezone.asInstanceOf[UTF8String].toString
+ if (isTsNanos) {
+ val v = time.asInstanceOf[TimestampNanosVal]
+ TimestampNanosVal.fromParts(func(v.epochMicros, tz), v.nanosWithinMicro)
+ } else {
+ func(time.asInstanceOf[Long], tz)
+ }
}
override def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
+ val tnv = classOf[TimestampNanosVal].getName
+ val javaType = CodeGenerator.javaType(dataType)
+ val defaultValue = CodeGenerator.defaultValue(dataType)
if (right.foldable) {
val tz = right.eval().asInstanceOf[UTF8String]
if (tz == null) {
ev.copy(code = code"""
|boolean ${ev.isNull} = true;
- |long ${ev.value} = 0;
+ |$javaType ${ev.value} = $defaultValue;
""".stripMargin)
} else {
val tzClass = classOf[ZoneId].getName
- val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
val escapedTz = StringEscapeUtils.escapeJava(tz.toString)
val tzTerm = ctx.addMutableState(tzClass, "tz",
v => s"""$v = $dtu.getZoneId("$escapedTz");""")
@@ -2765,15 +2793,27 @@ sealed trait UTCTimestamp extends BinaryExpression with ImplicitCastInputTypes {
case _: ToUTCTimestamp => (tzTerm, utcTerm)
}
val eval = left.genCode(ctx)
+ val convert = if (isTsNanos) {
+ s"$tnv.fromParts(" +
+ s"$dtu.convertTz(${eval.value}.epochMicros, $fromTz, $toTz), " +
+ s"${eval.value}.nanosWithinMicro)"
+ } else {
+ s"$dtu.convertTz(${eval.value}, $fromTz, $toTz)"
+ }
ev.copy(code = code"""
|${eval.code}
|boolean ${ev.isNull} = ${eval.isNull};
- |long ${ev.value} = 0;
+ |$javaType ${ev.value} = $defaultValue;
|if (!${ev.isNull}) {
- | ${ev.value} = $dtu.convertTz(${eval.value}, $fromTz, $toTz);
+ | ${ev.value} = $convert;
|}
""".stripMargin)
}
+ } else if (isTsNanos) {
+ defineCodeGen(ctx, ev, (timestamp, format) =>
+ s"$tnv.fromParts(" +
+ s"$dtu.$funcName($timestamp.epochMicros, $format.toString()), " +
+ s"$timestamp.nanosWithinMicro)")
} else {
defineCodeGen(ctx, ev, (timestamp, format) => {
s"""$dtu.$funcName($timestamp, $format.toString())"""
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 ef90fa5d68a5..bcff9fa53c32 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
@@ -1356,6 +1356,64 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
}
}
+ test("SPARK-59300: from_utc_timestamp and to_utc_timestamp on nanosecond timestamps") {
+ withDefaultTimeZone(UTC) {
+ val tz = LA.getId
+
+ // A zone shift moves only the whole-microsecond instant (zone offsets are whole seconds),
+ // so the sub-microsecond nanosWithinMicro is carried through unchanged, and the result keeps
+ // the source's exact nanosecond type/family (LTZ(p) stays LTZ(p), NTZ(p) stays NTZ(p)).
+ def check(
+ build: (Expression, Expression) => Expression,
+ micros: (Long, String) => Long): Unit = {
+ Seq(7, 8, 9).foreach { p =>
+ // Positive- and pre-1970 (negative-epoch) instants; the 9-digit fraction is floored to
+ // precision p, leaving a non-zero sub-microsecond remainder at every p.
+ Seq("2015-07-24T00:00:00.123456789", "1960-01-01T00:00:00.123456789").foreach { dtStr =>
+ val srcs = Seq(
+ DateTimeUtils.localDateTimeToTimestampNanos(
+ LocalDateTime.parse(dtStr), p) -> TimestampNTZNanosType(p),
+ DateTimeUtils.instantToTimestampNanos(
+ Instant.parse(dtStr + "Z"), p) -> TimestampLTZNanosType(p))
+ srcs.foreach { case (src, dt) =>
+ // The source must carry a sub-microsecond remainder for the checks below to be
+ // meaningful; the result then only shifts epochMicros and keeps that remainder.
+ assert(src.nanosWithinMicro != 0)
+ val expected =
+ TimestampNanosVal.fromParts(micros(src.epochMicros, tz), src.nanosWithinMicro)
+
+ // Foldable tz -> hand-written codegen; non-foldable tz -> defineCodeGen branch.
+ checkEvaluation(build(Literal.create(src, dt), Literal(tz)), expected)
+ checkEvaluation(
+ build(Literal.create(src, dt), NonFoldableLiteral.create(tz, StringType)), expected)
+
+ // Result keeps the exact source nanosecond type/family.
+ assert(build(Literal.create(src, dt), Literal(tz)).dataType === dt)
+
+ // NULL timestamp and NULL zone both propagate.
+ checkEvaluation(build(Literal.create(null, dt), Literal(tz)), null)
+ checkEvaluation(
+ build(Literal.create(src, dt), Literal.create(null, StringType)), null)
+ }
+ }
+
+ // Interpreted-vs-codegen parity over random remainders, through both the foldable-tz
+ // (Literal) and non-foldable-tz (NonFoldableLiteral) codegen branches.
+ Seq(TimestampNTZNanosType(p), TimestampLTZNanosType(p)).foreach { dt =>
+ checkConsistencyBetweenInterpretedAndCodegen(
+ (ts: Expression, _: Expression) => build(ts, Literal(tz)), dt, StringType)
+ checkConsistencyBetweenInterpretedAndCodegen(
+ (ts: Expression, _: Expression) =>
+ build(ts, NonFoldableLiteral.create(tz, StringType)), dt, StringType)
+ }
+ }
+ }
+
+ check(FromUTCTimestamp(_, _), DateTimeUtils.fromUTCTime(_, _))
+ check(ToUTCTimestamp(_, _), DateTimeUtils.toUTCTime(_, _))
+ }
+ }
+
test("creating values of DateType via make_date") {
Seq(true, false).foreach({ ansi =>
withSQLConf(SQLConf.ANSI_ENABLED.key -> ansi.toString) {
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 368ddf703b92..29be08f5e22b 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
@@ -741,6 +741,42 @@ org.apache.spark.sql.catalyst.ExtendedAnalysisException
}
+-- !query
+SELECT from_utc_timestamp(TIMESTAMP_LTZ '2015-07-24 00:00:00.123456789 UTC', 'Asia/Kolkata')
+-- !query analysis
+Project [from_utc_timestamp(2015-07-23 17:00:00.123456789, Asia/Kolkata) AS from_utc_timestamp(TIMESTAMP_LTZ '2015-07-23 17:00:00.123456789', Asia/Kolkata)#x]
++- OneRowRelation
+
+
+-- !query
+SELECT to_utc_timestamp(TIMESTAMP_LTZ '2015-07-24 00:00:00.123456789 UTC', 'Asia/Kolkata')
+-- !query analysis
+Project [to_utc_timestamp(2015-07-23 17:00:00.123456789, Asia/Kolkata) AS to_utc_timestamp(TIMESTAMP_LTZ '2015-07-23 17:00:00.123456789', Asia/Kolkata)#x]
++- OneRowRelation
+
+
+-- !query
+SELECT typeof(
+ to_utc_timestamp('2015-07-24 00:00:00.1234567 UTC' :: timestamp_ltz(7), 'Asia/Kolkata'))
+-- !query analysis
+Project [typeof(to_utc_timestamp(cast(2015-07-24 00:00:00.1234567 UTC as timestamp_ltz(7)), Asia/Kolkata)) AS typeof(to_utc_timestamp(CAST(2015-07-24 00:00:00.1234567 UTC AS TIMESTAMP_LTZ(7)), Asia/Kolkata))#x]
++- OneRowRelation
+
+
+-- !query
+SELECT from_utc_timestamp(CAST(NULL AS timestamp_ltz(9)), 'Asia/Kolkata')
+-- !query analysis
+Project [from_utc_timestamp(cast(null as timestamp_ltz(9)), Asia/Kolkata) AS from_utc_timestamp(CAST(NULL AS TIMESTAMP_LTZ(9)), Asia/Kolkata)#x]
++- OneRowRelation
+
+
+-- !query
+SELECT to_utc_timestamp(TIMESTAMP_LTZ '2015-07-24 00:00:00.123456789 UTC', CAST(NULL AS STRING))
+-- !query analysis
+Project [to_utc_timestamp(2015-07-23 17:00:00.123456789, cast(null as string)) AS to_utc_timestamp(TIMESTAMP_LTZ '2015-07-23 17:00:00.123456789', CAST(NULL AS STRING))#x]
++- OneRowRelation
+
+
-- !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 8fa51bf5ff5c..3418e5fe5d46 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
@@ -667,6 +667,42 @@ Project [convert_timezone(America/Los_Angeles, UTC, cast(null as timestamp_ntz(9
+- OneRowRelation
+-- !query
+SELECT from_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', 'America/Los_Angeles')
+-- !query analysis
+Project [from_utc_timestamp(2015-07-24 00:00:00.123456789, America/Los_Angeles) AS from_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', America/Los_Angeles)#x]
++- OneRowRelation
+
+
+-- !query
+SELECT to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', 'America/Los_Angeles')
+-- !query analysis
+Project [to_utc_timestamp(2015-07-24 00:00:00.123456789, America/Los_Angeles) AS to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', America/Los_Angeles)#x]
++- OneRowRelation
+
+
+-- !query
+SELECT typeof(
+ to_utc_timestamp('2015-07-24 00:00:00.1234567' :: timestamp_ntz(7), 'America/Los_Angeles'))
+-- !query analysis
+Project [typeof(to_utc_timestamp(cast(2015-07-24 00:00:00.1234567 as timestamp_ntz(7)), America/Los_Angeles)) AS typeof(to_utc_timestamp(CAST(2015-07-24 00:00:00.1234567 AS TIMESTAMP_NTZ(7)), America/Los_Angeles))#x]
++- OneRowRelation
+
+
+-- !query
+SELECT from_utc_timestamp(CAST(NULL AS timestamp_ntz(9)), 'America/Los_Angeles')
+-- !query analysis
+Project [from_utc_timestamp(cast(null as timestamp_ntz(9)), America/Los_Angeles) AS from_utc_timestamp(CAST(NULL AS TIMESTAMP_NTZ(9)), America/Los_Angeles)#x]
++- OneRowRelation
+
+
+-- !query
+SELECT to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', CAST(NULL AS STRING))
+-- !query analysis
+Project [to_utc_timestamp(2015-07-24 00:00:00.123456789, cast(null as string)) AS to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', CAST(NULL AS STRING))#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 1eba70d4f4b2..840d5126d9a7 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
@@ -211,6 +211,18 @@ SELECT TIMESTAMP_LTZ '2020-01-02 03:04:05.123456789 UTC' - CAST(NULL AS timestam
SELECT convert_timezone('Europe/Brussels', 'Europe/Moscow',
'2022-03-27 03:00:00.123456789 UTC' :: timestamp_ltz(9));
+-- SPARK-59300: from_utc_timestamp / to_utc_timestamp over nanosecond-precision TIMESTAMP_LTZ. A
+-- zone shift moves only the whole-microsecond instant, so the sub-microsecond remainder is carried
+-- through unchanged and the result keeps the source's exact LTZ precision. LTZ values render in
+-- the session time zone (America/Los_Angeles).
+SELECT from_utc_timestamp(TIMESTAMP_LTZ '2015-07-24 00:00:00.123456789 UTC', 'Asia/Kolkata');
+SELECT to_utc_timestamp(TIMESTAMP_LTZ '2015-07-24 00:00:00.123456789 UTC', 'Asia/Kolkata');
+SELECT typeof(
+ to_utc_timestamp('2015-07-24 00:00:00.1234567 UTC' :: timestamp_ltz(7), 'Asia/Kolkata'));
+-- NULL nanosecond timestamp and NULL zone both propagate.
+SELECT from_utc_timestamp(CAST(NULL AS timestamp_ltz(9)), 'Asia/Kolkata');
+SELECT to_utc_timestamp(TIMESTAMP_LTZ '2015-07-24 00:00:00.123456789 UTC', CAST(NULL AS STRING));
+
-- 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 266829fe7ab2..2f98c39f5df5 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
@@ -188,6 +188,17 @@ SELECT typeof(convert_timezone('Europe/Brussels', 'Europe/Moscow',
-- NULL nanosecond timestamp.
SELECT convert_timezone('America/Los_Angeles', 'UTC', CAST(NULL AS timestamp_ntz(9)));
+-- SPARK-59300: from_utc_timestamp / to_utc_timestamp over nanosecond-precision TIMESTAMP_NTZ. A
+-- zone shift moves only the whole-microsecond instant, so the sub-microsecond remainder is carried
+-- through unchanged and the result keeps the source's exact NTZ precision.
+SELECT from_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', 'America/Los_Angeles');
+SELECT to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', 'America/Los_Angeles');
+SELECT typeof(
+ to_utc_timestamp('2015-07-24 00:00:00.1234567' :: timestamp_ntz(7), 'America/Los_Angeles'));
+-- NULL nanosecond timestamp and NULL zone both propagate.
+SELECT from_utc_timestamp(CAST(NULL AS timestamp_ntz(9)), 'America/Los_Angeles');
+SELECT to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', CAST(NULL AS STRING));
+
-- 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 5fae7cc5041e..48453d6b5b6a 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
@@ -835,6 +835,47 @@ org.apache.spark.sql.catalyst.ExtendedAnalysisException
}
+-- !query
+SELECT from_utc_timestamp(TIMESTAMP_LTZ '2015-07-24 00:00:00.123456789 UTC', 'Asia/Kolkata')
+-- !query schema
+struct<from_utc_timestamp(TIMESTAMP_LTZ '2015-07-23 17:00:00.123456789', Asia/Kolkata):timestamp_ltz(9)>
+-- !query output
+2015-07-23 22:30:00.123456789
+
+
+-- !query
+SELECT to_utc_timestamp(TIMESTAMP_LTZ '2015-07-24 00:00:00.123456789 UTC', 'Asia/Kolkata')
+-- !query schema
+struct<to_utc_timestamp(TIMESTAMP_LTZ '2015-07-23 17:00:00.123456789', Asia/Kolkata):timestamp_ltz(9)>
+-- !query output
+2015-07-23 11:30:00.123456789
+
+
+-- !query
+SELECT typeof(
+ to_utc_timestamp('2015-07-24 00:00:00.1234567 UTC' :: timestamp_ltz(7), 'Asia/Kolkata'))
+-- !query schema
+struct<typeof(to_utc_timestamp(CAST(2015-07-24 00:00:00.1234567 UTC AS TIMESTAMP_LTZ(7)), Asia/Kolkata)):string>
+-- !query output
+timestamp_ltz(7)
+
+
+-- !query
+SELECT from_utc_timestamp(CAST(NULL AS timestamp_ltz(9)), 'Asia/Kolkata')
+-- !query schema
+struct<from_utc_timestamp(CAST(NULL AS TIMESTAMP_LTZ(9)), Asia/Kolkata):timestamp_ltz(9)>
+-- !query output
+NULL
+
+
+-- !query
+SELECT to_utc_timestamp(TIMESTAMP_LTZ '2015-07-24 00:00:00.123456789 UTC', CAST(NULL AS STRING))
+-- !query schema
+struct<to_utc_timestamp(TIMESTAMP_LTZ '2015-07-23 17:00:00.123456789', CAST(NULL AS STRING)):timestamp_ltz(9)>
+-- !query output
+NULL
+
+
-- !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 46fb523e378a..45590a119a9c 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
@@ -752,6 +752,47 @@ struct<convert_timezone(America/Los_Angeles, UTC, CAST(NULL AS TIMESTAMP_NTZ(9))
NULL
+-- !query
+SELECT from_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', 'America/Los_Angeles')
+-- !query schema
+struct<from_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', America/Los_Angeles):timestamp_ntz(9)>
+-- !query output
+2015-07-23 17:00:00.123456789
+
+
+-- !query
+SELECT to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', 'America/Los_Angeles')
+-- !query schema
+struct<to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', America/Los_Angeles):timestamp_ntz(9)>
+-- !query output
+2015-07-24 07:00:00.123456789
+
+
+-- !query
+SELECT typeof(
+ to_utc_timestamp('2015-07-24 00:00:00.1234567' :: timestamp_ntz(7), 'America/Los_Angeles'))
+-- !query schema
+struct<typeof(to_utc_timestamp(CAST(2015-07-24 00:00:00.1234567 AS TIMESTAMP_NTZ(7)), America/Los_Angeles)):string>
+-- !query output
+timestamp_ntz(7)
+
+
+-- !query
+SELECT from_utc_timestamp(CAST(NULL AS timestamp_ntz(9)), 'America/Los_Angeles')
+-- !query schema
+struct<from_utc_timestamp(CAST(NULL AS TIMESTAMP_NTZ(9)), America/Los_Angeles):timestamp_ntz(9)>
+-- !query output
+NULL
+
+
+-- !query
+SELECT to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', CAST(NULL AS STRING))
+-- !query schema
+struct<to_utc_timestamp(TIMESTAMP_NTZ '2015-07-24 00:00:00.123456789', CAST(NULL AS STRING)):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