diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/TimestampFormatter.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/TimestampFormatter.scala index de2fd312b7db5..8428964d45707 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/TimestampFormatter.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/TimestampFormatter.scala @@ -127,16 +127,37 @@ class FractionTimestampFormatter(zoneId: ZoneId) override protected lazy val formatter = DateTimeFormatterHelper.fractionFormatter // The new formatter will omit the trailing 0 in the timestamp string, but the legacy formatter - // can't. Here we borrow the code from Spark 2.4 DateTimeUtils.timestampToString to omit the - // trailing 0 for the legacy formatter as well. + // can't. Here we use the legacy formatter to format the given timestamp up to seconds fractions, + // and custom implementation to format the fractional part without trailing zeros. override def format(ts: Timestamp): String = { - val timestampString = ts.toString val formatted = legacyFormatter.format(ts) - - if (timestampString.length > 19 && timestampString.substring(19) != ".0") { - formatted + timestampString.substring(19) - } else { + var nanos = ts.getNanos + if (nanos == 0) { formatted + } else { + // Formats non-zero seconds fraction w/o trailing zeros. For example: + // formatted = '2020-05:27 15:55:30' + // nanos = 001234000 + // Counts the length of the fractional part: 001234000 -> 6 + var fracLen = 9 + while (nanos % 10 == 0) { + nanos /= 10 + fracLen -= 1 + } + // Places `nanos` = 1234 after '2020-05:27 15:55:30.' + val fracOffset = formatted.length + 1 + val totalLen = fracOffset + fracLen + // The buffer for the final result: '2020-05:27 15:55:30.001234' + val buf = new Array[Char](totalLen) + formatted.getChars(0, formatted.length, buf, 0) + buf(formatted.length) = '.' + var i = totalLen + do { + i -= 1 + buf(i) = ('0' + (nanos % 10)).toChar + nanos /= 10 + } while (i > fracOffset) + new String(buf) } } } diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/util/TimestampFormatterSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/util/TimestampFormatterSuite.scala index 4324d3cff63d7..7ff9b46bc6719 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/util/TimestampFormatterSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/util/TimestampFormatterSuite.scala @@ -135,6 +135,9 @@ class TimestampFormatterSuite extends SparkFunSuite with SQLHelper with Matchers test("format fraction of second") { val formatter = TimestampFormatter.getFractionFormatter(UTC) Seq( + -999999 -> "1969-12-31 23:59:59.000001", + -999900 -> "1969-12-31 23:59:59.0001", + -1 -> "1969-12-31 23:59:59.999999", 0 -> "1970-01-01 00:00:00", 1 -> "1970-01-01 00:00:00.000001", 1000 -> "1970-01-01 00:00:00.001",