Skip to content

Commit 2f232a7

Browse files
author
aokolnychyi
committed
[SPARK-17914][SQL] Fix parsing of timestamp strings with nanoseconds
1 parent 55b8cfe commit 2f232a7

File tree

2 files changed

+22
-1
lines changed

2 files changed

+22
-1
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import org.apache.spark.unsafe.types.UTF8String
3232
* Helper functions for converting between internal and external date and time representations.
3333
* Dates are exposed externally as java.sql.Date and are represented internally as the number of
3434
* dates since the Unix epoch (1970-01-01). Timestamps are exposed externally as java.sql.Timestamp
35-
* and are stored internally as longs, which are capable of storing timestamps with 100 nanosecond
35+
* and are stored internally as longs, which are capable of storing timestamps with microsecond
3636
* precision.
3737
*/
3838
object DateTimeUtils {
@@ -399,6 +399,11 @@ object DateTimeUtils {
399399
digitsMilli += 1
400400
}
401401

402+
while (digitsMilli > 6) {
403+
segments(6) /= 10
404+
digitsMilli -= 1
405+
}
406+
402407
if (!justTime && isInvalidDate(segments(0), segments(1), segments(2))) {
403408
return None
404409
}

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ class DateTimeUtilsSuite extends SparkFunSuite {
3434
((timestamp + tz.getOffset(timestamp)) / MILLIS_PER_DAY).toInt
3535
}
3636

37+
test("nanoseconds truncation") {
38+
def checkStringToTimestamp(originalTime: String, expectedParsedTime: String) {
39+
val parsedTimestampOp = DateTimeUtils.stringToTimestamp(UTF8String.fromString(originalTime))
40+
assert(parsedTimestampOp.isDefined, "timestamp with nanoseconds was not parsed correctly")
41+
assert(DateTimeUtils.timestampToString(parsedTimestampOp.get) === expectedParsedTime)
42+
}
43+
44+
checkStringToTimestamp("2015-01-02 00:00:00.123456789", "2015-01-02 00:00:00.123456")
45+
checkStringToTimestamp("2015-01-02 00:00:00.100000009", "2015-01-02 00:00:00.1")
46+
checkStringToTimestamp("2015-01-02 00:00:00.000050000", "2015-01-02 00:00:00.00005")
47+
checkStringToTimestamp("2015-01-02 00:00:00.12005", "2015-01-02 00:00:00.12005")
48+
checkStringToTimestamp("2015-01-02 00:00:00.100", "2015-01-02 00:00:00.1")
49+
checkStringToTimestamp("2015-01-02 00:00:00.000456789", "2015-01-02 00:00:00.000456")
50+
checkStringToTimestamp("1950-01-02 00:00:00.000456789", "1950-01-02 00:00:00.000456")
51+
}
52+
3753
test("timestamp and us") {
3854
val now = new Timestamp(System.currentTimeMillis())
3955
now.setNanos(1000)

0 commit comments

Comments
 (0)