Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,13 @@ object HiveResult {
}

private def zoneId = DateTimeUtils.getZoneId(SQLConf.get.sessionLocalTimeZone)
private def dateFormatter = DateFormatter(zoneId)
// Date formatting does not depend on time zone ID because it converts local days
// since the epoch to local date string. Time zone id does matter only in parsing
// when the parser has to handle special values like `now`, `yesterday` and etc.
// Here, `dateFormatter` is used only for formatting, so, we can initialize it by
// any time zone once, for instance, by the current session time zone. And we can
// reuse it even when the session time zone might be changed.
private val dateFormatter = DateFormatter(zoneId)

@MaxGekk MaxGekk May 31, 2020

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Just in case, can hiveResultString() or toHiveString() be called from multiple threads in parallel? If so, we cannot use SimpleDateFormat here because it is not thread-safe. In that case, dateFormatter must use FastDateFormat because it is thread-safe.

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.

Yes, it can be called concurrently (e.g. by concurrent thriftserver queries).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

ok. I use FastDateFormat for java.sql.Date which is thread-safe and DateTimeFormatter for java.time.LocalDate. The former one is thread-safe as well. Should be fine.

private def timestampFormatter = TimestampFormatter.getFractionFormatter(zoneId)

/** Formats a datum (based on the given data type) and returns the string representation. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,27 @@

package org.apache.spark.sql.execution

import org.apache.spark.sql.catalyst.util.DateTimeTestUtils
import org.apache.spark.sql.connector.InMemoryTableCatalog
import org.apache.spark.sql.internal.SQLConf
import org.apache.spark.sql.test.{ExamplePoint, ExamplePointUDT, SharedSparkSession}

class HiveResultSuite extends SharedSparkSession {
import testImplicits._

test("date formatting in hive result") {
val dates = Seq("2018-12-28", "1582-10-03", "1582-10-04", "1582-10-15")
val df = dates.toDF("a").selectExpr("cast(a as date) as b")
val executedPlan1 = df.queryExecution.executedPlan
val result = HiveResult.hiveResultString(executedPlan1)
assert(result == dates)
val executedPlan2 = df.selectExpr("array(b)").queryExecution.executedPlan
val result2 = HiveResult.hiveResultString(executedPlan2)
assert(result2 == dates.map(x => s"[$x]"))
DateTimeTestUtils.outstandingTimezonesIds.foreach { zoneId =>
withSQLConf(SQLConf.SESSION_LOCAL_TIMEZONE.key -> zoneId) {
val dates = Seq("2018-12-28", "1582-10-03", "1582-10-04", "1582-10-15")
val df = dates.toDF("a").selectExpr("cast(a as date) as b")
val executedPlan1 = df.queryExecution.executedPlan
val result = HiveResult.hiveResultString(executedPlan1)
assert(result == dates)
val executedPlan2 = df.selectExpr("array(b)").queryExecution.executedPlan
val result2 = HiveResult.hiveResultString(executedPlan2)
assert(result2 == dates.map(x => s"[$x]"))
}
}
}

test("timestamp formatting in hive result") {
Expand Down