Skip to content

Commit 37d2e03

Browse files
yaooqinncloud-fan
authored andcommitted
[SPARK-31507][SQL] Remove uncommon fields support and update some fields with meaningful names for extract function
### What changes were proposed in this pull request? Extracting millennium, century, decade, millisecond, microsecond and epoch from datetime is neither ANSI standard nor quite common in modern SQL platforms. Most of the systems listing below does not support these except PostgreSQL and redshift. https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions050.htm https://prestodb.io/docs/current/functions/datetime.html https://docs.cloudera.com/documentation/enterprise/5-8-x/topics/impala_datetime_functions.html https://docs.snowflake.com/en/sql-reference/functions-date-time.html#label-supported-date-time-parts https://www.postgresql.org/docs/9.1/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT This PR removes these extract fields support from extract function for date and timestamp values `isoyear` is PostgreSQL specific but `yearofweek` is more commonly used across platforms `isodow` is PostgreSQL specific but `iso` as a suffix is more commonly used across platforms so, `dow_iso` and `dayofweek_iso` is used to replace it. For historical reasons, we have [`dayofweek`, `dow`] implemented for representing a non-ISO day-of-week and a newly added `isodow` from PostgreSQL for ISO day-of-week. Many other systems only have one week-numbering system support and use either full names or abbreviations. Things in spark become a little bit complicated. 1. because of the existence of `isodow`, so we need to add iso-prefix to `dayofweek` to make a pair for it too. [`dayofweek`, `isodayofweek`, `dow` and `isodow`] 2. because there are rare `iso`-prefixed systems and more systems choose `iso`-suffixed way, so we may result in [`dayofweek`, `dayofweekiso`, `dow`, `dowiso`] 3. `dayofweekiso` looks nice and has use cases in the platforms listed above, e.g. snowflake, but `dowiso` looks weird and no use cases found. 4. with a discussion the community,we have agreed with an underscore before `iso` may look much better because `isodow` is new and there is no standard for `iso` kind of things, so this may be good for us to make it simple and clear for end-users if they are well documented too. Thus, we finally result in [`dayofweek`, `dow`] for Non-ISO day-of-week system and [`dayofweek_iso`, `dow_iso`] for ISO system ### Why are the changes needed? Remove some nonstandard and uncommon features as we can add them back if necessary ### Does this PR introduce any user-facing change? NO, we should target this to 3.0.0 and these are added during 3.0.0 ### How was this patch tested? Remove unused tests Closes #28284 from yaooqinn/SPARK-31507. Authored-by: Kent Yao <[email protected]> Signed-off-by: Wenchen Fan <[email protected]>
1 parent 2c2062e commit 37d2e03

File tree

14 files changed

+576
-1806
lines changed

14 files changed

+576
-1806
lines changed

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

Lines changed: 11 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -335,48 +335,6 @@ case class SecondWithFraction(child: Expression, timeZoneId: Option[String] = No
335335
}
336336
}
337337

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

430-
case class IsoYear(child: Expression) extends UnaryExpression with ImplicitCastInputTypes {
388+
case class YearOfWeek(child: Expression) extends UnaryExpression with ImplicitCastInputTypes {
431389

432390
override def inputTypes: Seq[AbstractDataType] = Seq(DateType)
433391

434392
override def dataType: DataType = IntegerType
435393

436394
override protected def nullSafeEval(date: Any): Any = {
437-
DateTimeUtils.getIsoYear(date.asInstanceOf[Int])
395+
DateTimeUtils.getWeekBasedYear(date.asInstanceOf[Int])
438396
}
439397

440398
override protected def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
441399
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
442-
defineCodeGen(ctx, ev, c => s"$dtu.getIsoYear($c)")
400+
defineCodeGen(ctx, ev, c => s"$dtu.getWeekBasedYear($c)")
443401
}
444402
}
445403

@@ -2033,108 +1991,26 @@ case class MakeTimestamp(
20331991
override def prettyName: String = "make_timestamp"
20341992
}
20351993

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

