Skip to content

Commit c506661

Browse files
committed
function date_add , date_sub
1 parent 163e3f1 commit c506661

File tree

5 files changed

+127
-1
lines changed

5 files changed

+127
-1
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,9 @@ object FunctionRegistry {
183183
// datetime functions
184184
expression[CurrentDate]("current_date"),
185185
expression[CurrentTimestamp]("current_timestamp"),
186+
expression[DateAdd]("date_add"),
186187
expression[DateFormatClass]("date_format"),
188+
expression[DateSub]("date_sub"),
187189
expression[DayOfMonth]("day"),
188190
expression[DayOfYear]("dayofyear"),
189191
expression[DayOfMonth]("dayofmonth"),

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,53 @@ case class CurrentTimestamp() extends LeafExpression with CodegenFallback {
6262
}
6363
}
6464

65+
/**
66+
* Adds a number of days to startdate.
67+
*/
68+
case class DateAdd(startDate: Expression, days: Expression)
69+
extends BinaryExpression with ImplicitCastInputTypes {
70+
71+
override def left: Expression = startDate
72+
override def right: Expression = days
73+
74+
override def inputTypes: Seq[AbstractDataType] = Seq(DateType, IntegerType)
75+
76+
override def dataType: DataType = DateType
77+
78+
override def nullSafeEval(start: Any, d: Any): Any = {
79+
start.asInstanceOf[Int] + d.asInstanceOf[Int]
80+
}
81+
82+
override def genCode(ctx: CodeGenContext, ev: GeneratedExpressionCode): String = {
83+
nullSafeCodeGen(ctx, ev, (sd, d) => {
84+
s"""${ev.primitive} = $sd + $d;"""
85+
})
86+
}
87+
}
88+
89+
/**
90+
* Subtracts a number of days to startdate.
91+
*/
92+
case class DateSub(startDate: Expression, days: Expression)
93+
extends BinaryExpression with ImplicitCastInputTypes {
94+
override def left: Expression = startDate
95+
override def right: Expression = days
96+
97+
override def inputTypes: Seq[AbstractDataType] = Seq(DateType, IntegerType)
98+
99+
override def dataType: DataType = DateType
100+
101+
override def nullSafeEval(start: Any, d: Any): Any = {
102+
start.asInstanceOf[Int] - d.asInstanceOf[Int]
103+
}
104+
105+
override def genCode(ctx: CodeGenContext, ev: GeneratedExpressionCode): String = {
106+
nullSafeCodeGen(ctx, ev, (sd, d) => {
107+
s"""${ev.primitive} = $sd - $d;"""
108+
})
109+
}
110+
}
111+
65112
case class Hour(child: Expression) extends UnaryExpression with ImplicitCastInputTypes {
66113

67114
override def inputTypes: Seq[AbstractDataType] = Seq(TimestampType)

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

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import java.text.SimpleDateFormat
2222
import java.util.Calendar
2323

2424
import org.apache.spark.SparkFunSuite
25-
import org.apache.spark.sql.types.{StringType, TimestampType, DateType}
25+
import org.apache.spark.sql.catalyst.util.DateTimeUtils
26+
import org.apache.spark.sql.types.{IntegerType, StringType, TimestampType, DateType}
2627

2728
class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
2829

@@ -246,4 +247,36 @@ class DateExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
246247
}
247248
}
248249

250+
test("date_add") {
251+
checkResult(
252+
DateAdd(Literal(Date.valueOf("2016-02-28")), Literal(1)),
253+
DateTimeUtils.fromJavaDate(Date.valueOf("2016-02-29")))
254+
checkResult(
255+
DateAdd(Literal("2016-03-01"), Literal(-1)),
256+
DateTimeUtils.fromJavaDate(Date.valueOf("2016-02-29")))
257+
checkResult(
258+
DateAdd(Literal(Timestamp.valueOf("2016-03-01 23:59:59")), Literal(-2)),
259+
DateTimeUtils.fromJavaDate(Date.valueOf("2016-02-28")))
260+
checkResult(
261+
DateAdd(Literal("2016-03-01 23:59:59"), Literal(-3)),
262+
DateTimeUtils.fromJavaDate(Date.valueOf("2016-02-27")))
263+
checkResult(DateAdd(Literal(null), Literal(-1)), null)
264+
}
265+
266+
test("date_sub") {
267+
checkResult(
268+
DateSub(Literal("2015-01-01"), Literal(1)),
269+
DateTimeUtils.fromJavaDate(Date.valueOf("2014-12-31")))
270+
checkResult(
271+
DateSub(Literal(Date.valueOf("2015-01-01")), Literal(-1)),
272+
DateTimeUtils.fromJavaDate(Date.valueOf("2015-01-02")))
273+
checkResult(
274+
DateSub(Literal(Timestamp.valueOf("2015-01-01 01:00:00")), Literal(-1)),
275+
DateTimeUtils.fromJavaDate(Date.valueOf("2015-01-02")))
276+
checkResult(
277+
DateSub(Literal("2015-01-01 01:00:00"), Literal(0)),
278+
DateTimeUtils.fromJavaDate(Date.valueOf("2015-01-01")))
279+
checkResult(
280+
DateSub(Literal("2015-01-01"), Literal.create(null, IntegerType)), null)
281+
}
249282
}

sql/core/src/main/scala/org/apache/spark/sql/functions.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2293,6 +2293,20 @@ object functions {
22932293
def date_format(dateColumnName: String, format: String): Column =
22942294
date_format(Column(dateColumnName), format)
22952295

2296+
/**
2297+
* Extracts the year as an integer from a given date/timestamp/string.
2298+
* @group datetime_funcs
2299+
* @since 1.5.0
2300+
*/
2301+
def date_add(startdate: Column, days: Column): Column = DateAdd(startdate.expr, days.expr)
2302+
2303+
/**
2304+
* Extracts the year as an integer from a given date/timestamp/string.
2305+
* @group datetime_funcs
2306+
* @since 1.5.0
2307+
*/
2308+
def date_sub(startdate: Column, days: Column): Column = DateSub(startdate.expr, days.expr)
2309+
22962310
/**
22972311
* Extracts the year as an integer from a given date/timestamp/string.
22982312
* @group datetime_funcs

sql/core/src/test/scala/org/apache/spark/sql/DateFunctionsSuite.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,34 @@ class DateFunctionsSuite extends QueryTest {
184184
Row(15, 15, 15))
185185
}
186186

187+
test("function date_add") {
188+
val df =
189+
Seq((1, Date.valueOf("2015-06-01")), (3, Date.valueOf("2015-06-02"))).toDF("num", "day")
190+
checkAnswer(
191+
df.select(date_add(col("day"), col("num"))),
192+
Seq(Row(Date.valueOf("2015-06-02")), Row(Date.valueOf("2015-06-05"))))
193+
checkAnswer(
194+
df.select(date_add(column("day"), lit(null))).limit(1), Row(null))
195+
196+
checkAnswer(df.selectExpr("DATE_ADD(null, num)"), Seq(Row(null), Row(null)))
197+
checkAnswer(
198+
df.selectExpr("""DATE_ADD(day, num)"""),
199+
Seq(Row(Date.valueOf("2015-06-02")), Row(Date.valueOf("2015-06-05"))))
200+
}
201+
202+
test("function date_sub") {
203+
val df =
204+
Seq((1, Date.valueOf("2015-06-01")), (3, Date.valueOf("2015-06-02"))).toDF("num", "day")
205+
checkAnswer(
206+
df.select(date_sub(col("day"), col("num"))),
207+
Seq(Row(Date.valueOf("2015-05-31")), Row(Date.valueOf("2015-05-30"))))
208+
checkAnswer(
209+
df.select(date_sub(lit(null), column("num"))).limit(1), Row(null))
210+
211+
checkAnswer(df.selectExpr("""DATE_SUB(day, null)"""), Seq(Row(null), Row(null)))
212+
checkAnswer(
213+
df.selectExpr("""DATE_SUB(day, num)"""),
214+
Seq(Row(Date.valueOf("2015-05-31")), Row(Date.valueOf("2015-05-30"))))
215+
}
216+
187217
}

0 commit comments

Comments
 (0)