Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions python/pyspark/sql/functions/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13958,11 +13958,15 @@ def timestamp_add(unit: str, quantity: "ColumnOrName", ts: "ColumnOrName") -> Co
unit : literal string
This indicates the units of datetime that you want to add.
Supported options are (case insensitive): "YEAR", "QUARTER", "MONTH", "WEEK",
"DAY", "HOUR", "MINUTE", "SECOND", "MILLISECOND" and "MICROSECOND".
"DAY", "HOUR", "MINUTE", "SECOND", "MILLISECOND", "MICROSECOND" and "NANOSECOND".
"NANOSECOND" is only valid for nanosecond-precision timestamp inputs
(TIMESTAMP_NTZ(p) / TIMESTAMP_LTZ(p), p in [7, 9]); the result is floored to the
input's precision.
quantity : :class:`~pyspark.sql.Column` or column name
The number of units of time that you want to add.
ts : :class:`~pyspark.sql.Column` or column name
A timestamp to which you want to add.
A timestamp to which you want to add. A nanosecond-precision timestamp keeps its
sub-microsecond fraction; units of MICROSECOND or coarser leave the fraction unchanged.

Returns
-------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1445,7 +1445,7 @@ shiftOperator
datetimeUnit
: YEAR | QUARTER | MONTH
| WEEK | DAY | DAYOFYEAR
| HOUR | MINUTE | SECOND | MILLISECOND | MICROSECOND
| HOUR | MINUTE | SECOND | MILLISECOND | MICROSECOND | NANOSECOND
;

