This is an automated email from the ASF dual-hosted git repository.
philo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-gluten.git
The following commit(s) were added to refs/heads/main by this push:
new 61e627b5ec [GLUTEN-10781][VL] Add support for `months_between`
function (#10782)
61e627b5ec is described below
commit 61e627b5ec00c149256b1b3fd2397e835c7bd681
Author: Mingliang Zhu <[email protected]>
AuthorDate: Fri Oct 10 15:55:29 2025 +0800
[GLUTEN-10781][VL] Add support for `months_between` function (#10782)
---
.../clickhouse/CHSparkPlanExecApi.scala | 9 +++
.../expression/CHExpressionTransformer.scala | 13 ++++
.../backendsapi/velox/VeloxSparkPlanExecApi.scala | 9 +++
.../functions/DateFunctionsValidateSuite.scala | 17 +++++
.../gluten/backendsapi/SparkPlanExecApi.scala | 7 ++
.../DateTimeExpressionsTransformer.scala | 3 +-
.../gluten/expression/ExpressionConverter.scala | 2 +-
.../utils/clickhouse/ClickHouseTestSettings.scala | 1 +
.../gluten/utils/velox/VeloxTestSettings.scala | 2 +
.../expressions/GlutenDateExpressionsSuite.scala | 81 +++++++++++++++++++++-
.../utils/clickhouse/ClickHouseTestSettings.scala | 1 +
.../gluten/utils/velox/VeloxTestSettings.scala | 2 +
.../expressions/GlutenDateExpressionsSuite.scala | 79 +++++++++++++++++++++
.../utils/clickhouse/ClickHouseTestSettings.scala | 1 +
.../gluten/utils/velox/VeloxTestSettings.scala | 2 +
.../expressions/GlutenDateExpressionsSuite.scala | 79 +++++++++++++++++++++
.../utils/clickhouse/ClickHouseTestSettings.scala | 1 +
.../gluten/utils/velox/VeloxTestSettings.scala | 2 +
.../expressions/GlutenDateExpressionsSuite.scala | 79 +++++++++++++++++++++
19 files changed, 386 insertions(+), 4 deletions(-)
diff --git
a/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHSparkPlanExecApi.scala
b/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHSparkPlanExecApi.scala
index 1291b4d257..dbaccb16c8 100644
---
a/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHSparkPlanExecApi.scala
+++
b/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHSparkPlanExecApi.scala
@@ -1043,4 +1043,13 @@ class CHSparkPlanExecApi extends SparkPlanExecApi with
Logging {
extract.get.last,
original)
}
+
+ override def genMonthsBetweenTransformer(
+ substraitExprName: String,
+ date1: ExpressionTransformer,
+ date2: ExpressionTransformer,
+ roundOff: ExpressionTransformer,
+ original: MonthsBetween): ExpressionTransformer = {
+ CHMonthsBetweenTransformer(substraitExprName, date1, date2, roundOff,
original)
+ }
}
diff --git
a/backends-clickhouse/src/main/scala/org/apache/gluten/expression/CHExpressionTransformer.scala
b/backends-clickhouse/src/main/scala/org/apache/gluten/expression/CHExpressionTransformer.scala
index 984fb2f790..d0137b9fd2 100644
---
a/backends-clickhouse/src/main/scala/org/apache/gluten/expression/CHExpressionTransformer.scala
+++
b/backends-clickhouse/src/main/scala/org/apache/gluten/expression/CHExpressionTransformer.scala
@@ -307,3 +307,16 @@ case class CHTimestampAddTransformer(
Seq(LiteralTransformer(unit), left, right, LiteralTransformer(timeZoneId))
}
}
+
+case class CHMonthsBetweenTransformer(
+ substraitExprName: String,
+ date1: ExpressionTransformer,
+ date2: ExpressionTransformer,
+ roundOff: ExpressionTransformer,
+ original: MonthsBetween)
+ extends ExpressionTransformer {
+ override def children: Seq[ExpressionTransformer] = {
+ val timeZoneId = original.timeZoneId.map(timeZoneId =>
LiteralTransformer(timeZoneId))
+ Seq(date1, date2, roundOff) ++ timeZoneId
+ }
+}
diff --git
a/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxSparkPlanExecApi.scala
b/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxSparkPlanExecApi.scala
index 6d46d09c1f..20f52997ab 100644
---
a/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxSparkPlanExecApi.scala
+++
b/backends-velox/src/main/scala/org/apache/gluten/backendsapi/velox/VeloxSparkPlanExecApi.scala
@@ -1064,4 +1064,13 @@ class VeloxSparkPlanExecApi extends SparkPlanExecApi {
original: Expression): ExpressionTransformer = {
ToUnixTimestampTransformer(substraitExprName, timeExp, format, original)
}
+
+ override def genMonthsBetweenTransformer(
+ substraitExprName: String,
+ date1: ExpressionTransformer,
+ date2: ExpressionTransformer,
+ roundOff: ExpressionTransformer,
+ original: MonthsBetween): ExpressionTransformer = {
+ MonthsBetweenTransformer(substraitExprName, date1, date2, roundOff,
original)
+ }
}
diff --git
a/backends-velox/src/test/scala/org/apache/gluten/functions/DateFunctionsValidateSuite.scala
b/backends-velox/src/test/scala/org/apache/gluten/functions/DateFunctionsValidateSuite.scala
index 81e1457021..c3e0165873 100644
---
a/backends-velox/src/test/scala/org/apache/gluten/functions/DateFunctionsValidateSuite.scala
+++
b/backends-velox/src/test/scala/org/apache/gluten/functions/DateFunctionsValidateSuite.scala
@@ -488,4 +488,21 @@ abstract class DateFunctionsValidateSuite extends
FunctionsValidateSuite {
}
}
}
+
+ test("months_between") {
+ withTempPath {
+ path =>
+ val t1 = Timestamp.valueOf("1997-02-28 10:30:00")
+ val t2 = Timestamp.valueOf("1996-10-30 00:00:00")
+ Seq((t1, t2)).toDF("t1", "t2").write.parquet(path.getCanonicalPath)
+
+
spark.read.parquet(path.getCanonicalPath).createOrReplaceTempView("time")
+ runQueryAndCompare("select months_between(t1, t2) from time") {
+ checkGlutenOperatorMatch[ProjectExecTransformer]
+ }
+ runQueryAndCompare("select months_between(t1, t2, false) from time") {
+ checkGlutenOperatorMatch[ProjectExecTransformer]
+ }
+ }
+ }
}
diff --git
a/gluten-substrait/src/main/scala/org/apache/gluten/backendsapi/SparkPlanExecApi.scala
b/gluten-substrait/src/main/scala/org/apache/gluten/backendsapi/SparkPlanExecApi.scala
index fc53cbf6a3..2ee41f0d8d 100644
---
a/gluten-substrait/src/main/scala/org/apache/gluten/backendsapi/SparkPlanExecApi.scala
+++
b/gluten-substrait/src/main/scala/org/apache/gluten/backendsapi/SparkPlanExecApi.scala
@@ -802,6 +802,13 @@ trait SparkPlanExecApi {
throw new GlutenNotSupportException("timestampdiff is not supported")
}
+ def genMonthsBetweenTransformer(
+ substraitExprName: String,
+ date1: ExpressionTransformer,
+ date2: ExpressionTransformer,
+ roundOff: ExpressionTransformer,
+ original: MonthsBetween): ExpressionTransformer
+
def isRowIndexMetadataColumn(columnName: String): Boolean = {
SparkShimLoader.getSparkShims.isRowIndexMetadataColumn(columnName)
}
diff --git
a/gluten-substrait/src/main/scala/org/apache/gluten/expression/DateTimeExpressionsTransformer.scala
b/gluten-substrait/src/main/scala/org/apache/gluten/expression/DateTimeExpressionsTransformer.scala
index 2e2611d5ab..4385419b02 100644
---
a/gluten-substrait/src/main/scala/org/apache/gluten/expression/DateTimeExpressionsTransformer.scala
+++
b/gluten-substrait/src/main/scala/org/apache/gluten/expression/DateTimeExpressionsTransformer.scala
@@ -56,8 +56,7 @@ case class MonthsBetweenTransformer(
original: MonthsBetween)
extends ExpressionTransformer {
override def children: Seq[ExpressionTransformer] = {
- val timeZoneId = original.timeZoneId.map(timeZoneId =>
LiteralTransformer(timeZoneId))
- Seq(date1, date2, roundOff) ++ timeZoneId
+ Seq(date1, date2, roundOff)
}
}
diff --git
a/gluten-substrait/src/main/scala/org/apache/gluten/expression/ExpressionConverter.scala
b/gluten-substrait/src/main/scala/org/apache/gluten/expression/ExpressionConverter.scala
index 37f90d512e..43837274a2 100644
---
a/gluten-substrait/src/main/scala/org/apache/gluten/expression/ExpressionConverter.scala
+++
b/gluten-substrait/src/main/scala/org/apache/gluten/expression/ExpressionConverter.scala
@@ -328,7 +328,7 @@ object ExpressionConverter extends SQLConfHelper with
Logging {
t
)
case m: MonthsBetween =>
- MonthsBetweenTransformer(
+
BackendsApiManager.getSparkPlanExecApiInstance.genMonthsBetweenTransformer(
substraitExprName,
replaceWithExpressionTransformer0(m.date1, attributeSeq,
expressionsMap),
replaceWithExpressionTransformer0(m.date2, attributeSeq,
expressionsMap),
diff --git
a/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
b/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
index 2d867a3226..7f9bdba52b 100644
---
a/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
+++
b/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
@@ -695,6 +695,7 @@ class ClickHouseTestSettings extends BackendTestSettings {
.exclude("add_months")
.exclude("SPARK-34721: add a year-month interval to a date")
.exclude("months_between")
+ .excludeGlutenTest("months_between")
.exclude("next_day")
.exclude("TruncDate")
.exclude("TruncTimestamp")
diff --git
a/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
b/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index dcf598887d..43a3250fc9 100644
---
a/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++
b/gluten-ut/spark32/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -219,6 +219,8 @@ class VeloxTestSettings extends BackendTestSettings {
.exclude("to_timestamp exception mode")
// Replaced by a gluten test to pass timezone through config.
.exclude("from_unixtime")
+ // Replaced by a gluten test to pass timezone through config.
+ .exclude("months_between")
//
https://github.com/facebookincubator/velox/pull/10563/files#diff-140dc50e6dac735f72d29014da44b045509df0dd1737f458de1fe8cfd33d8145
.excludeGlutenTest("from_unixtime")
enableSuite[GlutenDecimalExpressionSuite]
diff --git
a/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
b/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
index dfa3640679..9c215f13bd 100644
---
a/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
+++
b/gluten-ut/spark32/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
@@ -23,7 +23,7 @@ import org.apache.spark.sql.catalyst.util.DateTimeTestUtils._
import org.apache.spark.sql.catalyst.util.DateTimeUtils
import org.apache.spark.sql.catalyst.util.DateTimeUtils.{getZoneId,
TimeZoneUTC}
import org.apache.spark.sql.internal.SQLConf
-import org.apache.spark.sql.types.{DateType, IntegerType, LongType,
StringType, TimestampNTZType, TimestampType}
+import org.apache.spark.sql.types._
import org.apache.spark.unsafe.types.UTF8String
import java.sql.{Date, Timestamp}
@@ -476,4 +476,83 @@ class GlutenDateExpressionsSuite extends
DateExpressionsSuite with GlutenTestsTr
}
}
}
+
+ testGluten("months_between") {
+ val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
+ for (zid <- outstandingZoneIds) {
+ withSQLConf(
+ SQLConf.SESSION_LOCAL_TIMEZONE.key -> zid.getId
+ ) {
+ val timeZoneId = Option(zid.getId)
+ sdf.setTimeZone(TimeZone.getTimeZone(zid))
+
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("1997-02-28 10:30:00").getTime)),
+ Literal(new Timestamp(sdf.parse("1996-10-30 00:00:00").getTime)),
+ Literal.TrueLiteral,
+ timeZoneId = timeZoneId
+ ),
+ 3.94959677
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("1997-02-28 10:30:00").getTime)),
+ Literal(new Timestamp(sdf.parse("1996-10-30 00:00:00").getTime)),
+ Literal.FalseLiteral,
+ timeZoneId = timeZoneId
+ ),
+ 3.9495967741935485
+ )
+
+ Seq(Literal.FalseLiteral, Literal.TrueLiteral).foreach {
+ roundOff =>
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-01-30
11:52:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-01-30
11:50:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ 0.0
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-01-31
00:00:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-03-31
22:00:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ -2.0
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-03-31
22:00:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-02-28
00:00:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ 1.0
+ )
+ }
+ val t = Literal(Timestamp.valueOf("2015-03-31 22:00:00"))
+ val tnull = Literal.create(null, TimestampType)
+ checkEvaluation(MonthsBetween(t, tnull, Literal.TrueLiteral,
timeZoneId = timeZoneId), null)
+ checkEvaluation(MonthsBetween(tnull, t, Literal.TrueLiteral,
timeZoneId = timeZoneId), null)
+ checkEvaluation(
+ MonthsBetween(tnull, tnull, Literal.TrueLiteral, timeZoneId =
timeZoneId),
+ null)
+ checkEvaluation(
+ MonthsBetween(t, t, Literal.create(null, BooleanType), timeZoneId =
timeZoneId),
+ null)
+ checkConsistencyBetweenInterpretedAndCodegen(
+ (time1: Expression, time2: Expression, roundOff: Expression) =>
+ MonthsBetween(time1, time2, roundOff, timeZoneId = timeZoneId),
+ TimestampType,
+ TimestampType,
+ BooleanType
+ )
+ }
+ }
+ }
}
diff --git
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
index d0979b5fea..6ce7d1e325 100644
---
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
+++
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
@@ -717,6 +717,7 @@ class ClickHouseTestSettings extends BackendTestSettings {
.exclude("add_months")
.exclude("SPARK-34721: add a year-month interval to a date")
.exclude("months_between")
+ .excludeGlutenTest("months_between")
.exclude("next_day")
.exclude("TruncDate")
.exclude("TruncTimestamp")
diff --git
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index 2b0c748339..52ce14bda3 100644
---
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -141,6 +141,8 @@ class VeloxTestSettings extends BackendTestSettings {
.exclude("to_timestamp exception mode")
// Replaced by a gluten test to pass timezone through config.
.exclude("from_unixtime")
+ // Replaced by a gluten test to pass timezone through config.
+ .exclude("months_between")
.exclude("test timestamp add")
//
https://github.com/facebookincubator/velox/pull/10563/files#diff-140dc50e6dac735f72d29014da44b045509df0dd1737f458de1fe8cfd33d8145
.excludeGlutenTest("from_unixtime")
diff --git
a/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
b/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
index 34c2358fe7..8c494699c1 100644
---
a/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
+++
b/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
@@ -496,4 +496,83 @@ class GlutenDateExpressionsSuite extends
DateExpressionsSuite with GlutenTestsTr
TimestampAdd("YEAR", Literal(1), Literal(Timestamp.valueOf("2022-02-15
12:57:00"))),
Timestamp.valueOf("2023-02-15 12:57:00"))
}
+
+ testGluten("months_between") {
+ val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
+ for (zid <- outstandingZoneIds) {
+ withSQLConf(
+ SQLConf.SESSION_LOCAL_TIMEZONE.key -> zid.getId
+ ) {
+ val timeZoneId = Option(zid.getId)
+ sdf.setTimeZone(TimeZone.getTimeZone(zid))
+
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("1997-02-28 10:30:00").getTime)),
+ Literal(new Timestamp(sdf.parse("1996-10-30 00:00:00").getTime)),
+ Literal.TrueLiteral,
+ timeZoneId = timeZoneId
+ ),
+ 3.94959677
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("1997-02-28 10:30:00").getTime)),
+ Literal(new Timestamp(sdf.parse("1996-10-30 00:00:00").getTime)),
+ Literal.FalseLiteral,
+ timeZoneId = timeZoneId
+ ),
+ 3.9495967741935485
+ )
+
+ Seq(Literal.FalseLiteral, Literal.TrueLiteral).foreach {
+ roundOff =>
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-01-30
11:52:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-01-30
11:50:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ 0.0
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-01-31
00:00:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-03-31
22:00:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ -2.0
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-03-31
22:00:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-02-28
00:00:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ 1.0
+ )
+ }
+ val t = Literal(Timestamp.valueOf("2015-03-31 22:00:00"))
+ val tnull = Literal.create(null, TimestampType)
+ checkEvaluation(MonthsBetween(t, tnull, Literal.TrueLiteral,
timeZoneId = timeZoneId), null)
+ checkEvaluation(MonthsBetween(tnull, t, Literal.TrueLiteral,
timeZoneId = timeZoneId), null)
+ checkEvaluation(
+ MonthsBetween(tnull, tnull, Literal.TrueLiteral, timeZoneId =
timeZoneId),
+ null)
+ checkEvaluation(
+ MonthsBetween(t, t, Literal.create(null, BooleanType), timeZoneId =
timeZoneId),
+ null)
+ checkConsistencyBetweenInterpretedAndCodegen(
+ (time1: Expression, time2: Expression, roundOff: Expression) =>
+ MonthsBetween(time1, time2, roundOff, timeZoneId = timeZoneId),
+ TimestampType,
+ TimestampType,
+ BooleanType
+ )
+ }
+ }
+ }
}
diff --git
a/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
b/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
index 36beac1eb4..21cf94a61e 100644
---
a/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
+++
b/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
@@ -601,6 +601,7 @@ class ClickHouseTestSettings extends BackendTestSettings {
.exclude("add_months")
.exclude("SPARK-34721: add a year-month interval to a date")
.exclude("months_between")
+ .excludeGlutenTest("months_between")
.exclude("next_day")
.exclude("TruncDate")
.exclude("TruncTimestamp")
diff --git
a/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
b/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index 3cc38a2e98..519cef5b76 100644
---
a/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++
b/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -137,6 +137,8 @@ class VeloxTestSettings extends BackendTestSettings {
.exclude("to_timestamp exception mode")
// Replaced by a gluten test to pass timezone through config.
.exclude("from_unixtime")
+ // Replaced by a gluten test to pass timezone through config.
+ .exclude("months_between")
// Vanilla Spark does not have a unified DST Timestamp fastTime.
1320570000000L and
// 1320566400000L both represent 2011-11-06 01:00:00
.exclude("SPARK-42635: timestampadd near daylight saving transition")
diff --git
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
index 794db27c12..c290322850 100644
---
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
+++
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
@@ -578,4 +578,83 @@ class GlutenDateExpressionsSuite extends
DateExpressionsSuite with GlutenTestsTr
repeatedTime)
}
}
+
+ testGluten("months_between") {
+ val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
+ for (zid <- outstandingZoneIds) {
+ withSQLConf(
+ SQLConf.SESSION_LOCAL_TIMEZONE.key -> zid.getId
+ ) {
+ val timeZoneId = Option(zid.getId)
+ sdf.setTimeZone(TimeZone.getTimeZone(zid))
+
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("1997-02-28 10:30:00").getTime)),
+ Literal(new Timestamp(sdf.parse("1996-10-30 00:00:00").getTime)),
+ Literal.TrueLiteral,
+ timeZoneId = timeZoneId
+ ),
+ 3.94959677
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("1997-02-28 10:30:00").getTime)),
+ Literal(new Timestamp(sdf.parse("1996-10-30 00:00:00").getTime)),
+ Literal.FalseLiteral,
+ timeZoneId = timeZoneId
+ ),
+ 3.9495967741935485
+ )
+
+ Seq(Literal.FalseLiteral, Literal.TrueLiteral).foreach {
+ roundOff =>
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-01-30
11:52:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-01-30
11:50:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ 0.0
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-01-31
00:00:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-03-31
22:00:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ -2.0
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-03-31
22:00:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-02-28
00:00:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ 1.0
+ )
+ }
+ val t = Literal(Timestamp.valueOf("2015-03-31 22:00:00"))
+ val tnull = Literal.create(null, TimestampType)
+ checkEvaluation(MonthsBetween(t, tnull, Literal.TrueLiteral,
timeZoneId = timeZoneId), null)
+ checkEvaluation(MonthsBetween(tnull, t, Literal.TrueLiteral,
timeZoneId = timeZoneId), null)
+ checkEvaluation(
+ MonthsBetween(tnull, tnull, Literal.TrueLiteral, timeZoneId =
timeZoneId),
+ null)
+ checkEvaluation(
+ MonthsBetween(t, t, Literal.create(null, BooleanType), timeZoneId =
timeZoneId),
+ null)
+ checkConsistencyBetweenInterpretedAndCodegen(
+ (time1: Expression, time2: Expression, roundOff: Expression) =>
+ MonthsBetween(time1, time2, roundOff, timeZoneId = timeZoneId),
+ TimestampType,
+ TimestampType,
+ BooleanType
+ )
+ }
+ }
+ }
}
diff --git
a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
index f30f1543a4..e7e3ddf8a0 100644
---
a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
+++
b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
@@ -696,6 +696,7 @@ class ClickHouseTestSettings extends BackendTestSettings {
.excludeCH("WeekOfYear")
.excludeCH("add_months")
.excludeCH("months_between")
+ .excludeGlutenTest("months_between")
.excludeCH("TruncDate")
.excludeCH("unsupported fmt fields for trunc/date_trunc results null")
.excludeCH("to_utc_timestamp")
diff --git
a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index 4088e08ab0..27af909029 100644
---
a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++
b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -137,6 +137,8 @@ class VeloxTestSettings extends BackendTestSettings {
.exclude("to_timestamp exception mode")
// Replaced by a gluten test to pass timezone through config.
.exclude("from_unixtime")
+ // Replaced by a gluten test to pass timezone through config.
+ .exclude("months_between")
// Vanilla Spark does not have a unified DST Timestamp fastTime.
1320570000000L and
// 1320566400000L both represent 2011-11-06 01:00:00.
.exclude("SPARK-42635: timestampadd near daylight saving transition")
diff --git
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
index d53c9187d3..da68989a33 100644
---
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
+++
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenDateExpressionsSuite.scala
@@ -578,4 +578,83 @@ class GlutenDateExpressionsSuite extends
DateExpressionsSuite with GlutenTestsTr
repeatedTime)
}
}
+
+ testGluten("months_between") {
+ val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
+ for (zid <- outstandingZoneIds) {
+ withSQLConf(
+ SQLConf.SESSION_LOCAL_TIMEZONE.key -> zid.getId
+ ) {
+ val timeZoneId = Option(zid.getId)
+ sdf.setTimeZone(TimeZone.getTimeZone(zid))
+
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("1997-02-28 10:30:00").getTime)),
+ Literal(new Timestamp(sdf.parse("1996-10-30 00:00:00").getTime)),
+ Literal.TrueLiteral,
+ timeZoneId = timeZoneId
+ ),
+ 3.94959677
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("1997-02-28 10:30:00").getTime)),
+ Literal(new Timestamp(sdf.parse("1996-10-30 00:00:00").getTime)),
+ Literal.FalseLiteral,
+ timeZoneId = timeZoneId
+ ),
+ 3.9495967741935485
+ )
+
+ Seq(Literal.FalseLiteral, Literal.TrueLiteral).foreach {
+ roundOff =>
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-01-30
11:52:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-01-30
11:50:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ 0.0
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-01-31
00:00:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-03-31
22:00:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ -2.0
+ )
+ checkEvaluation(
+ MonthsBetween(
+ Literal(new Timestamp(sdf.parse("2015-03-31
22:00:00").getTime)),
+ Literal(new Timestamp(sdf.parse("2015-02-28
00:00:00").getTime)),
+ roundOff,
+ timeZoneId = timeZoneId
+ ),
+ 1.0
+ )
+ }
+ val t = Literal(Timestamp.valueOf("2015-03-31 22:00:00"))
+ val tnull = Literal.create(null, TimestampType)
+ checkEvaluation(MonthsBetween(t, tnull, Literal.TrueLiteral,
timeZoneId = timeZoneId), null)
+ checkEvaluation(MonthsBetween(tnull, t, Literal.TrueLiteral,
timeZoneId = timeZoneId), null)
+ checkEvaluation(
+ MonthsBetween(tnull, tnull, Literal.TrueLiteral, timeZoneId =
timeZoneId),
+ null)
+ checkEvaluation(
+ MonthsBetween(t, t, Literal.create(null, BooleanType), timeZoneId =
timeZoneId),
+ null)
+ checkConsistencyBetweenInterpretedAndCodegen(
+ (time1: Expression, time2: Expression, roundOff: Expression) =>
+ MonthsBetween(time1, time2, roundOff, timeZoneId = timeZoneId),
+ TimestampType,
+ TimestampType,
+ BooleanType
+ )
+ }
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]