From c9ed9dfccb72bc8d30557dcd2809c298a75c3f69 Mon Sep 17 00:00:00 2001 From: Kent Yao Date: Mon, 29 Apr 2024 11:13:39 -0700 Subject: [PATCH] [SPARK-48042][SQL] Use a timestamp formatter with timezone at class level instead of making copies at method level ### What changes were proposed in this pull request? This PR creates a timestamp formatter with the timezone directly for formatting. Previously, we called `withZone` for every value in the `format` function. Because the original `zoneId` in the formatter is null and never equals the one we pass in, it creates new copies of the formatter over and over. ```java ... * * param zone the new override zone, null if no override * return a formatter based on this formatter with the requested override zone, not null */ public DateTimeFormatter withZone(ZoneId zone) { if (Objects.equals(this.zone, zone)) { return this; } return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } ``` ### Why are the changes needed? improvement ### Does this PR introduce _any_ user-facing change? no ### How was this patch tested? - Existing tests - I also ran the DateTimeBenchmark result locally, there's no performance gain at least for these cases. ### Was this patch authored or co-authored using generative AI tooling? no Closes #46282 from yaooqinn/SPARK-48042. Authored-by: Kent Yao Signed-off-by: Dongjoon Hyun --- .../apache/spark/sql/catalyst/util/TimestampFormatter.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sql/api/src/main/scala/org/apache/spark/sql/catalyst/util/TimestampFormatter.scala b/sql/api/src/main/scala/org/apache/spark/sql/catalyst/util/TimestampFormatter.scala index d59b52a3818ac..9f57f8375c54d 100644 --- a/sql/api/src/main/scala/org/apache/spark/sql/catalyst/util/TimestampFormatter.scala +++ b/sql/api/src/main/scala/org/apache/spark/sql/catalyst/util/TimestampFormatter.scala @@ -162,6 +162,9 @@ class Iso8601TimestampFormatter( protected lazy val formatter: DateTimeFormatter = getOrCreateFormatter(pattern, locale, isParsing) + @transient + private lazy val zonedFormatter: DateTimeFormatter = formatter.withZone(zoneId) + @transient protected lazy val legacyFormatter = TimestampFormatter.getLegacyFormatter( pattern, zoneId, locale, legacyFormat) @@ -231,7 +234,7 @@ class Iso8601TimestampFormatter( override def format(instant: Instant): String = { try { - formatter.withZone(zoneId).format(instant) + zonedFormatter.format(instant) } catch checkFormattedDiff(toJavaTimestamp(instantToMicros(instant)), (t: Timestamp) => format(t)) }