primaryExpression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4981,8 +4981,13 @@ case class ConvertTimezone(
- "SECOND"
- "MILLISECOND"
- "MICROSECOND"
- "NANOSECOND" - only for nanosecond-precision timestamp inputs (TIMESTAMP_NTZ(p) /
TIMESTAMP_LTZ(p), p in [7, 9]); the result is floored to the input's precision, so a
quantity finer than the type's step (10^(9-p) ns) is truncated to it
* quantity - this is the number of units of time that you want to add.
* timestamp - this is a timestamp (w/ or w/o timezone) to which you want to add.
* timestamp - this is a timestamp (w/ or w/o timezone) to which you want to add. A
nanosecond-precision timestamp keeps its sub-microsecond fraction; units of MICROSECOND or
coarser leave the fraction unchanged.
""",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PySpark timestamp_add still documents units as ending at MICROSECOND. SQL ExpressionDescription was updated; the Python list was not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for noticing, updated.

examples = """
Examples:
Expand Down Expand Up @@ -5020,23 +5025,48 @@ case class TimestampAdd(
override def left: Expression = quantity
override def right: Expression = timestamp

override def inputTypes: Seq[AbstractDataType] = Seq(LongType, AnyTimestampType)
override def inputTypes: Seq[AbstractDataType] =
Seq(LongType, TypeCollection(AnyTimestampType, AnyTimestampNanoType))
override def dataType: DataType = timestamp.dataType

// A nanosecond-precision timestamp is carried as a TimestampNanosVal object rather than a
// primitive microsecond Long, so the nanos-aware add path preserves the sub-microsecond fraction.
@transient private lazy val isNanos: Boolean =
timestamp.dataType.isInstanceOf[AnyTimestampNanoType]

// The declared fractional-second precision p in [7, 9] of a nanosecond timestamp input; -1 for a
// microsecond timestamp (where it is unused). Every produced element is floored to this p.
@transient private lazy val nanosPrecision: Int = timestamp.dataType match {
case t: TimestampNTZNanosType => t.precision
case t: TimestampLTZNanosType => t.precision
case _ => -1
}

override def withTimeZone(timeZoneId: String): TimeZoneAwareExpression =
copy(timeZoneId = Option(timeZoneId))

@transient private lazy val zoneIdInEval: ZoneId = zoneIdForType(timestamp.dataType)

override def nullSafeEval(q: Any, micros: Any): Any = {
DateTimeUtils.timestampAdd(unit, q.asInstanceOf[Long], micros.asInstanceOf[Long], zoneIdInEval)
override def nullSafeEval(q: Any, ts: Any): Any = {
if (isNanos) {
DateTimeUtils.timestampAddNanos(
unit, q.asInstanceOf[Long], ts.asInstanceOf[TimestampNanosVal],
nanosPrecision, zoneIdInEval)
} else {
DateTimeUtils.timestampAdd(unit, q.asInstanceOf[Long], ts.asInstanceOf[Long], zoneIdInEval)
}
}

override def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
val zid = ctx.addReferenceObj("zoneId", zoneIdInEval, classOf[ZoneId].getName)
defineCodeGen(ctx, ev, (q, micros) =>
s"""$dtu.timestampAdd("$unit", $q, $micros, $zid)""")
if (isNanos) {
defineCodeGen(ctx, ev, (q, ts) =>
s"""$dtu.timestampAddNanos("$unit", $q, $ts, $nanosPrecision, $zid)""")
} else {
defineCodeGen(ctx, ev, (q, micros) =>
s"""$dtu.timestampAdd("$unit", $q, $micros, $zid)""")
}
}

override def prettyName: String = "timestampadd"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,61 @@ object DateTimeUtils extends SparkDateTimeUtils {
}
}

/**
* Adds the specified number of units to a nanosecond-precision timestamp of precision `p` (in
* [7, 9]), returning a value floored to that precision.
*
* For units of MICROSECOND or coarser the addition runs on the microsecond grid (reusing
* [[timestampAdd]] for all the calendar, DST and overflow handling) and the input's already
* `p`-aligned fraction is carried through unchanged. For the NANOSECOND unit the fraction absorbs
* `quantity` nanoseconds and any whole microseconds carry into `epochMicros`; the resulting
* fraction is then floored to `p` (via [[truncateTimestampNanosToPrecision]]) so a NANOSECOND
* quantity finer than the type's step never produces an off-grid value that would compare, hash
* or sort unequal to its displayed (aligned) form. NANOSECOND is only valid here: adding
* nanoseconds to a microsecond timestamp is unrepresentable, so that combination is rejected as
* an invalid unit through the microsecond [[timestampAdd]] path.
*
* @param unit A keyword that specifies the interval units to add to the input timestamp.
* @param quantity The amount of `unit`s to add. It can be positive or negative.
* @param ts The input nanosecond-precision timestamp.
* @param precision The declared fractional-second precision `p` in [7, 9] of the input/output.
* @param zoneId The time zone ID at which the operation is performed.
* @return A nanosecond-precision timestamp value floored to `precision`.
*/
def timestampAddNanos(
unit: String,
quantity: Long,
ts: TimestampNanosVal,
precision: Int,
zoneId: ZoneId): TimestampNanosVal = {
if (unit.toUpperCase(Locale.ROOT) == "NANOSECOND") {
try {
// Split the added nanoseconds into whole microseconds and a [0, 999] remainder first, so
// the fraction sum stays within [0, 1998] and only the true microsecond total can overflow
// a Long (adding the remainder to `nanosWithinMicro` before the split would spuriously
// reject a large-but-representable quantity).
val quotientMicros = Math.floorDiv(quantity, NANOS_PER_MICROS)
val remainderNanos = Math.floorMod(quantity, NANOS_PER_MICROS)
val fractionSum = ts.nanosWithinMicro.toLong + remainderNanos
val carryMicros =
Math.addExact(quotientMicros, Math.floorDiv(fractionSum, NANOS_PER_MICROS))
val newFraction = Math.floorMod(fractionSum, NANOS_PER_MICROS).toShort
val newMicros = Math.addExact(ts.epochMicros, carryMicros)
truncateTimestampNanosToPrecision(
TimestampNanosVal.fromParts(newMicros, newFraction), precision)
} catch {
case _: ArithmeticException | _: DateTimeException =>
throw QueryExecutionErrors.timestampAddOverflowError(ts.epochMicros, quantity, unit)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overflow still goes through timestampAddOverflowError, which renders only epochMicros. Pre-existing helper; the fraction is dropped from the error text.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this only happens at the very edge of the Long range (~year 294247). I reused the existing error instead of adding a nanosecond-specific one (the digits are correct up to nanosecond), but I can add one if you'd like.

}
} else {
// Units of MICROSECOND or coarser do not touch the sub-microsecond fraction; add on the
// microsecond grid and re-attach the input's fraction, which is already `p`-aligned. An
// unknown unit is rejected by timestampAdd, and NANOSECOND is handled above.
TimestampNanosVal.fromParts(
timestampAdd(unit, quantity, ts.epochMicros, zoneId), ts.nanosWithinMicro)
}
}

private val timestampDiffMap = Map[String, (Temporal, Temporal) => Long](
"MICROSECOND" -> ChronoUnit.MICROS.between,
"MILLISECOND" -> ChronoUnit.MILLIS.between,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2687,6 +2687,76 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
}
}

