Skip to content

[SPARK-57821][SQL] Support nanosecond-precision timestamps in date_trunc#57224

Open
stevomitric wants to merge 2 commits into
apache:masterfrom
stevomitric:stevomitric/spark-57821-nanos
Open

[SPARK-57821][SQL] Support nanosecond-precision timestamps in date_trunc#57224
stevomitric wants to merge 2 commits into
apache:masterfrom
stevomitric:stevomitric/spark-57821-nanos

Conversation

@stevomitric

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Extend date_trunc (TruncTimestamp) to accept nanosecond-precision timestamps (TIMESTAMP_NTZ(p)/TIMESTAMP_LTZ(p), p in [7, 9]) in addition to microsecond TimestampType. The result keeps the input's nanos type/family; truncation floors epochMicros (NTZ at UTC, LTZ in the session zone) and always zeroes the sub-microsecond remainder, since date_trunc's finest unit is MICROSECOND. Adds DateTimeUtils.truncTimestampNanos delegating to the existing truncTimestamp.

Why are the changes needed?

date_trunc previously narrowed a nanosecond argument to microseconds, dropping the sub-microsecond digits and forcing an LTZ result even for NTZ input.

Does this PR introduce any user-facing change?

Yes. date_trunc(fmt, ts_nanos) now returns the same nanosecond type/family with correct truncation (preview spark.sql.timestampNanosTypes.enabled).

How was this patch tested?

DateExpressionsSuite and timestamp-{ltz,ntz}-nanos.sql golden files.

Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code

Extend date_trunc (TruncTimestamp) to accept the nanosecond-precision timestamp types TIMESTAMP_NTZ(p)/TIMESTAMP_LTZ(p) (p in [7,9]) in addition to the microsecond TimestampType. The result now carries the input's nanosecond type/family, and truncation floors epochMicros via the existing truncTimestamp (NTZ evaluated at UTC, LTZ in the session zone) while always zeroing the sub-microsecond remainder, since date_trunc's finest unit is MICROSECOND. A new DateTimeUtils.truncTimestampNanos delegates to truncTimestamp so behavior stays identical to the micro path on the shared range.

Why: the nanosecond timestamp preview (SPARK-56822) needs date_trunc to work end-to-end without silently downcasting to microseconds.

Co-authored-by: Isaac
date_trunc's @ExpressionDescription had no nanosecond example, unlike its
nanos-extended peers in this file (DayOfYear, Year, Quarter, Month,
DayOfMonth, DayOfWeek, NanosToTimestamp, UnixNanos), so
`DESCRIBE FUNCTION EXTENDED date_trunc` did not surface nanosecond support.

Add a matching example gated on spark.sql.timestampNanosTypes.enabled that
truncates a TIMESTAMP_NTZ nanos literal to MICROSECOND (dropping the
sub-microsecond remainder). Extend the scalastyle directive to
line.contains.tab, since the SET-output line carries a literal tab, matching
the peer examples.

Co-authored-by: Isaac
@stevomitric stevomitric changed the title [WIP][SPARK-57821][SQL] Support nanosecond-precision timestamps in date_trunc [SPARK-57821][SQL] Support nanosecond-precision timestamps in date_trunc Jul 13, 2026
@stevomitric

Copy link
Copy Markdown
Contributor Author

cc @MaxGekk, @cloud-fan PTAL.

@cloud-fan cloud-fan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

0 blocking, 0 non-blocking, 0 nits.
Clean, well-scoped extension of date_trunc to nanosecond timestamps, following the established nanos-expression pattern (TimeAdd/TimestampAddInterval/SecondWithFractionNanos): dataType-based branching in a single expression class, zoneIdForType for zone selection, and a delegating truncTimestampNanos helper in DateTimeUtils. LGTM.

Verification

Traced the semantics across the type families: nanos input keeps its exact type/family (NTZ->NTZ, LTZ->LTZ, precision preserved); micro TIMESTAMP_NTZ is deliberately left out of the accepted type set so the analyzer still casts it to micro LTZ (matching the inline comment), and zoneIdForType sends the NTZ family to UTC and everything else to the session zone. truncTimestampNanos reuses the existing truncTimestamp for the epoch-micros floor (inheriting all DST/offset/level handling) and zeroes nanosWithinMicro since MICROSECOND is date_trunc's finest unit. The LTZ golden output correctly shifts to the session zone, including the DAY-crosses-local-day case, and eval/codegen parity is exercised for both families. All hold.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants