This is an automated email from the ASF dual-hosted git repository.
morrysnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 3a34ec95af [FE](fucntion) add date_floor/ceil in FE function (#23539)
3a34ec95af is described below
commit 3a34ec95af4001aeca5a237feb67d6193be27272
Author: zhangstar333 <[email protected]>
AuthorDate: Thu Aug 31 19:26:47 2023 +0800
[FE](fucntion) add date_floor/ceil in FE function (#23539)
---
.../sql-functions/date-time-functions/date_ceil.md | 96 +++++++++++
.../date-time-functions/date_floor.md | 104 ++++++++++++
.../sql-functions/date-time-functions/date_ceil.md | 96 +++++++++++
.../date-time-functions/date_floor.md | 104 ++++++++++++
.../antlr4/org/apache/doris/nereids/DorisLexer.g4 | 4 +-
.../antlr4/org/apache/doris/nereids/DorisParser.g4 | 14 ++
fe/fe-core/src/main/cup/sql_parser.cup | 10 +-
.../org/apache/doris/analysis/DateLiteral.java | 16 +-
.../apache/doris/analysis/FunctionCallExpr.java | 13 ++
.../doris/nereids/parser/LogicalPlanBuilder.java | 74 +++++++++
.../trees/expressions/ExpressionEvaluator.java | 4 +-
.../functions/executable/TimeRoundSeries.java | 106 +++++++-----
.../trees/expressions/literal/DateLiteral.java | 34 +++-
.../trees/expressions/literal/DateTimeLiteral.java | 10 +-
.../expressions/literal/DateTimeV2Literal.java | 12 +-
.../trees/expressions/literal/DateV2Literal.java | 6 +-
.../java/org/apache/doris/rewrite/FEFunctions.java | 184 ++++++++++++++++++++-
gensrc/script/doris_builtins_functions.py | 4 +-
.../datetime_functions/test_date_floor_ceil.out | 37 +++++
.../datetime_functions/test_date_floor_ceil.groovy | 36 ++++
20 files changed, 892 insertions(+), 72 deletions(-)
diff --git
a/docs/en/docs/sql-manual/sql-functions/date-time-functions/date_ceil.md
b/docs/en/docs/sql-manual/sql-functions/date-time-functions/date_ceil.md
new file mode 100644
index 0000000000..d2a988295b
--- /dev/null
+++ b/docs/en/docs/sql-manual/sql-functions/date-time-functions/date_ceil.md
@@ -0,0 +1,96 @@
+---
+{
+ "title": "date_ceil",
+ "language": "en"
+}
+---
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+## date_ceil
+### description
+#### Syntax
+
+`DATETIME DATE_CEIL(DATETIME datetime, INTERVAL period type)`
+
+
+Convert the date to the nearest rounding up time of the specified time
interval period.
+
+The datetime parameter is a valid date expression.
+
+The period parameter specifies how many units each cycle consists of, starting
from 0001-01-01T00:00:00
+
+type :YEAR, MONTH, DAY, HOUR, MINUTE, SECOND.
+
+### example
+
+```
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 second);
++--------------------------------------------------------------+
+| second_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++--------------------------------------------------------------+
+| 2023-07-13 22:28:20 |
++--------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 minute);
++--------------------------------------------------------------+
+| minute_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++--------------------------------------------------------------+
+| 2023-07-13 22:30:00 |
++--------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 hour);
++------------------------------------------------------------+
+| hour_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++------------------------------------------------------------+
+| 2023-07-13 23:00:00 |
++------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 day);
++-----------------------------------------------------------+
+| day_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++-----------------------------------------------------------+
+| 2023-07-15 00:00:00 |
++-----------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 month);
++-------------------------------------------------------------+
+| month_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++-------------------------------------------------------------+
+| 2023-12-01 00:00:00 |
++-------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 year);
++------------------------------------------------------------+
+| year_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++------------------------------------------------------------+
+| 2026-01-01 00:00:00 |
++------------------------------------------------------------+
+1 row in set (0.00 sec)
+```
+
+### keywords
+
+ DATE_CEIL,DATE,CEIL
diff --git
a/docs/en/docs/sql-manual/sql-functions/date-time-functions/date_floor.md
b/docs/en/docs/sql-manual/sql-functions/date-time-functions/date_floor.md
new file mode 100644
index 0000000000..cc2ac666d9
--- /dev/null
+++ b/docs/en/docs/sql-manual/sql-functions/date-time-functions/date_floor.md
@@ -0,0 +1,104 @@
+---
+{
+ "title": "date_floor",
+ "language": "en"
+}
+---
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+## date_floor
+### description
+#### Syntax
+
+`DATETIME DATE_FLOOR(DATETIME datetime, INTERVAL period type)`
+
+
+Converts a date to the nearest rounding down time of a specified time interval
period.
+
+The datetime parameter is a valid date expression.
+
+The period parameter specifies how many units each cycle consists of, starting
from 0001-01-01T00:00:00
+
+type :YEAR, MONTH, DAY, HOUR, MINUTE, SECOND.
+
+### example
+
+```
+mysql>select date_floor("0001-01-01 00:00:16",interval 5 second);
++---------------------------------------------------------------+
+| second_floor('0001-01-01 00:00:16', 5, '0001-01-01 00:00:00') |
++---------------------------------------------------------------+
+| 0001-01-01 00:00:15 |
++---------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql>select date_floor("0001-01-01 00:00:18",interval 5 second);
++---------------------------------------------------------------+
+| second_floor('0001-01-01 00:00:18', 5, '0001-01-01 00:00:00') |
++---------------------------------------------------------------+
+| 0001-01-01 00:00:15 |
++---------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 minute);
++---------------------------------------------------------------+
+| minute_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++---------------------------------------------------------------+
+| 2023-07-13 22:25:00 |
++---------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 hour);
++-------------------------------------------------------------+
+| hour_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++-------------------------------------------------------------+
+| 2023-07-13 18:00:00 |
++-------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 day);
++------------------------------------------------------------+
+| day_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++------------------------------------------------------------+
+| 2023-07-10 00:00:00 |
++------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 month);
++--------------------------------------------------------------+
+| month_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++--------------------------------------------------------------+
+| 2023-07-01 00:00:00 |
++--------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 year);
++-------------------------------------------------------------+
+| year_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++-------------------------------------------------------------+
+| 2021-01-01 00:00:00 |
++-------------------------------------------------------------+
+
+```
+
+### keywords
+
+ DATE_FLOOR,DATE,FLOOR
diff --git
a/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/date_ceil.md
b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/date_ceil.md
new file mode 100644
index 0000000000..eefe578262
--- /dev/null
+++ b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/date_ceil.md
@@ -0,0 +1,96 @@
+---
+{
+ "title": "date_ceil",
+ "language": "zh-CN"
+}
+---
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+## date_ceil
+### description
+#### Syntax
+
+`DATETIME DATE_CEIL(DATETIME datetime, INTERVAL period type)`
+
+
+将日期转化为指定的时间间隔周期的最近上取整时刻。
+
+datetime 参数是合法的日期表达式。
+
+period 参数是指定每个周期有多少个单位组成,开始的时间起点为0001-01-01T00:00:00.
+
+type 参数可以是下列值:YEAR, MONTH, DAY, HOUR, MINUTE, SECOND.
+
+### example
+
+```
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 second);
++--------------------------------------------------------------+
+| second_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++--------------------------------------------------------------+
+| 2023-07-13 22:28:20 |
++--------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 minute);
++--------------------------------------------------------------+
+| minute_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++--------------------------------------------------------------+
+| 2023-07-13 22:30:00 |
++--------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 hour);
++------------------------------------------------------------+
+| hour_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++------------------------------------------------------------+
+| 2023-07-13 23:00:00 |
++------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 day);
++-----------------------------------------------------------+
+| day_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++-----------------------------------------------------------+
+| 2023-07-15 00:00:00 |
++-----------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 month);
++-------------------------------------------------------------+
+| month_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++-------------------------------------------------------------+
+| 2023-12-01 00:00:00 |
++-------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql [(none)]>select date_ceil("2023-07-13 22:28:18",interval 5 year);
++------------------------------------------------------------+
+| year_ceil('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++------------------------------------------------------------+
+| 2026-01-01 00:00:00 |
++------------------------------------------------------------+
+1 row in set (0.00 sec)
+```
+
+### keywords
+
+ DATE_CEIL,DATE,CEIL
diff --git
a/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/date_floor.md
b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/date_floor.md
new file mode 100644
index 0000000000..53ef6003fd
--- /dev/null
+++ b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/date_floor.md
@@ -0,0 +1,104 @@
+---
+{
+ "title": "date_floor",
+ "language": "zh-CN"
+}
+---
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+## date_floor
+### description
+#### Syntax
+
+`DATETIME DATE_FLOOR(DATETIME datetime, INTERVAL period type)`
+
+
+将日期转化为指定的时间间隔周期的最近下取整时刻。
+
+datetime 参数是合法的日期表达式。
+
+period 参数是指定每个周期有多少个单位组成,开始的时间起点为0001-01-01T00:00:00.
+
+type 参数可以是下列值:YEAR, MONTH, DAY, HOUR, MINUTE, SECOND.
+
+### example
+
+```
+mysql>select date_floor("0001-01-01 00:00:16",interval 5 second);
++---------------------------------------------------------------+
+| second_floor('0001-01-01 00:00:16', 5, '0001-01-01 00:00:00') |
++---------------------------------------------------------------+
+| 0001-01-01 00:00:15 |
++---------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql>select date_floor("0001-01-01 00:00:18",interval 5 second);
++---------------------------------------------------------------+
+| second_floor('0001-01-01 00:00:18', 5, '0001-01-01 00:00:00') |
++---------------------------------------------------------------+
+| 0001-01-01 00:00:15 |
++---------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 minute);
++---------------------------------------------------------------+
+| minute_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++---------------------------------------------------------------+
+| 2023-07-13 22:25:00 |
++---------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 hour);
++-------------------------------------------------------------+
+| hour_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++-------------------------------------------------------------+
+| 2023-07-13 18:00:00 |
++-------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 day);
++------------------------------------------------------------+
+| day_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++------------------------------------------------------------+
+| 2023-07-10 00:00:00 |
++------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 month);
++--------------------------------------------------------------+
+| month_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++--------------------------------------------------------------+
+| 2023-07-01 00:00:00 |
++--------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql>select date_floor("2023-07-13 22:28:18",interval 5 year);
++-------------------------------------------------------------+
+| year_floor('2023-07-13 22:28:18', 5, '0001-01-01 00:00:00') |
++-------------------------------------------------------------+
+| 2021-01-01 00:00:00 |
++-------------------------------------------------------------+
+
+```
+
+### keywords
+
+ DATE_FLOOR,DATE,FLOOR
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
index b6f1ae30e5..532f05dec2 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
@@ -160,8 +160,10 @@ DATE: 'DATE';
DATEADD: 'DATEADD';
DATEDIFF: 'DATEDIFF';
DATE_ADD: 'DATE_ADD';
-DATE_SUB: 'DATE_SUB';
+DATE_CEIL: 'DATE_CEIL';
DATE_DIFF: 'DATE_DIFF';
+DATE_FLOOR: 'DATE_FLOOR';
+DATE_SUB: 'DATE_SUB';
DBPROPERTIES: 'DBPROPERTIES';
DEFAULT: 'DEFAULT';
DEFINED: 'DEFINED';
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index ee8757f8e3..c982b133ed 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -395,6 +395,18 @@ primaryExpression
(INTERVAL unitsAmount=valueExpression unit=datetimeUnit
| unitsAmount=valueExpression)
RIGHT_PAREN
#date_sub
+ | name=DATE_FLOOR
+ LEFT_PAREN
+ timestamp=valueExpression COMMA
+ (INTERVAL unitsAmount=valueExpression unit=datetimeUnit
+ | unitsAmount=valueExpression)
+ RIGHT_PAREN
#dateFloor
+ | name=DATE_CEIL
+ LEFT_PAREN
+ timestamp=valueExpression COMMA
+ (INTERVAL unitsAmount=valueExpression unit=datetimeUnit
+ | unitsAmount=valueExpression)
+ RIGHT_PAREN
#dateCeil
| CASE whenClause+ (ELSE elseExpression=expression)? END
#searchedCase
| CASE value=expression whenClause+ (ELSE elseExpression=expression)? END
#simpleCase
| name=CAST LEFT_PAREN expression AS dataType RIGHT_PAREN
#cast
@@ -624,8 +636,10 @@ nonReserved
| DATE
| DATEV2
| DATE_ADD
+ | DATE_CEIL
| DATEDIFF
| DATE_DIFF
+ | DATE_FLOOR
| DAY
| DBPROPERTIES
| DEFINED
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup
b/fe/fe-core/src/main/cup/sql_parser.cup
index 5f3311ab0f..6f37773c7e 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -6873,8 +6873,14 @@ timestamp_arithmetic_expr ::=
// This function should not fully qualified
throw new Exception("interval should not be qualified by database name");
}
-
- RESULT = new TimestampArithmeticExpr(functionName.getFunction(), l.get(0),
v, u);
+ //eg: date_floor("0001-01-01 00:00:18",interval 5 second) convert to
+ //second_floor("0001-01-01 00:00:18", 5, "0001-01-01 00:00:00");
+ if ("date_floor".equalsIgnoreCase(functionName.getFunction()) ||
+ "date_ceil".equalsIgnoreCase(functionName.getFunction())) {
+ RESULT =
FunctionCallExpr.functionWithIntervalConvert(functionName.getFunction().toLowerCase(),
l.get(0), v, u);
+ } else {
+ RESULT = new TimestampArithmeticExpr(functionName.getFunction(),
l.get(0), v, u);
+ }
:}
| function_name:functionName LPAREN time_unit:u COMMA expr:e1 COMMA expr:e2
RPAREN
{:
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
index 11d17ec816..8a2f693a55 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
@@ -1046,19 +1046,19 @@ public class DateLiteral extends LiteralExpr {
return LocalDateTime.of(year, month, dayOfMonth, hour, minute, second,
microSeconds * 1000);
}
- public DateLiteral plusYears(int year) throws AnalysisException {
+ public DateLiteral plusYears(long year) throws AnalysisException {
return new DateLiteral(getTimeFormatter().plusYears(year), type);
}
- public DateLiteral plusMonths(int month) throws AnalysisException {
+ public DateLiteral plusMonths(long month) throws AnalysisException {
return new DateLiteral(getTimeFormatter().plusMonths(month), type);
}
- public DateLiteral plusDays(int day) throws AnalysisException {
+ public DateLiteral plusDays(long day) throws AnalysisException {
return new DateLiteral(getTimeFormatter().plusDays(day), type);
}
- public DateLiteral plusHours(int hour) throws AnalysisException {
+ public DateLiteral plusHours(long hour) throws AnalysisException {
if (type.isDate()) {
return new DateLiteral(getTimeFormatter().plusHours(hour),
Type.DATETIME);
}
@@ -1068,7 +1068,7 @@ public class DateLiteral extends LiteralExpr {
return new DateLiteral(getTimeFormatter().plusHours(hour), type);
}
- public DateLiteral plusMinutes(int minute) {
+ public DateLiteral plusMinutes(long minute) {
if (type.isDate()) {
return new DateLiteral(getTimeFormatter().plusMinutes(minute),
Type.DATETIME);
}
@@ -1078,7 +1078,7 @@ public class DateLiteral extends LiteralExpr {
return new DateLiteral(getTimeFormatter().plusMinutes(minute), type);
}
- public DateLiteral plusSeconds(int second) {
+ public DateLiteral plusSeconds(long second) {
if (type.isDate()) {
return new DateLiteral(getTimeFormatter().plusSeconds(second),
Type.DATETIME);
}
@@ -1536,6 +1536,10 @@ public class DateLiteral extends LiteralExpr {
}
}
+ public long daynr() {
+ return calcDaynr(this.year, this.month, this.day);
+ }
+
// calculate the number of days from year 0000-00-00 to year-month-day
private long calcDaynr(long year, long month, long day) {
long delsum = 0;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
index c4c7f967d9..c9af3ed874 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
@@ -2267,4 +2267,17 @@ public class FunctionCallExpr extends Expr {
}
return fn;
}
+
+ // eg: date_floor("0001-01-01 00:00:18",interval 5 second) convert to
+ // second_floor("0001-01-01 00:00:18", 5, "0001-01-01 00:00:00");
+ public static FunctionCallExpr functionWithIntervalConvert(String
functionName, Expr str, Expr interval,
+ String timeUnitIdent) throws AnalysisException {
+ String newFunctionName = timeUnitIdent + "_" +
functionName.split("_")[1];
+ List<Expr> params = new ArrayList<>();
+ Expr defaultDatetime = new DateLiteral(0001, 01, 01, 0, 0, 0, 0,
Type.DATETIMEV2);
+ params.add(str);
+ params.add(interval);
+ params.add(defaultDatetime);
+ return new FunctionCallExpr(newFunctionName, params);
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index bf966f574f..b4c2cf5a5c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -48,6 +48,8 @@ import
org.apache.doris.nereids.DorisParser.ComplexDataTypeContext;
import org.apache.doris.nereids.DorisParser.ConstantContext;
import org.apache.doris.nereids.DorisParser.CreateRowPolicyContext;
import org.apache.doris.nereids.DorisParser.CteContext;
+import org.apache.doris.nereids.DorisParser.DateCeilContext;
+import org.apache.doris.nereids.DorisParser.DateFloorContext;
import org.apache.doris.nereids.DorisParser.Date_addContext;
import org.apache.doris.nereids.DorisParser.Date_subContext;
import org.apache.doris.nereids.DorisParser.DecimalLiteralContext;
@@ -198,26 +200,40 @@ import
org.apache.doris.nereids.trees.expressions.functions.scalar.Char;
import org.apache.doris.nereids.trees.expressions.functions.scalar.ConvertTo;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateMap;
import
org.apache.doris.nereids.trees.expressions.functions.scalar.CreateStruct;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DayCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DayFloor;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysSub;
import org.apache.doris.nereids.trees.expressions.functions.scalar.ElementAt;
import
org.apache.doris.nereids.trees.expressions.functions.scalar.EncryptKeyRef;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HourCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HourFloor;
import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteFloor;
import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthFloor;
import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondFloor;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekFloor;
import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearFloor;
import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsSub;
@@ -1095,6 +1111,64 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
+ ", supported time unit: YEAR/MONTH/DAY/HOUR/MINUTE/SECOND",
ctx);
}
+ @Override
+ public Expression visitDateFloor(DateFloorContext ctx) {
+ Expression timeStamp = (Expression) visit(ctx.timestamp);
+ Expression amount = (Expression) visit(ctx.unitsAmount);
+ if (ctx.unit == null) {
+ // use "SECOND" as unit by default
+ return new SecondFloor(timeStamp, amount);
+ }
+ Expression e = new DateTimeV2Literal(0001L, 01L, 01L, 0L, 0L, 0L, 0L);
+
+ if ("Year".equalsIgnoreCase(ctx.unit.getText())) {
+ return new YearFloor(timeStamp, amount, e);
+ } else if ("MONTH".equalsIgnoreCase(ctx.unit.getText())) {
+ return new MonthFloor(timeStamp, amount, e);
+ } else if ("WEEK".equalsIgnoreCase(ctx.unit.getText())) {
+ return new WeekFloor(timeStamp, amount, e);
+ } else if ("DAY".equalsIgnoreCase(ctx.unit.getText())) {
+ return new DayFloor(timeStamp, amount, e);
+ } else if ("Hour".equalsIgnoreCase(ctx.unit.getText())) {
+ return new HourFloor(timeStamp, amount, e);
+ } else if ("Minute".equalsIgnoreCase(ctx.unit.getText())) {
+ return new MinuteFloor(timeStamp, amount, e);
+ } else if ("Second".equalsIgnoreCase(ctx.unit.getText())) {
+ return new SecondFloor(timeStamp, amount, e);
+ }
+ throw new ParseException("Unsupported time unit: " + ctx.unit
+ + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND", ctx);
+ }
+
+ @Override
+ public Expression visitDateCeil(DateCeilContext ctx) {
+ Expression timeStamp = (Expression) visit(ctx.timestamp);
+ Expression amount = (Expression) visit(ctx.unitsAmount);
+ if (ctx.unit == null) {
+ // use "Second" as unit by default
+ return new SecondCeil(timeStamp, amount);
+ }
+ DateTimeV2Literal e = new DateTimeV2Literal(0001L, 01L, 01L, 0L, 0L,
0L, 0L);
+
+ if ("Year".equalsIgnoreCase(ctx.unit.getText())) {
+ return new YearCeil(timeStamp, amount, e);
+ } else if ("MONTH".equalsIgnoreCase(ctx.unit.getText())) {
+ return new MonthCeil(timeStamp, amount, e);
+ } else if ("WEEK".equalsIgnoreCase(ctx.unit.getText())) {
+ return new WeekCeil(timeStamp, amount, e);
+ } else if ("DAY".equalsIgnoreCase(ctx.unit.getText())) {
+ return new DayCeil(timeStamp, amount, e);
+ } else if ("Hour".equalsIgnoreCase(ctx.unit.getText())) {
+ return new HourCeil(timeStamp, amount, e);
+ } else if ("Minute".equalsIgnoreCase(ctx.unit.getText())) {
+ return new MinuteCeil(timeStamp, amount, e);
+ } else if ("Second".equalsIgnoreCase(ctx.unit.getText())) {
+ return new SecondCeil(timeStamp, amount, e);
+ }
+ throw new ParseException("Unsupported time unit: " + ctx.unit
+ + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND", ctx);
+ }
+
@Override
public Expression visitDoublePipes(DorisParser.DoublePipesContext ctx) {
return ParserUtils.withOrigin(ctx, () -> {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionEvaluator.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionEvaluator.java
index 2964a4eeb3..a3b5fda56c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionEvaluator.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionEvaluator.java
@@ -27,6 +27,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.executable.DateTimeA
import
org.apache.doris.nereids.trees.expressions.functions.executable.DateTimeExtractAndTransform;
import
org.apache.doris.nereids.trees.expressions.functions.executable.ExecutableFunctions;
import
org.apache.doris.nereids.trees.expressions.functions.executable.NumericArithmetic;
+import
org.apache.doris.nereids.trees.expressions.functions.executable.TimeRoundSeries;
import org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
@@ -141,7 +142,8 @@ public enum ExpressionEvaluator {
ExecutableFunctions.class,
DateLiteral.class,
DateTimeArithmetic.class,
- NumericArithmetic.class
+ NumericArithmetic.class,
+ TimeRoundSeries.class
);
for (Class<?> cls : classes) {
for (Method method : cls.getDeclaredMethods()) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/TimeRoundSeries.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/TimeRoundSeries.java
index 77fd375023..cde59d8568 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/TimeRoundSeries.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/TimeRoundSeries.java
@@ -25,10 +25,7 @@ import
org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
-import java.time.Duration;
import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
-import java.time.temporal.TemporalUnit;
/**
* executable functions:
@@ -48,49 +45,82 @@ public class TimeRoundSeries {
SECOND
}
- private static ChronoUnit dateEnumToUnit(DATE tag) {
+ // get it's from
be/src/vec/functions/function_datetime_floor_ceil.cpp##time_round
+ private static LocalDateTime getDateCeilOrFloor(DATE tag, LocalDateTime
date, int period, LocalDateTime origin,
+ boolean getCeil) {
+ DateTimeV2Literal dt = (DateTimeV2Literal)
DateTimeV2Literal.fromJavaDateType(date);
+ DateTimeV2Literal start = (DateTimeV2Literal)
DateTimeV2Literal.fromJavaDateType(origin);
+ long diff = 0;
+ long trivialPart = 0;
+ switch (tag) {
+ case YEAR: {
+ diff = dt.getYear() - start.getYear();
+ trivialPart = (dt.getValue() % 10000000000L) -
(start.getValue() % 10000000000L);
+ break;
+ }
+ case MONTH: {
+ diff = (dt.getYear() - start.getYear()) * 12 + (dt.getMonth()
- start.getMonth());
+ trivialPart = (dt.getValue() % 100000000L) - (start.getValue()
% 100000000L);
+ break;
+ }
+ case DAY: {
+ diff = dt.getTotalDays() - start.getTotalDays();
+ long part2 = dt.getHour() * 3600 + dt.getMinute() * 60 +
dt.getSecond();
+ long part1 = start.getHour() * 3600 + start.getMinute() * 60 +
start.getSecond();
+ trivialPart = part2 - part1;
+ break;
+ }
+ case HOUR: {
+ diff = (dt.getTotalDays() - start.getTotalDays()) * 24 +
(dt.getHour() - start.getHour());
+ trivialPart = (dt.getMinute() * 60 + dt.getSecond())
+ - (start.getMinute() * 60 + start.getSecond());
+ break;
+ }
+ case MINUTE: {
+ diff = (dt.getTotalDays() - start.getTotalDays()) * 24 * 60 +
(dt.getHour() - start.getHour()) * 60
+ + (dt.getMinute() - start.getMinute());
+ trivialPart = dt.getSecond() - start.getSecond();
+ break;
+ }
+ case SECOND: {
+ diff = (dt.getTotalDays() - start.getTotalDays()) * 24 * 60 *
60
+ + (dt.getHour() - start.getHour()) * 60 * 60
+ + (dt.getMinute() - start.getMinute()) * 60
+ + (dt.getSecond() - start.getSecond());
+ trivialPart = 0;
+ break;
+ }
+ default: {
+ return null;
+ }
+ }
+ if (getCeil) {
+ diff = diff + (trivialPart > 0 ? 1 : 0);
+ } else {
+ diff = diff - (trivialPart < 0 ? 1 : 0);
+ }
+ long deltaInsidePeriod = (diff % period + period) % period;
+ long step = diff - deltaInsidePeriod;
+ if (getCeil) {
+ step = step + (deltaInsidePeriod == 0 ? 0 : period);
+ }
switch (tag) {
case YEAR:
- return ChronoUnit.YEARS;
+ return ((DateTimeLiteral)
start.plusYears(step)).toJavaDateType();
case MONTH:
- return ChronoUnit.MONTHS;
+ return ((DateTimeLiteral)
start.plusMonths(step)).toJavaDateType();
case DAY:
- return ChronoUnit.DAYS;
+ return ((DateTimeLiteral)
start.plusDays(step)).toJavaDateType();
case HOUR:
- return ChronoUnit.HOURS;
+ return ((DateTimeLiteral)
start.plusHours(step)).toJavaDateType();
case MINUTE:
- return ChronoUnit.MINUTES;
+ return ((DateTimeLiteral)
start.plusMinutes(step)).toJavaDateType();
+ case SECOND:
+ return ((DateTimeLiteral)
start.plusSeconds(step)).toJavaDateType();
default:
- return ChronoUnit.SECONDS;
+ break;
}
- }
-
- private static LocalDateTime getDateCeilOrFloor(DATE tag, LocalDateTime
date, int period, LocalDateTime origin,
- boolean getCeil) {
- // Algorithm:
- // Firstly, get the unit distance of the two date.
- // Secondly, if the origin date is bigger than the date, subtract it
to a date before the date by unit.
- // Thirdly, re-calculate the distance of the two date.
- // Fourthly, get the ceil and floor date of the date by unit and
select the corresponding date as the answer.
-
- // handle origin > date
- TemporalUnit unit = dateEnumToUnit(tag);
- if (origin.isAfter(date)) {
- Duration duration = Duration.between(date, origin);
- long hour = Math.abs(duration.get(unit));
- long ceil = ((hour - 1) / period + 1) * period;
- origin = origin.minus(ceil, unit);
- }
-
- // get distance
- Duration duration = Duration.between(origin, date);
- long hour = Math.abs(duration.get(unit));
- long ceil = ((hour - 1) / period + 1) * period;
- long floor = hour / period * period;
- LocalDateTime floorDate = origin.plus(floor, unit);
- LocalDateTime ceilDate = origin.plus(ceil, unit);
-
- return getCeil ? ceilDate : floorDate;
+ return null;
}
/**
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
index f75e2d81da..dfb7177ebc 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
@@ -206,15 +206,15 @@ public class DateLiteral extends Literal {
return day;
}
- public Expression plusDays(int days) {
+ public Expression plusDays(long days) {
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusDays(days));
}
- public Expression plusMonths(int months) {
+ public Expression plusMonths(long months) {
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusMonths(months));
}
- public Expression plusYears(int years) {
+ public Expression plusYears(long years) {
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusYears(years));
}
@@ -222,6 +222,34 @@ public class DateLiteral extends Literal {
return LocalDateTime.of(((int) getYear()), ((int) getMonth()), ((int)
getDay()), 0, 0, 0);
}
+ public long getTotalDays() {
+ return calculateDays(this.year, this.month, this.day);
+ }
+
+ // calculate the number of days from year 0000-00-00 to year-month-day
+ private long calculateDays(long year, long month, long day) {
+ long totalDays = 0;
+ long y = year;
+
+ if (year == 0 && month == 0) {
+ return 0;
+ }
+
+ /* Cast to int to be able to handle month == 0 */
+ totalDays = 365 * y + 31 * (month - 1) + day;
+ if (month <= 2) {
+ // No leap year
+ y--;
+ } else {
+ // This is great!!!
+ // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
+ // 0, 0, 3, 3, 4, 4, 5, 5, 5, 6, 7, 8
+ totalDays -= (month * 4 + 23) / 10;
+ }
+ // Every 400 year has 97 leap year, 100, 200, 300 are not leap year.
+ return totalDays + y / 4 - y / 100 + y / 400;
+ }
+
public static Expression fromJavaDateType(LocalDateTime dateTime) {
return isDateOutOfRange(dateTime)
? new NullLiteral(DateType.INSTANCE)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
index 2db3dacb49..bf41c967cc 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
@@ -316,23 +316,23 @@ public class DateTimeLiteral extends DateLiteral {
return new org.apache.doris.analysis.DateLiteral(year, month, day,
hour, minute, second, Type.DATETIME);
}
- public Expression plusYears(int years) {
+ public Expression plusYears(long years) {
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusYears(years));
}
- public Expression plusMonths(int months) {
+ public Expression plusMonths(long months) {
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusMonths(months));
}
- public Expression plusDays(int days) {
+ public Expression plusDays(long days) {
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusDays(days));
}
- public Expression plusHours(int hours) {
+ public Expression plusHours(long hours) {
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusHours(hours));
}
- public Expression plusMinutes(int minutes) {
+ public Expression plusMinutes(long minutes) {
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusMinutes(minutes));
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
index ef09399a5a..c65c921ac7 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
@@ -82,31 +82,31 @@ public class DateTimeV2Literal extends DateTimeLiteral {
}
@Override
- public Expression plusYears(int years) {
+ public Expression plusYears(long years) {
return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
.plusYears(years), getDataType().getScale());
}
@Override
- public Expression plusMonths(int months) {
+ public Expression plusMonths(long months) {
return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
.plusMonths(months), getDataType().getScale());
}
@Override
- public Expression plusDays(int days) {
+ public Expression plusDays(long days) {
return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
.plusDays(days), getDataType().getScale());
}
@Override
- public Expression plusHours(int hours) {
+ public Expression plusHours(long hours) {
return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
.plusHours(hours), getDataType().getScale());
}
@Override
- public Expression plusMinutes(int minutes) {
+ public Expression plusMinutes(long minutes) {
return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
.plusMinutes(minutes), getDataType().getScale());
}
@@ -117,7 +117,7 @@ public class DateTimeV2Literal extends DateTimeLiteral {
.plusSeconds(seconds), getDataType().getScale());
}
- public Expression plusMicroSeconds(int microSeconds) {
+ public Expression plusMicroSeconds(long microSeconds) {
return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
.plusNanos(microSeconds * 1000L), getDataType().getScale());
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateV2Literal.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateV2Literal.java
index 928b7ca0f2..32164c977c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateV2Literal.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateV2Literal.java
@@ -50,15 +50,15 @@ public class DateV2Literal extends DateLiteral {
return visitor.visitDateV2Literal(this, context);
}
- public Expression plusDays(int days) {
+ public Expression plusDays(long days) {
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusDays(days));
}
- public Expression plusMonths(int months) {
+ public Expression plusMonths(long months) {
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusMonths(months));
}
- public Expression plusYears(int years) {
+ public Expression plusYears(long years) {
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusYears(years));
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/rewrite/FEFunctions.java
b/fe/fe-core/src/main/java/org/apache/doris/rewrite/FEFunctions.java
index bce2dd7dfa..dd1bce88cc 100755
--- a/fe/fe-core/src/main/java/org/apache/doris/rewrite/FEFunctions.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/rewrite/FEFunctions.java
@@ -26,6 +26,7 @@ import org.apache.doris.analysis.LargeIntLiteral;
import org.apache.doris.analysis.LiteralExpr;
import org.apache.doris.analysis.NullLiteral;
import org.apache.doris.analysis.StringLiteral;
+import org.apache.doris.analysis.TimestampArithmeticExpr.TimeUnit;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.InvalidFormatException;
@@ -111,37 +112,37 @@ public class FEFunctions {
@FEFunction(name = "years_add", argTypes = { "DATETIME", "INT" },
returnType = "DATETIME")
public static DateLiteral yearsAdd(LiteralExpr date, LiteralExpr year)
throws AnalysisException {
DateLiteral dateLiteral = (DateLiteral) date;
- return dateLiteral.plusYears((int) year.getLongValue());
+ return dateLiteral.plusYears(year.getLongValue());
}
@FEFunction(name = "months_add", argTypes = { "DATETIME", "INT" },
returnType = "DATETIME")
public static DateLiteral monthsAdd(LiteralExpr date, LiteralExpr month)
throws AnalysisException {
DateLiteral dateLiteral = (DateLiteral) date;
- return dateLiteral.plusMonths((int) month.getLongValue());
+ return dateLiteral.plusMonths(month.getLongValue());
}
@FEFunction(name = "days_add", argTypes = { "DATETIME", "INT" },
returnType = "DATETIME")
public static DateLiteral daysAdd(LiteralExpr date, LiteralExpr day)
throws AnalysisException {
DateLiteral dateLiteral = (DateLiteral) date;
- return dateLiteral.plusDays((int) day.getLongValue());
+ return dateLiteral.plusDays(day.getLongValue());
}
@FEFunction(name = "hours_add", argTypes = { "DATETIME", "INT" },
returnType = "DATETIME")
public static DateLiteral hoursAdd(LiteralExpr date, LiteralExpr hour)
throws AnalysisException {
DateLiteral dateLiteral = (DateLiteral) date;
- return dateLiteral.plusHours((int) hour.getLongValue());
+ return dateLiteral.plusHours(hour.getLongValue());
}
@FEFunction(name = "minutes_add", argTypes = { "DATETIME", "INT" },
returnType = "DATETIME")
public static DateLiteral minutesAdd(LiteralExpr date, LiteralExpr minute)
throws AnalysisException {
DateLiteral dateLiteral = (DateLiteral) date;
- return dateLiteral.plusMinutes((int) minute.getLongValue());
+ return dateLiteral.plusMinutes(minute.getLongValue());
}
@FEFunction(name = "seconds_add", argTypes = { "DATETIME", "INT" },
returnType = "DATETIME")
public static DateLiteral secondsAdd(LiteralExpr date, LiteralExpr
second) throws AnalysisException {
DateLiteral dateLiteral = (DateLiteral) date;
- return dateLiteral.plusSeconds((int) second.getLongValue());
+ return dateLiteral.plusSeconds(second.getLongValue());
}
@FEFunction(name = "date_format", argTypes = { "DATETIME", "VARCHAR" },
returnType = "VARCHAR")
@@ -350,6 +351,177 @@ public class FEFunctions {
return null;
}
+ @FEFunction(name = "second_floor", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral second_floor(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
false, TimeUnit.SECOND);
+ }
+
+ @FEFunction(name = "second_ceil", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral second_ceil(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
true, TimeUnit.SECOND);
+ }
+
+ @FEFunction(name = "minute_floor", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral minute_floor(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
false, TimeUnit.MINUTE);
+ }
+
+ @FEFunction(name = "minute_ceil", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral minute_ceil(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
true, TimeUnit.MINUTE);
+ }
+
+ @FEFunction(name = "hour_floor", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral hour_floor(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
false, TimeUnit.HOUR);
+ }
+
+ @FEFunction(name = "hour_ceil", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral hour_ceil(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
true, TimeUnit.HOUR);
+ }
+
+ @FEFunction(name = "day_floor", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral day_floor(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
false, TimeUnit.DAY);
+ }
+
+ @FEFunction(name = "day_ceil", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral day_ceil(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
true, TimeUnit.DAY);
+ }
+
+ // get it's from
be/src/vec/functions/function_datetime_floor_ceil.cpp##time_round
+ public static DateLiteral getFloorCeilDateLiteral(LiteralExpr datetime,
LiteralExpr period,
+ LiteralExpr defaultDatetime, boolean isCeil, TimeUnit type) throws
AnalysisException {
+ DateLiteral dt = ((DateLiteral) datetime);
+ DateLiteral start = ((DateLiteral) defaultDatetime);
+ long periodValue = ((IntLiteral) period).getValue();
+ long diff = 0;
+ long trivialPart = 0;
+
+ switch (type) {
+ case YEAR: {
+ diff = dt.getYear() - start.getYear();
+ trivialPart = (dt.getLongValue() % 10000000000L) -
(start.getLongValue() % 10000000000L);
+ break;
+ }
+ case MONTH: {
+ diff = (dt.getYear() - start.getYear()) * 12 + (dt.getMonth()
- start.getMonth());
+ trivialPart = (dt.getLongValue() % 100000000L) -
(start.getLongValue() % 100000000L);
+ break;
+ }
+ case WEEK: {
+ diff = (dt.daynr() / 7) - (start.daynr() / 7);
+ long part2 = (dt.daynr() % 7) * 24 * 3600 + dt.getHour() *
3600 + dt.getMinute() * 60 + dt.getSecond();
+ long part1 = (start.daynr() % 7) * 24 * 3600 + start.getHour()
* 3600 + start.getMinute() * 60
+ + start.getSecond();
+ trivialPart = part2 - part1;
+ break;
+ }
+ case DAY: {
+ diff = dt.daynr() - start.daynr();
+ long part2 = dt.getHour() * 3600 + dt.getMinute() * 60 +
dt.getSecond();
+ long part1 = start.getHour() * 3600 + start.getMinute() * 60 +
start.getSecond();
+ trivialPart = part2 - part1;
+ break;
+ }
+ case HOUR: {
+ diff = (dt.daynr() - start.daynr()) * 24 + (dt.getHour() -
start.getHour());
+ trivialPart = (dt.getMinute() * 60 + dt.getSecond()) -
(start.getMinute() * 60 + start.getSecond());
+ break;
+ }
+ case MINUTE: {
+ diff = (dt.daynr() - start.daynr()) * 24 * 60 + (dt.getHour()
- start.getHour()) * 60
+ + (dt.getMinute() - start.getMinute());
+ trivialPart = dt.getSecond() - start.getSecond();
+ break;
+ }
+ case SECOND: {
+ diff = (dt.daynr() - start.daynr()) * 24 * 60 * 60 +
(dt.getHour() - start.getHour()) * 60 * 60
+ + (dt.getMinute() - start.getMinute()) * 60 +
(dt.getSecond() - start.getSecond());
+ trivialPart = 0;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (isCeil) {
+ diff = diff + (trivialPart > 0 ? 1 : 0);
+ } else {
+ diff = diff - (trivialPart < 0 ? 1 : 0);
+ }
+ long deltaInsidePeriod = (diff % periodValue + periodValue) %
periodValue;
+ long step = diff - deltaInsidePeriod;
+ if (isCeil) {
+ step = step + (deltaInsidePeriod == 0 ? 0 : periodValue);
+ }
+ switch (type) {
+ case YEAR:
+ return start.plusYears(step);
+ case MONTH:
+ return start.plusMonths(step);
+ case WEEK:
+ return start.plusDays(step * 7);
+ case DAY:
+ return start.plusDays(step);
+ case HOUR:
+ return start.plusHours(step);
+ case MINUTE:
+ return start.plusMinutes(step);
+ case SECOND:
+ return start.plusSeconds(step);
+ default:
+ break;
+ }
+ return null;
+ }
+
+ @FEFunction(name = "week_floor", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral week_floor(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
false, TimeUnit.WEEK);
+ }
+
+ @FEFunction(name = "week_ceil", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral week_ceil(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
true, TimeUnit.WEEK);
+ }
+
+ @FEFunction(name = "month_floor", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral month_floor(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
false, TimeUnit.MONTH);
+ }
+
+ @FEFunction(name = "month_ceil", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral month_ceil(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
true, TimeUnit.MONTH);
+ }
+
+ @FEFunction(name = "year_floor", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral year_floor(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
false, TimeUnit.YEAR);
+ }
+
+ @FEFunction(name = "year_ceil", argTypes = { "DATETIMEV2", "INT",
"DATETIMEV2" }, returnType = "DATETIMEV2")
+ public static DateLiteral year_ceil(LiteralExpr datetime, LiteralExpr
period, LiteralExpr defaultDatetime)
+ throws AnalysisException {
+ return getFloorCeilDateLiteral(datetime, period, defaultDatetime,
true, TimeUnit.YEAR);
+ }
+
@FEFunction(name = "date_trunc", argTypes = {"DATETIME", "VARCHAR"},
returnType = "DATETIME")
public static DateLiteral dateTrunc(LiteralExpr date, LiteralExpr
truncate) {
if (date.getType().isDateLike()) {
diff --git a/gensrc/script/doris_builtins_functions.py
b/gensrc/script/doris_builtins_functions.py
index b4a50f6866..c0bec45f35 100644
--- a/gensrc/script/doris_builtins_functions.py
+++ b/gensrc/script/doris_builtins_functions.py
@@ -890,9 +890,11 @@ visible_functions = {
[['to_monday'], 'DATE', ['DATE'], 'ALWAYS_NULLABLE'],
[['to_days'], 'INT', ['DATE'], 'ALWAYS_NULLABLE'],
- [['date_trunc'], 'DATETIME', ['DATETIME', 'VARCHAR'],
'ALWAYS_NULLABLE'],
+ [['date_floor'], 'DATETIMEV2', ['DATETIMEV2', 'INT'],
'ALWAYS_NULLABLE'],
+ [['date_ceil'], 'DATETIMEV2', ['DATETIMEV2', 'INT'],
'ALWAYS_NULLABLE'],
[['date_trunc'], 'DATETIMEV2', ['DATETIMEV2', 'VARCHAR'],
'ALWAYS_NULLABLE'],
+ [['date_trunc'], 'DATETIME', ['DATETIME', 'VARCHAR'],
'ALWAYS_NULLABLE'],
[['year'], 'SMALLINT', ['DATETIME'], 'ALWAYS_NULLABLE'],
[['month'], 'TINYINT', ['DATETIME'], 'ALWAYS_NULLABLE'],
diff --git
a/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_floor_ceil.out
b/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_floor_ceil.out
new file mode 100644
index 0000000000..247a44b173
--- /dev/null
+++
b/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_floor_ceil.out
@@ -0,0 +1,37 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !sql1 --
+2023-07-14T10:51:10
+
+-- !sql2 --
+2023-07-14T10:50
+
+-- !sql3 --
+2023-07-14T09:00
+
+-- !sql4 --
+2023-07-10T00:00
+
+-- !sql5 --
+2023-07-01T00:00
+
+-- !sql6 --
+2021-01-01T00:00
+
+-- !sql7 --
+2023-07-14T10:51:15
+
+-- !sql8 --
+2023-07-14T10:55
+
+-- !sql9 --
+2023-07-14T14:00
+
+-- !sql10 --
+2023-07-15T00:00
+
+-- !sql11 --
+2023-12-01T00:00
+
+-- !sql12 --
+2026-01-01T00:00
+
diff --git
a/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_floor_ceil.groovy
b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_floor_ceil.groovy
new file mode 100644
index 0000000000..5d374c8fba
--- /dev/null
+++
b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_floor_ceil.groovy
@@ -0,0 +1,36 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_date_floor_ceil") {
+ sql "set enable_nereids_planner=true;"
+ sql "set enable_fallback_to_original_planner=false;"
+ sql "set enable_fold_constant_by_be=false;"
+
+ qt_sql1 """select date_floor("2023-07-14 10:51:11",interval 5 second); """
+ qt_sql2 """select date_floor("2023-07-14 10:51:00",interval 5 minute); """
+ qt_sql3 """select date_floor("2023-07-14 10:51:00",interval 5 hour); """
+ qt_sql4 """select date_floor("2023-07-14 10:51:00",interval 5 day); """
+ qt_sql5 """select date_floor("2023-07-14 10:51:00",interval 5 month); """
+ qt_sql6 """select date_floor("2023-07-14 10:51:00",interval 5 year); """
+
+ qt_sql7 """select date_ceil("2023-07-14 10:51:11",interval 5 second); """
+ qt_sql8 """select date_ceil("2023-07-14 10:51:00",interval 5 minute); """
+ qt_sql9 """select date_ceil("2023-07-14 10:51:00",interval 5 hour); """
+ qt_sql10 """select date_ceil("2023-07-14 10:51:00",interval 5 day); """
+ qt_sql11 """select date_ceil("2023-07-14 10:51:00",interval 5 month); """
+ qt_sql12 """select date_ceil("2023-07-14 10:51:00",interval 5 year); """
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]