test("SPARK-57833: timestampadd over nanosecond-precision timestamps") {
val sec = 1000000L // microseconds per second
def ntz(micros: Long, frac: Int, p: Int = 9): Literal =
Literal(TimestampNanosVal.fromParts(micros, frac.toShort), TimestampNTZNanosType(p))
def ltz(micros: Long, frac: Int, p: Int = 9): Literal =
Literal(TimestampNanosVal.fromParts(micros, frac.toShort), TimestampLTZNanosType(p))
def tnv(micros: Long, frac: Int): TimestampNanosVal =
TimestampNanosVal.fromParts(micros, frac.toShort)

// Units of MICROSECOND or coarser keep the sub-microsecond fraction unchanged and the result
// stays nanosecond-typed (NTZ and LTZ, precisions 7/8/9).
checkEvaluation(TimestampAdd("SECOND", Literal(5L), ntz(0, 123)), tnv(5 * sec, 123))
checkEvaluation(TimestampAdd("MICROSECOND", Literal(2L), ntz(0, 123)), tnv(2, 123))
checkEvaluation(TimestampAdd("MINUTE", Literal(1L), ntz(3 * sec, 120, 8)), tnv(63 * sec, 120))
checkEvaluation(TimestampAdd("HOUR", Literal(0L), ntz(sec, 100, 7)), tnv(sec, 100))
// LTZ (zone-aware) with a whole-second unit: UTC keeps epoch alignment, fraction preserved.
checkEvaluation(TimestampAdd("SECOND", Literal(1L), ltz(0, 500), Some("UTC")), tnv(sec, 500))

// NANOSECOND unit: the fraction absorbs the quantity and whole microseconds carry into
// epochMicros.
checkEvaluation(TimestampAdd("NANOSECOND", Literal(300L), ntz(0, 123)), tnv(0, 423))
checkEvaluation(TimestampAdd("NANOSECOND", Literal(900L), ntz(0, 200)), tnv(1, 100))
checkEvaluation(TimestampAdd("NANOSECOND", Literal(2500L), ntz(0, 100)), tnv(2, 600))
// A negative quantity floors the carry: 100 - 300 ns = -200 ns => -1 microsecond, fraction 800.
checkEvaluation(TimestampAdd("NANOSECOND", Literal(-300L), ntz(5, 100)), tnv(4, 800))
// The unit keyword is case-insensitive.
checkEvaluation(TimestampAdd("Nanosecond", Literal(1L), ntz(0, 0)), tnv(0, 1))

// NANOSECOND result is floored to the input's precision, so a quantity finer than the type's
// step (10^(9-p) ns) never yields an off-grid fraction (which would compare / hash / sort
// unequal to its displayed, aligned form). p=7 -> 100ns step, p=8 -> 10ns step.
checkEvaluation(TimestampAdd("NANOSECOND", Literal(150L), ntz(0, 100, 7)), tnv(0, 200))
checkEvaluation(TimestampAdd("NANOSECOND", Literal(50L), ntz(0, 100, 7)), tnv(0, 100))
checkEvaluation(TimestampAdd("NANOSECOND", Literal(15L), ntz(0, 100, 8)), tnv(0, 110))
// Carry into the microsecond then floor the remainder: 100 + 905 = 1005 -> (+1 us, 5ns),
// floored to the 10ns step -> 0ns.
checkEvaluation(TimestampAdd("NANOSECOND", Literal(905L), ntz(0, 100, 8)), tnv(1, 0))
// LTZ at p=7 floors the same way.
checkEvaluation(
TimestampAdd("NANOSECOND", Literal(250L), ltz(0, 100, 7), Some("UTC")), tnv(0, 300))
// A near-Long.MaxValue nanosecond quantity that stays representable is not spuriously rejected:
// it carries ~9.2e15 microseconds forward from a small epoch. 9223372036854775000 ns =
// 9223372036854775 us + 0 ns; from epochMicros 0 that lands on that microsecond, fraction 0.
checkEvaluation(
TimestampAdd("NANOSECOND", Literal(9223372036854775000L), ntz(0, 0)),
tnv(9223372036854775L, 0))

// Null propagation on either operand.
checkEvaluation(
TimestampAdd("NANOSECOND", Literal.create(null, LongType), ntz(0, 1)), null)
checkEvaluation(
TimestampAdd("SECOND", Literal(1L), Literal.create(null, TimestampNTZNanosType(9))), null)

// NANOSECOND is meaningful only for a nanosecond-precision timestamp; on a microsecond
// timestamp nanoseconds are unrepresentable, so it is rejected as an invalid unit.
checkErrorInExpression[SparkIllegalArgumentException](
TimestampAdd("NANOSECOND", Literal(1L), Literal(0L, TimestampType)),
condition = "INVALID_PARAMETER_VALUE.DATETIME_UNIT",
parameters = Map(
"functionName" -> "`TIMESTAMPADD`",
"parameter" -> "`unit`",
"invalidValue" -> "'NANOSECOND'"))

// Overflow while carrying nanoseconds into epochMicros surfaces as a datetime overflow.
val overflow = intercept[SparkArithmeticException] {
TimestampAdd("NANOSECOND", Literal(1000L), ntz(Long.MaxValue, 999)).eval(null)
}
assert(overflow.getCondition == "DATETIME_OVERFLOW")
}