21091996
def parseExtractField(
21101997
extractField: String,
21111998
source: Expression,
21121999
errorHandleFunc: => Nothing): Expression = extractField.toUpperCase(Locale.ROOT) match {
2113-
case "MILLENNIUM" | "MILLENNIA" | "MIL" | "MILS" => Millennium(source)
2114-
case "CENTURY" | "CENTURIES" | "C" | "CENT" => Century(source)
2115-
case "DECADE" | "DECADES" | "DEC" | "DECS" => Decade(source)
21162000
case "YEAR" | "Y" | "YEARS" | "YR" | "YRS" => Year(source)
2117-
case "ISOYEAR" => IsoYear(source)
2001+
case "YEAROFWEEK" => YearOfWeek(source)
21182002
case "QUARTER" | "QTR" => Quarter(source)
21192003
case "MONTH" | "MON" | "MONS" | "MONTHS" => Month(source)
21202004
case "WEEK" | "W" | "WEEKS" => WeekOfYear(source)
21212005
case "DAY" | "D" | "DAYS" => DayOfMonth(source)
21222006
case "DAYOFWEEK" | "DOW" => DayOfWeek(source)
2123-
case "ISODOW" => Add(WeekDay(source), Literal(1))
2007+
case "DAYOFWEEK_ISO" | "DOW_ISO" => Add(WeekDay(source), Literal(1))
21242008
case "DOY" => DayOfYear(source)
21252009
case "HOUR" | "H" | "HOURS" | "HR" | "HRS" => Hour(source)
21262010
case "MINUTE" | "M" | "MIN" | "MINS" | "MINUTES" => Minute(source)
21272011
case "SECOND" | "S" | "SEC" | "SECONDS" | "SECS" => SecondWithFraction(source)
2128-
case "MILLISECONDS" | "MSEC" | "MSECS" | "MILLISECON" | "MSECONDS" | "MS" =>
2129-
Milliseconds(source)
2130-
case "MICROSECONDS" | "USEC" | "USECS" | "USECONDS" | "MICROSECON" | "US" =>
2131-
Microseconds(source)
2132-
case "EPOCH" => Epoch(source)
21332012
case _ => errorHandleFunc
21342013
}
2135-
}
2136-
2137-
object DatePartLike {
21382014

21392015
def toEquivalentExpr(field: Expression, source: Expression): Expression = {
21402016
if (!field.foldable) {
@@ -2192,7 +2068,7 @@ case class DatePart(field: Expression, source: Expression, child: Expression)
21922068
extends RuntimeReplaceable {
21932069

21942070
def this(field: Expression, source: Expression) = {
2195-
this(field, source, DatePartLike.toEquivalentExpr(field, source))
2071+
this(field, source, DatePart.toEquivalentExpr(field, source))
21962072
}
21972073

21982074
override def flatArguments: Iterator[Any] = Iterator(field, source)
@@ -2206,26 +2082,20 @@ case class DatePart(field: Expression, source: Expression, child: Expression)
22062082
arguments = """
22072083
Arguments:
22082084
* field - selects which part of the source should be extracted
2209-
- Supported string values of `field` for dates and timestamps are:
2210-
- "MILLENNIUM", ("MILLENNIA", "MIL", "MILS") - the conventional numbering of millennia
2211-
- "CENTURY", ("CENTURIES", "C", "CENT") - the conventional numbering of centuries
2212-
- "DECADE", ("DECADES", "DEC", "DECS") - the year field divided by 10
2085+
- Supported string values of `field` for dates and timestamps are(case insensitive):
22132086
- "YEAR", ("Y", "YEARS", "YR", "YRS") - the year field
2214-
- "ISOYEAR" - the ISO 8601 week-numbering year that the datetime falls in
2087+
- "YEAROFWEEK" - the ISO 8601 week-numbering year that the datetime falls in. For example, 2005-01-02 is part of the 53rd week of year 2004, so the result is 2004
22152088
- "QUARTER", ("QTR") - the quarter (1 - 4) of the year that the datetime falls in
22162089
- "MONTH", ("MON", "MONS", "MONTHS") - the month field (1 - 12)
22172090
- "WEEK", ("W", "WEEKS") - the number of the ISO 8601 week-of-week-based-year. A week is considered to start on a Monday and week 1 is the first week with >3 days. In the ISO week-numbering system, it is possible for early-January dates to be part of the 52nd or 53rd week of the previous year, and for late-December dates to be part of the first week of the next year. For example, 2005-01-02 is part of the 53rd week of year 2004, while 2012-12-31 is part of the first week of 2013
22182091
- "DAY", ("D", "DAYS") - the day of the month field (1 - 31)
22192092
- "DAYOFWEEK",("DOW") - the day of the week for datetime as Sunday(1) to Saturday(7)
2220-
- "ISODOW" - ISO 8601 based day of the week for datetime as Monday(1) to Sunday(7)
2093+
- "DAYOFWEEK_ISO",("DOW_ISO") - ISO 8601 based day of the week for datetime as Monday(1) to Sunday(7)
22212094
- "DOY" - the day of the year (1 - 365/366)
22222095
- "HOUR", ("H", "HOURS", "HR", "HRS") - The hour field (0 - 23)
22232096
- "MINUTE", ("M", "MIN", "MINS", "MINUTES") - the minutes field (0 - 59)
22242097
- "SECOND", ("S", "SEC", "SECONDS", "SECS") - the seconds field, including fractional parts
2225-
- "MILLISECONDS", ("MSEC", "MSECS", "MILLISECON", "MSECONDS", "MS") - the seconds field, including fractional parts, multiplied by 1000. Note that this includes full seconds
2226-
- "MICROSECONDS", ("USEC", "USECS", "USECONDS", "MICROSECON", "US") - The seconds field, including fractional parts, multiplied by 1000000. Note that this includes full seconds
2227-
- "EPOCH" - the number of seconds with fractional part in microsecond precision since 1970-01-01 00:00:00 local time (can be negative)
2228-
- Supported string values of `field` for interval(which consists of `months`, `days`, `microseconds`) are:
2098+
- Supported string values of `field` for interval(which consists of `months`, `days`, `microseconds`) are(case insensitive):
22292099
- "YEAR", ("Y", "YEARS", "YR", "YRS") - the total `months` / 12
22302100
- "MONTH", ("MON", "MONS", "MONTHS") - the total `months` % 12
22312101
- "DAY", ("D", "DAYS") - the `days` part of interval
@@ -2258,7 +2128,7 @@ case class Extract(field: Expression, source: Expression, child: Expression)
22582128
extends RuntimeReplaceable {
22592129

22602130
def this(field: Expression, source: Expression) = {
2261-
this(field, source, DatePartLike.toEquivalentExpr(field, source))
2131+
this(field, source, DatePart.toEquivalentExpr(field, source))
22622132
}
22632133

22642134
override def flatArguments: Iterator[Any] = Iterator(field, source)

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

Lines changed: 1 addition & 38 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.
@@ -581,7 +555,7 @@ object DateTimeUtils {
581555
* Returns the year which conforms to ISO 8601. Each ISO 8601 week-numbering
582556
* year begins with the Monday of the week containing the 4th of January.
583557
*/
584-
def getIsoYear(date: SQLDate): Int = {
558+
def getWeekBasedYear(date: SQLDate): Int = {
585559
daysToLocalDate(date).get(IsoFields.WEEK_BASED_YEAR)
586560
}
587561

@@ -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: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,94 +1032,9 @@ 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") {
1121-
checkEvaluation(IsoYear(MakeDate(Literal(2006), Literal(1), Literal(1))), 2005)
1122-
checkEvaluation(IsoYear(MakeDate(Literal(2006), Literal(1), Literal(2))), 2006)
1036+
checkEvaluation(YearOfWeek(MakeDate(Literal(2006), Literal(1), Literal(1))), 2005)
1037+
checkEvaluation(YearOfWeek(MakeDate(Literal(2006), Literal(1), Literal(2))), 2006)
11231038
}
11241039

11251040
test("extract the seconds part with fraction from timestamps") {

0 commit comments

Comments
 (0)