Skip to content

Commit 79beb00

Browse files
committed
[SPARK-23905][SQL] Add UDF weekday
1 parent 8d40a79 commit 79beb00

File tree

5 files changed

+66
-10
lines changed

5 files changed

+66
-10
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionRegistry.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ object FunctionRegistry {
395395
expression[TruncTimestamp]("date_trunc"),
396396
expression[UnixTimestamp]("unix_timestamp"),
397397
expression[DayOfWeek]("dayofweek"),
398+
expression[WeekDay]("weekday"),
398399
expression[WeekOfYear]("weekofyear"),
399400
expression[Year]("year"),
400401
expression[TimeWindow]("window"),

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

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -426,15 +426,7 @@ case class DayOfMonth(child: Expression) extends UnaryExpression with ImplicitCa
426426
""",
427427
since = "2.3.0")
428428
// scalastyle:on line.size.limit
429-
case class DayOfWeek(child: Expression) extends UnaryExpression with ImplicitCastInputTypes {
430-
431-
override def inputTypes: Seq[AbstractDataType] = Seq(DateType)
432-
433-
override def dataType: DataType = IntegerType
434-
435-
@transient private lazy val c = {
436-
Calendar.getInstance(DateTimeUtils.getTimeZone("UTC"))
437-
}
429+
case class DayOfWeek(child: Expression) extends DayWeek {
438430

439431
override protected def nullSafeEval(date: Any): Any = {
440432
c.setTimeInMillis(date.asInstanceOf[Int] * 1000L * 3600L * 24L)
@@ -456,6 +448,49 @@ case class DayOfWeek(child: Expression) extends UnaryExpression with ImplicitCas
456448
}
457449
}
458450

451+
// scalastyle:off line.size.limit
452+
@ExpressionDescription(
453+
usage = "_FUNC_(date) - Returns the day of the week for date/timestamp (0 = Monday, 1 = Tuesday, ..., 6 = Sunday).",
454+
examples = """
455+
Examples:
456+
> SELECT _FUNC_('2009-07-30');
457+
3
458+
""",
459+
since = "2.4.0")
460+
// scalastyle:on line.size.limit
461+
case class WeekDay(child: Expression) extends DayWeek {
462+
463+
override protected def nullSafeEval(date: Any): Any = {
464+
c.setTimeInMillis(date.asInstanceOf[Int] * 1000L * 3600L * 24L)
465+
(c.get(Calendar.DAY_OF_WEEK) + 5 ) % 7
466+
}
467+
468+
override protected def doGenCode(ctx: CodegenContext, ev: ExprCode): ExprCode = {
469+
nullSafeCodeGen(ctx, ev, time => {
470+
val cal = classOf[Calendar].getName
471+
val dtu = DateTimeUtils.getClass.getName.stripSuffix("$")
472+
val c = "calWeekDay"
473+
ctx.addImmutableStateIfNotExists(cal, c,
474+
v => s"""$v = $cal.getInstance($dtu.getTimeZone("UTC"));""")
475+
s"""
476+
$c.setTimeInMillis($time * 1000L * 3600L * 24L);
477+
${ev.value} = ($c.get($cal.DAY_OF_WEEK) + 5) % 7;
478+
"""
479+
})
480+
}
481+
}
482+
483+
abstract class DayWeek extends UnaryExpression with ImplicitCastInputTypes {
484+
485+
override def inputTypes: Seq[AbstractDataType] = Seq(DateType)
486+
487+
override def dataType: DataType = IntegerType
488+
489+
@transient protected lazy val c: Calendar = {
490+
Calendar.getInstance(DateTimeUtils.getTimeZone("UTC"))
491+
}
492+
}
493+
459494
// scalastyle:off line.size.limit
460495
@ExpressionDescription(
461496
usage = "_FUNC_(date) - Returns the week of the year of the given date. A week is considered to start on a Monday and week 1 is the first week with >3 days.",

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,17 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
211211
checkConsistencyBetweenInterpretedAndCodegen(DayOfWeek, DateType)
212212
}
213213

214+
test("WeekDay") {
215+
checkEvaluation(WeekDay(Literal.create(null, DateType)), null)
216+
checkEvaluation(WeekDay(Literal(d)), 2)
217+
checkEvaluation(WeekDay(Cast(Literal(sdfDate.format(d)), DateType, gmtId)), 2)
218+
checkEvaluation(WeekDay(Cast(Literal(ts), DateType, gmtId)), 4)
219+
checkEvaluation(WeekDay(Cast(Literal("2011-05-06"), DateType, gmtId)), 4)
220+
checkEvaluation(WeekDay(Literal(new Date(sdf.parse("2017-05-27 13:10:15").getTime))), 5)
221+
checkEvaluation(WeekDay(Literal(new Date(sdf.parse("1582-10-15 13:10:15").getTime))), 4)
222+
checkConsistencyBetweenInterpretedAndCodegen(WeekDay, DateType)
223+
}
224+
214225
test("WeekOfYear") {
215226
checkEvaluation(WeekOfYear(Literal.create(null, DateType)), null)
216227
checkEvaluation(WeekOfYear(Literal(d)), 15)

sql/core/src/test/resources/sql-tests/inputs/datetime.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ create temporary view ttf2 as select * from values
2525
select current_date = current_date(), current_timestamp = current_timestamp(), a, b from ttf2;
2626

2727
select a, b from ttf2 order by a, current_date;
28+
29+
select weekday('2007-02-03'), weekday('2009-07-30'), weekday('2017-05-27'), weekday(null), weekday('1582-10-15 13:10:15');

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
-- Automatically generated by SQLQueryTestSuite
2-
-- Number of queries: 9
2+
-- Number of queries: 10
33

44

55
-- !query 0
@@ -81,3 +81,10 @@ struct<a:int,b:int>
8181
-- !query 8 output
8282
1 2
8383
2 3
84+
85+
-- !query 9
86+
select weekday('2007-02-03'), weekday('2009-07-30'), weekday('2017-05-27'), weekday(null), weekday('1582-10-15 13:10:15')
87+
-- !query 3 schema
88+
struct<weekday(CAST(2007-02-03 AS DATE)):int,weekday(CAST(2009-07-30 AS DATE)):int,weekday(CAST(2017-05-27 AS DATE)):int,weekday(CAST(NULL AS DATE)):int,weekday(CAST(1582-10-15 13:10:15 AS DATE)):int>
89+
-- !query 3 output
90+
5 3 5 NULL 4

0 commit comments

Comments
 (0)