diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala index f2cc08d89dc6..5700e2f1438d 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala @@ -378,6 +378,9 @@ object DateTimeUtils { throw QueryExecutionErrors.cannotCastUTF8StringToDataTypeError(s, TimestampType) } } + // See issue SPARK-35679 + // min second cause overflow in instant to micro + private val MIN_SECONDS = Math.floorDiv(Long.MinValue, MICROS_PER_SECOND) /** * Gets the number of microseconds since the epoch of 1970-01-01 00:00:00Z from the given @@ -385,9 +388,14 @@ object DateTimeUtils { * microseconds where microsecond 0 is 1970-01-01 00:00:00Z. */ def instantToMicros(instant: Instant): Long = { - val us = Math.multiplyExact(instant.getEpochSecond, MICROS_PER_SECOND) - val result = Math.addExact(us, NANOSECONDS.toMicros(instant.getNano)) - result + val secs = instant.getEpochSecond + if (secs == MIN_SECONDS) { + val us = Math.multiplyExact(secs + 1, MICROS_PER_SECOND) + Math.addExact(us, NANOSECONDS.toMicros(instant.getNano) - MICROS_PER_SECOND) + } else { + val us = Math.multiplyExact(secs, MICROS_PER_SECOND) + Math.addExact(us, NANOSECONDS.toMicros(instant.getNano)) + } } /** diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala index 58a8bff62cdb..4987c931b33a 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala @@ -820,4 +820,9 @@ class DateTimeUtilsSuite extends SparkFunSuite with Matchers with SQLHelper { } } } + + test("SPARK-35679: instantToMicros should be able to return microseconds of Long.MinValue") { + assert(instantToMicros(microsToInstant(Long.MaxValue)) === Long.MaxValue) + assert(instantToMicros(microsToInstant(Long.MinValue)) === Long.MinValue) + } }