diff --git a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/schema/RowSet.scala b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/schema/RowSet.scala index e240ed8e1f7..8cc88156ba5 100644 --- a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/schema/RowSet.scala +++ b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/schema/RowSet.scala @@ -261,7 +261,7 @@ object RowSet { formatLocalDate(ld) case (t: Timestamp, TimestampType) => - formatTimestamp(t) + formatTimestamp(t, Option(timeZone)) case (t: LocalDateTime, ntz) if ntz.getClass.getSimpleName.equals(TIMESTAMP_NTZ) => formatLocalDateTime(t) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/util/RowSetUtils.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/util/RowSetUtils.scala index 6eb6b6225ff..82417a73092 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/util/RowSetUtils.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/util/RowSetUtils.scala @@ -24,7 +24,7 @@ import java.time.chrono.IsoChronology import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatterBuilder import java.time.temporal.ChronoField -import java.util.{Date, Locale} +import java.util.{Date, Locale, TimeZone} import java.util.concurrent.TimeUnit import scala.language.implicitConversions @@ -77,8 +77,14 @@ private[kyuubi] object RowSetUtils { .getOrElse(timestampFormatter.format(i)) } - def formatTimestamp(t: Timestamp): String = { - legacyTimestampFormatter.format(t) + def formatTimestamp(t: Timestamp, timeZone: Option[ZoneId] = None): String = { + timeZone.map(zoneId => { + FastDateFormat.getInstance( + legacyTimestampFormatter.getPattern, + TimeZone.getTimeZone(zoneId), + legacyTimestampFormatter.getLocale) + .format(t) + }).getOrElse(legacyTimestampFormatter.format(t)) } implicit def bitSetToBuffer(bitSet: java.util.BitSet): ByteBuffer = { diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/engine/spark/SparkSqlEngineSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/engine/spark/SparkSqlEngineSuite.scala index 461a8287016..1e35d2f1dc8 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/engine/spark/SparkSqlEngineSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/engine/spark/SparkSqlEngineSuite.scala @@ -132,5 +132,22 @@ class SparkSqlEngineSuite extends WithKyuubiServer with HiveJDBCTestHelper { } } + test("Spark session timezone format") { + withJdbcStatement() { statement => + val setUTCResultSet = statement.executeQuery("set spark.sql.session.timeZone=UTC") + assert(setUTCResultSet.next()) + val utcResultSet = statement.executeQuery("select from_utc_timestamp(from_unixtime(" + + "1670404535000/1000,'yyyy-MM-dd HH:mm:ss'),'GMT+08:00')") + assert(utcResultSet.next()) + assert(utcResultSet.getString(1) == "2022-12-07 17:15:35.0") + val setGMT8ResultSet = statement.executeQuery("set spark.sql.session.timeZone=GMT+8") + assert(setGMT8ResultSet.next()) + val gmt8ResultSet = statement.executeQuery("select from_utc_timestamp(from_unixtime(" + + "1670404535000/1000,'yyyy-MM-dd HH:mm:ss'),'GMT+08:00')") + assert(gmt8ResultSet.next()) + assert(gmt8ResultSet.getString(1) == "2022-12-08 01:15:35.0") + } + } + override protected def jdbcUrl: String = getJdbcUrl }