Skip to content

Commit a496d73

Browse files
committed
Precise calculation micros per month
1 parent f8a45b3 commit a496d73

File tree

2 files changed

+21
-15
lines changed

2 files changed

+21
-15
lines changed

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

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,24 @@ import org.apache.spark.sql.types.Decimal
2121
import org.apache.spark.unsafe.types.CalendarInterval
2222

2323
object IntervalUtils {
24-
val MONTHS_PER_YEAR: Int = 12
25-
val MONTHS_PER_QUARTER: Byte = 3
26-
val YEARS_PER_MILLENNIUM: Int = 1000
27-
val YEARS_PER_CENTURY: Int = 100
28-
val YEARS_PER_DECADE: Int = 10
29-
val MICROS_PER_HOUR: Long = DateTimeUtils.MILLIS_PER_HOUR * DateTimeUtils.MICROS_PER_MILLIS
30-
val MICROS_PER_MINUTE: Long = DateTimeUtils.MILLIS_PER_MINUTE * DateTimeUtils.MICROS_PER_MILLIS
31-
val DAYS_PER_MONTH: Byte = 30
32-
val MICROS_PER_MONTH: Long = DAYS_PER_MONTH * DateTimeUtils.SECONDS_PER_DAY
33-
/* 365.25 days per year assumes leap year every four years */
34-
val MICROS_PER_YEAR: Long = (36525L * DateTimeUtils.MICROS_PER_DAY) / 100
24+
final val MONTHS_PER_YEAR: Int = 12
25+
final val MONTHS_PER_QUARTER: Byte = 3
26+
final val YEARS_PER_MILLENNIUM: Int = 1000
27+
final val YEARS_PER_CENTURY: Int = 100
28+
final val YEARS_PER_DECADE: Int = 10
29+
final val MICROS_PER_HOUR: Long = DateTimeUtils.MILLIS_PER_HOUR * DateTimeUtils.MICROS_PER_MILLIS
30+
final val MICROS_PER_MINUTE: Long = {
31+
DateTimeUtils.MILLIS_PER_MINUTE * DateTimeUtils.MICROS_PER_MILLIS
32+
}
33+
// The average year of the Gregorian calendar 365.2425 days long, see
34+
// https://en.wikipedia.org/wiki/Gregorian_calendar
35+
// Leap year occurs every 4 years, except for years that are divisible by 100
36+
// and not divisible by 400. So, the mean length of of the Gregorian calendar year is:
37+
// 1 mean year = (365 + 1/4 - 1/100 + 1/400) days = 365.2425 days
38+
// The mean year length in seconds is:
39+
// 60 * 60 * 24 * 365.2425 = 31556952.0 = 12 * 2629746
40+
final val SECONDS_PER_MONTH: Int = 2629746
41+
final val MICROS_PER_MONTH: Long = SECONDS_PER_MONTH * DateTimeUtils.MICROS_PER_SECOND
3542

3643
def getYears(interval: CalendarInterval): Int = {
3744
interval.months / MONTHS_PER_YEAR
@@ -83,9 +90,8 @@ object IntervalUtils {
8390

8491
// Returns total number of seconds with microseconds fractional part in the given interval.
8592
def getEpoch(interval: CalendarInterval): Decimal = {
86-
var result = interval.microseconds
87-
result += MICROS_PER_YEAR * (interval.months / MONTHS_PER_YEAR)
88-
result += MICROS_PER_MONTH * (interval.months % MONTHS_PER_YEAR)
93+
val monthsDurationUs = Math.multiplyExact(interval.months, MICROS_PER_MONTH)
94+
val result = Math.addExact(interval.microseconds, monthsDurationUs)
8995
Decimal(result, 18, 6)
9096
}
9197
}

sql/core/src/test/resources/sql-tests/results/date_part.sql.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ select date_part('epoch', c) from t2
857857
-- !query 106 schema
858858
struct<date_part('epoch', t2.`c`):decimal(18,6)>
859859
-- !query 106 output
860-
31873892788.332003
860+
31896905999.004003
861861

862862

863863
-- !query 107

0 commit comments

Comments
 (0)