test("SPARK-42635: timestampadd near daylight saving transition") {
// In America/Los_Angeles timezone, timestamp value `skippedTime` is 2011-03-13 03:00:00.
// The next second of 2011-03-13 01:59:59 jumps to 2011-03-13 03:00:00.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2117,6 +2117,12 @@ org.apache.spark.sql.catalyst.parser.ParseException
}


-- !query
select timestampdiff(NANOSECOND, timestamp'2022-02-14 01:02:03', timestamp'2022-02-14 01:02:04')
-- !query analysis
[Analyzer test output redacted due to nondeterminism]


-- !query
select timediff(QUARTER, timestamp'2023-08-10 01:02:03', timestamp'2022-01-14 01:02:03')
-- !query analysis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,12 @@ org.apache.spark.sql.catalyst.parser.ParseException
}


-- !query
select timestampdiff(NANOSECOND, timestamp'2022-02-14 01:02:03', timestamp'2022-02-14 01:02:04')
-- !query analysis
[Analyzer test output redacted due to nondeterminism]


-- !query
select timediff(QUARTER, timestamp'2023-08-10 01:02:03', timestamp'2022-01-14 01:02:03')
-- !query analysis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1621,3 +1621,44 @@ SELECT element_at(map(TIMESTAMP_LTZ '2020-01-01 00:00:00.000000001 UTC', 'a',
-- !query analysis
Project [element_at(map(2019-12-31 16:00:00.000000001, a, 2019-12-31 16:00:00.000000999, b), 2019-12-31 16:00:00.000000001, None, true) AS element_at(map(TIMESTAMP_LTZ '2019-12-31 16:00:00.000000001', a, TIMESTAMP_LTZ '2019-12-31 16:00:00.000000999', b), TIMESTAMP_LTZ '2019-12-31 16:00:00.000000001')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(SECOND, 5, TIMESTAMP_LTZ '2020-01-01 00:00:00.000000123 UTC')
-- !query analysis
Project [timestampadd(SECOND, cast(5 as bigint), 2019-12-31 16:00:00.000000123, Some(America/Los_Angeles)) AS timestampadd(SECOND, 5, TIMESTAMP_LTZ '2019-12-31 16:00:00.000000123')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(MICROSECOND, 2, TIMESTAMP_LTZ '2020-01-01 00:00:00.000000123 UTC')
-- !query analysis
Project [timestampadd(MICROSECOND, cast(2 as bigint), 2019-12-31 16:00:00.000000123, Some(America/Los_Angeles)) AS timestampadd(MICROSECOND, 2, TIMESTAMP_LTZ '2019-12-31 16:00:00.000000123')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, 300, TIMESTAMP_LTZ '2020-01-01 00:00:00.000000123 UTC')
-- !query analysis
Project [timestampadd(NANOSECOND, cast(300 as bigint), 2019-12-31 16:00:00.000000123, Some(America/Los_Angeles)) AS timestampadd(NANOSECOND, 300, TIMESTAMP_LTZ '2019-12-31 16:00:00.000000123')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, -300, TIMESTAMP_LTZ '2020-01-01 00:00:00.000000100 UTC')
-- !query analysis
Project [timestampadd(NANOSECOND, cast(-300 as bigint), 2019-12-31 16:00:00.0000001, Some(America/Los_Angeles)) AS timestampadd(NANOSECOND, -300, TIMESTAMP_LTZ '2019-12-31 16:00:00.000000100')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, 900, TIMESTAMP_LTZ '2020-01-01 00:00:00.000000200 UTC')
-- !query analysis
Project [timestampadd(NANOSECOND, cast(900 as bigint), 2019-12-31 16:00:00.0000002, Some(America/Los_Angeles)) AS timestampadd(NANOSECOND, 900, TIMESTAMP_LTZ '2019-12-31 16:00:00.000000200')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, 1, TIMESTAMP_LTZ '2020-01-01 00:00:00 UTC')
-- !query analysis
[Analyzer test output redacted due to nondeterminism]
Original file line number Diff line number Diff line change
Expand Up @@ -1575,3 +1575,59 @@ SELECT element_at(map(TIMESTAMP_NTZ '2020-01-01 00:00:00.000000001', 'a',
-- !query analysis
Project [element_at(map(2020-01-01 00:00:00.000000001, a, 2020-01-01 00:00:00.000000999, b), 2020-01-01 00:00:00.000000001, None, true) AS element_at(map(TIMESTAMP_NTZ '2020-01-01 00:00:00.000000001', a, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000999', b), TIMESTAMP_NTZ '2020-01-01 00:00:00.000000001')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(SECOND, 5, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000123')
-- !query analysis
Project [timestampadd(SECOND, cast(5 as bigint), 2020-01-01 00:00:00.000000123, Some(America/Los_Angeles)) AS timestampadd(SECOND, 5, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000123')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(MICROSECOND, 2, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000123')
-- !query analysis
Project [timestampadd(MICROSECOND, cast(2 as bigint), 2020-01-01 00:00:00.000000123, Some(America/Los_Angeles)) AS timestampadd(MICROSECOND, 2, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000123')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, 300, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000123')
-- !query analysis
Project [timestampadd(NANOSECOND, cast(300 as bigint), 2020-01-01 00:00:00.000000123, Some(America/Los_Angeles)) AS timestampadd(NANOSECOND, 300, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000123')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, 900, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000200')
-- !query analysis
Project [timestampadd(NANOSECOND, cast(900 as bigint), 2020-01-01 00:00:00.0000002, Some(America/Los_Angeles)) AS timestampadd(NANOSECOND, 900, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000200')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, -300, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000100')
-- !query analysis
Project [timestampadd(NANOSECOND, cast(-300 as bigint), 2020-01-01 00:00:00.0000001, Some(America/Los_Angeles)) AS timestampadd(NANOSECOND, -300, TIMESTAMP_NTZ '2020-01-01 00:00:00.000000100')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, 1, TIMESTAMP_NTZ '2020-01-01 00:00:00')
-- !query analysis
Project [timestampadd(NANOSECOND, cast(1 as bigint), 2020-01-01 00:00:00, Some(America/Los_Angeles)) AS timestampadd(NANOSECOND, 1, TIMESTAMP_NTZ '2020-01-01 00:00:00')#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, 150, '2020-01-01 00:00:00.0000001' :: timestamp_ntz(7))
-- !query analysis
Project [timestampadd(NANOSECOND, cast(150 as bigint), cast(2020-01-01 00:00:00.0000001 as timestamp_ntz(7)), Some(America/Los_Angeles)) AS timestampadd(NANOSECOND, 150, CAST(2020-01-01 00:00:00.0000001 AS TIMESTAMP_NTZ(7)))#x]
+- OneRowRelation


-- !query
SELECT timestampadd(NANOSECOND, 50, '2020-01-01 00:00:00.0000001' :: timestamp_ntz(7))
-- !query analysis
Project [timestampadd(NANOSECOND, cast(50 as bigint), cast(2020-01-01 00:00:00.0000001 as timestamp_ntz(7)), Some(America/Los_Angeles)) AS timestampadd(NANOSECOND, 50, CAST(2020-01-01 00:00:00.0000001 AS TIMESTAMP_NTZ(7)))#x]
+- OneRowRelation
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,12 @@ org.apache.spark.sql.catalyst.parser.ParseException
}


-- !query
select timestampdiff(NANOSECOND, timestamp'2022-02-14 01:02:03', timestamp'2022-02-14 01:02:04')
-- !query analysis
[Analyzer test output redacted due to nondeterminism]


-- !query
select timediff(QUARTER, timestamp'2023-08-10 01:02:03', timestamp'2022-01-14 01:02:03')
-- !query analysis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,13 @@ org.apache.spark.sql.catalyst.parser.ParseException
}


-- !query
select timestampdiff(NANOSECOND, timestamp'2022-02-14 01:02:03', timestamp'2022-02-14 01:02:04')
-- !query analysis
Project [timestampdiff(NANOSECOND, cast(2022-02-14 01:02:03 as timestamp), cast(2022-02-14 01:02:04 as timestamp), Some(America/Los_Angeles)) AS timestampdiff(NANOSECOND, TIMESTAMP_NTZ '2022-02-14 01:02:03', TIMESTAMP_NTZ '2022-02-14 01:02:04')#xL]
+- OneRowRelation


-- !query
select timediff(QUARTER, timestamp'2023-08-10 01:02:03', timestamp'2022-01-14 01:02:03')
-- !query analysis
Expand Down
Loading