Skip to content

Commit 3146b65

Browse files
committed
[SPARK-31507][SQL] Remove millennium, century, decade, millisecond, microsecond and epoch fields support from extract fucntion
1 parent e7bc6f3 commit 3146b65

File tree

12 files changed

+190
-1132
lines changed

12 files changed

+190
-1132
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/datetimeExpressions.scala

Lines changed: 1 addition & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@
1717

1818
package org.apache.spark.sql.catalyst.expressions
1919

20-
import java.sql.Timestamp
2120
import java.time.{DateTimeException, LocalDate, LocalDateTime, ZoneId}
2221
import java.time.temporal.IsoFields
23-
import java.util.{Locale, TimeZone}
22+
import java.util.Locale
2423

2524
import scala.util.control.NonFatal
2625

@@ -336,48 +335,6 @@ case class SecondWithFraction(child: Expression, timeZoneId: Option[String] = No
336335
}
337336
}
338337

339-
case class Milliseconds(child: Expression, timeZoneId: Option[String] = None)
340-
extends UnaryExpression with ImplicitCastInputTypes with TimeZoneAwareExpression {
341-
342-
override def inputTypes: Seq[AbstractDataType] = Seq(TimestampType)
343-
// DecimalType is used here to not lose precision while converting microseconds to
344-
// the fractional part of milliseconds. Scale 3 is taken to have all microseconds as
345-
// the fraction. The precision 8 should cover 2 digits for seconds, 3 digits for
346-
// milliseconds and 3 digits for microseconds.
347-
override def dataType: DataType = DecimalType(8, 3)
348-
override def withTimeZone(timeZoneId: String): TimeZoneAwareExpression =
349-
copy(timeZoneId = Option(timeZoneId))
350-
351-
override protected def nullSafeEval(timestamp: Any): Any = {
352-
DateTimeUtils.getMilliseconds(timestamp.asInstanceOf[Long], zoneId)
353-
}
354-
355-
override protected def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
356-
val zid = ctx.addReferenceObj("zoneId", zoneId, classOf[ZoneId].getName)
357-
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
358-
defineCodeGen(ctx, ev, c => s"$dtu.getMilliseconds($c, $zid)")
359-
}
360-
}
361-
362-
case class Microseconds(child: Expression, timeZoneId: Option[String] = None)
363-
extends UnaryExpression with ImplicitCastInputTypes with TimeZoneAwareExpression {
364-
365-
override def inputTypes: Seq[AbstractDataType] = Seq(TimestampType)
366-
override def dataType: DataType = IntegerType
367-
override def withTimeZone(timeZoneId: String): TimeZoneAwareExpression =
368-
copy(timeZoneId = Option(timeZoneId))
369-
370-
override protected def nullSafeEval(timestamp: Any): Any = {
371-
DateTimeUtils.getMicroseconds(timestamp.asInstanceOf[Long], zoneId)
372-
}
373-
374-
override protected def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
375-
val zid = ctx.addReferenceObj("zoneId", zoneId, classOf[ZoneId].getName)
376-
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
377-
defineCodeGen(ctx, ev, c => s"$dtu.getMicroseconds($c, $zid)")
378-
}
379-
}
380-
381338
@ExpressionDescription(
382339
usage = "_FUNC_(date) - Returns the day of year of the date/timestamp.",
383340
examples = """
@@ -2034,86 +1991,12 @@ case class MakeTimestamp(
20341991
override def prettyName: String = "make_timestamp"
20351992
}
20361993

2037-
case class Millennium(child: Expression) extends UnaryExpression with ImplicitCastInputTypes {
2038-
2039-
override def inputTypes: Seq[AbstractDataType] = Seq(DateType)
2040-
2041-
override def dataType: DataType = IntegerType
2042-
2043-
override protected def nullSafeEval(date: Any): Any = {
2044-
DateTimeUtils.getMillennium(date.asInstanceOf[Int])
2045-
}
2046-
2047-
override protected def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
2048-
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
2049-
defineCodeGen(ctx, ev, c => s"$dtu.getMillennium($c)")
2050-
}
2051-
}
2052-
2053-
case class Century(child: Expression) extends UnaryExpression with ImplicitCastInputTypes {
2054-
2055-
override def inputTypes: Seq[AbstractDataType] = Seq(DateType)
2056-
2057-
override def dataType: DataType = IntegerType
2058-
2059-
override protected def nullSafeEval(date: Any): Any = {
2060-
DateTimeUtils.getCentury(date.asInstanceOf[Int])
2061-
}
2062-
2063-
override protected def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
2064-
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
2065-
defineCodeGen(ctx, ev, c => s"$dtu.getCentury($c)")
2066-
}
2067-
}
2068-
2069-
case class Decade(child: Expression) extends UnaryExpression with ImplicitCastInputTypes {
2070-
2071-
override def inputTypes: Seq[AbstractDataType] = Seq(DateType)
2072-
2073-
override def dataType: DataType = IntegerType
2074-
2075-
override protected def nullSafeEval(date: Any): Any = {
2076-
DateTimeUtils.getDecade(date.asInstanceOf[Int])
2077-
}
2078-
2079-
override protected def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
2080-
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
2081-
defineCodeGen(ctx, ev, c => s"$dtu.getDecade($c)")
2082-
}
2083-
}
2084-
2085-
case class Epoch(child: Expression, timeZoneId: Option[String] = None)
2086-
extends UnaryExpression with ImplicitCastInputTypes with TimeZoneAwareExpression {
2087-
2088-
override def inputTypes: Seq[AbstractDataType] = Seq(TimestampType)
2089-
// DecimalType is used to not lose precision while converting microseconds to
2090-
// the fractional part of seconds. Scale 6 is taken to have all microseconds as
2091-
// the fraction. The precision 20 should cover whole valid range of years [1, 9999]
2092-
// plus negative years that can be used in some cases though are not officially supported.
2093-
override def dataType: DataType = DecimalType(20, 6)
2094-
override def withTimeZone(timeZoneId: String): TimeZoneAwareExpression =
2095-
copy(timeZoneId = Option(timeZoneId))
2096-
2097-
override protected def nullSafeEval(timestamp: Any): Any = {
2098-
DateTimeUtils.getEpoch(timestamp.asInstanceOf[Long], zoneId)
2099-
}
2100-
2101-
override protected def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
2102-
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
2103-
val zid = ctx.addReferenceObj("zoneId", zoneId, classOf[ZoneId].getName)
2104-
defineCodeGen(ctx, ev, c => s"$dtu.getEpoch($c, $zid)")
2105-
}
2106-
}
2107-
21081994
object DatePart {
21091995

21101996
def parseExtractField(
21111997
extractField: String,
21121998
source: Expression,
21131999
errorHandleFunc: => Nothing): Expression = extractField.toUpperCase(Locale.ROOT) match {
2114-
case "MILLENNIUM" | "MILLENNIA" | "MIL" | "MILS" => Millennium(source)
2115-
case "CENTURY" | "CENTURIES" | "C" | "CENT" => Century(source)
2116-
case "DECADE" | "DECADES" | "DEC" | "DECS" => Decade(source)
21172000
case "YEAR" | "Y" | "YEARS" | "YR" | "YRS" => Year(source)
21182001
case "ISOYEAR" => IsoYear(source)
21192002
case "QUARTER" | "QTR" => Quarter(source)
@@ -2127,11 +2010,6 @@ object DatePart {
21272010
case "HOUR" | "H" | "HOURS" | "HR" | "HRS" => Hour(source)
21282011
case "MINUTE" | "M" | "MIN" | "MINS" | "MINUTES" => Minute(source)
21292012
case "SECOND" | "S" | "SEC" | "SECONDS" | "SECS" => SecondWithFraction(source)
2130-
case "MILLISECONDS" | "MSEC" | "MSECS" | "MILLISECON" | "MSECONDS" | "MS" =>
2131-
Milliseconds(source)
2132-
case "MICROSECONDS" | "USEC" | "USECS" | "USECONDS" | "MICROSECON" | "US" =>
2133-
Microseconds(source)
2134-
case "EPOCH" => Epoch(source)
21352013
case _ => errorHandleFunc
21362014
}
21372015
}

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

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -526,14 +526,6 @@ object DateTimeUtils {
526526
Decimal(getMicroseconds(microsec, zoneId), 8, 6)
527527
}
528528

529-
/**
530-
* Returns seconds, including fractional parts, multiplied by 1000. The timestamp
531-
* is expressed in microseconds since the epoch.
532-
*/
533-
def getMilliseconds(timestamp: SQLTimestamp, zoneId: ZoneId): Decimal = {
534-
Decimal(getMicroseconds(timestamp, zoneId), 8, 3)
535-
}
536-
537529
/**
538530
* Returns seconds, including fractional parts, multiplied by 1000000. The timestamp
539531
* is expressed in microseconds since the epoch.
@@ -551,24 +543,6 @@ object DateTimeUtils {
551543
LocalDate.ofEpochDay(date).getDayOfYear
552544
}
553545

554-
private def extractFromYear(date: SQLDate, divider: Int): Int = {
555-
val localDate = daysToLocalDate(date)
556-
val yearOfEra = localDate.get(ChronoField.YEAR_OF_ERA)
557-
var result = yearOfEra / divider
558-
if ((yearOfEra % divider) != 0 || yearOfEra <= 1) result += 1
559-
if (localDate.get(ChronoField.ERA) == 0) result = -result
560-
result
561-
}
562-
563-
/** Returns the millennium for the given date. The date is expressed in days since 1.1.1970. */
564-
def getMillennium(date: SQLDate): Int = extractFromYear(date, 1000)
565-
566-
/** Returns the century for the given date. The date is expressed in days since 1.1.1970. */
567-
def getCentury(date: SQLDate): Int = extractFromYear(date, 100)
568-
569-
/** Returns the decade for the given date. The date is expressed in days since 1.1.1970. */
570-
def getDecade(date: SQLDate): Int = Math.floorDiv(getYear(date), 10)
571-
572546
/**
573547
* Returns the year value for the given date. The date is expressed in days
574548
* since 1.1.1970.
@@ -863,17 +837,6 @@ object DateTimeUtils {
863837
convertTz(time, getZoneId(timeZone), ZoneOffset.UTC)
864838
}
865839

866-
/**
867-
* Returns the number of seconds with fractional part in microsecond precision
868-
* since 1970-01-01 00:00:00 local time.
869-
*/
870-
def getEpoch(timestamp: SQLTimestamp, zoneId: ZoneId): Decimal = {
871-
val offset = SECONDS.toMicros(
872-
zoneId.getRules.getOffset(microsToInstant(timestamp)).getTotalSeconds)
873-
val sinceEpoch = timestamp + offset
874-
Decimal(sinceEpoch, 20, 6)
875-
}
876-
877840
def currentTimestamp(): SQLTimestamp = instantToMicros(Instant.now())
878841

879842
def currentDate(zoneId: ZoneId): SQLDate = localDateToDays(LocalDate.now(zoneId))

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala

Lines changed: 0 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,91 +1032,6 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
10321032
checkEvaluation(makeTimestampExpr, Timestamp.valueOf("2019-08-12 00:00:58.000001"))
10331033
}
10341034

1035-
test("millennium") {
1036-
val date = MakeDate(Literal(2019), Literal(1), Literal(1))
1037-
checkEvaluation(Millennium(date), 3)
1038-
checkEvaluation(Millennium(date.copy(year = Literal(2001))), 3)
1039-
checkEvaluation(Millennium(date.copy(year = Literal(2000))), 2)
1040-
checkEvaluation(Millennium(date.copy(year = Literal(1001), day = Literal(28))), 2)
1041-
checkEvaluation(Millennium(date.copy(year = Literal(1))), 1)
1042-
checkEvaluation(Millennium(date.copy(year = Literal(-1))), -1)
1043-
checkEvaluation(Millennium(date.copy(year = Literal(-100), month = Literal(12))), -1)
1044-
checkEvaluation(Millennium(date.copy(year = Literal(-2019))), -3)
1045-
}
1046-
1047-
test("century") {
1048-
val date = MakeDate(Literal(2019), Literal(1), Literal(1))
1049-
checkEvaluation(Century(date), 21)
1050-
checkEvaluation(Century(date.copy(year = Literal(2001))), 21)
1051-
checkEvaluation(Century(date.copy(year = Literal(2000))), 20)
1052-
checkEvaluation(Century(date.copy(year = Literal(1001), day = Literal(28))), 11)
1053-
checkEvaluation(Century(date.copy(year = Literal(1))), 1)
1054-
checkEvaluation(Century(date.copy(year = Literal(-1))), -1)
1055-
checkEvaluation(Century(date.copy(year = Literal(-100), month = Literal(12))), -2)
1056-
checkEvaluation(Century(date.copy(year = Literal(-2019))), -21)
1057-
}
1058-
1059-
test("decade") {
1060-
val date = MakeDate(Literal(2019), Literal(8), Literal(8))
1061-
checkEvaluation(Decade(date), 201)
1062-
checkEvaluation(Decade(date.copy(year = Literal(2011))), 201)
1063-
checkEvaluation(Decade(date.copy(year = Literal(2010))), 201)
1064-
checkEvaluation(Decade(date.copy(year = Literal(2009))), 200)
1065-
checkEvaluation(Decade(date.copy(year = Literal(10))), 1)
1066-
checkEvaluation(Decade(date.copy(year = Literal(1))), 0)
1067-
checkEvaluation(Decade(date.copy(year = Literal(-1))), -1)
1068-
checkEvaluation(Decade(date.copy(year = Literal(-10))), -1)
1069-
checkEvaluation(Decade(date.copy(year = Literal(-11))), -2)
1070-
checkEvaluation(Decade(date.copy(year = Literal(-2019))), -202)
1071-
}
1072-
1073-
test("milliseconds and microseconds") {
1074-
outstandingTimezonesIds.foreach { timezone =>
1075-
var timestamp = MakeTimestamp(Literal(2019), Literal(8), Literal(10),
1076-
Literal(0), Literal(0), Literal(Decimal(BigDecimal(10.123456789), 8, 6)),
1077-
Some(Literal(timezone)), Some(timezone))
1078-
def millis(ts: MakeTimestamp): Milliseconds = Milliseconds(timestamp, Some(timezone))
1079-
def micros(ts: MakeTimestamp): Microseconds = Microseconds(timestamp, Some(timezone))
1080-
1081-
checkEvaluation(millis(timestamp), Decimal(BigDecimal(10123.457), 8, 3))
1082-
checkEvaluation(
1083-
millis(timestamp.copy(year = Literal(10))),
1084-
Decimal(BigDecimal(10123.457), 8, 3))
1085-
1086-
checkEvaluation(micros(timestamp), 10123457)
1087-
checkEvaluation(
1088-
micros(timestamp.copy(year = Literal(10))),
1089-
10123457)
1090-
1091-
timestamp = timestamp.copy(sec = Literal(Decimal(0.0, 8, 6)))
1092-
checkEvaluation(millis(timestamp), Decimal(0, 8, 3))
1093-
checkEvaluation(micros(timestamp), 0)
1094-
1095-
timestamp = timestamp.copy(sec = Literal(Decimal(BigDecimal(59.999999), 8, 6)))
1096-
checkEvaluation(millis(timestamp), Decimal(BigDecimal(59999.999), 8, 3))
1097-
checkEvaluation(micros(timestamp), 59999999)
1098-
1099-
timestamp = timestamp.copy(sec = Literal(Decimal(BigDecimal(60.0), 8, 6)))
1100-
checkEvaluation(millis(timestamp), Decimal(0, 8, 3))
1101-
checkEvaluation(micros(timestamp), 0)
1102-
}
1103-
}
1104-
1105-
test("epoch") {
1106-
val zoneId = ZoneId.systemDefault()
1107-
val nanos = 123456000
1108-
val timestamp = Epoch(MakeTimestamp(
1109-
Literal(2019), Literal(8), Literal(9), Literal(0), Literal(0),
1110-
Literal(Decimal(nanos / NANOS_PER_SECOND.toDouble, 8, 6)),
1111-
Some(Literal(zoneId.getId))))
1112-
val instant = LocalDateTime.of(2019, 8, 9, 0, 0, 0, nanos)
1113-
.atZone(zoneId).toInstant
1114-
val expected = Decimal(BigDecimal(nanos) / NANOS_PER_SECOND +
1115-
instant.getEpochSecond +
1116-
zoneId.getRules.getOffset(instant).getTotalSeconds)
1117-
checkEvaluation(timestamp, expected)
1118-
}
1119-
11201035
test("ISO 8601 week-numbering year") {
11211036
checkEvaluation(IsoYear(MakeDate(Literal(2006), Literal(1), Literal(1))), 2005)
11221037
checkEvaluation(IsoYear(MakeDate(Literal(2006), Literal(1), Literal(2))), 2006)

0 commit comments

Comments
 (0)