This is an automated email from the ASF dual-hosted git repository.
kxiao pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.0 by this push:
new 2961a2f9176 [opt](Nereids): new Date/Datetime parser (#24523)
2961a2f9176 is described below
commit 2961a2f9176448e17a43bd9144f5f4d79712d58e
Author: jakevin <[email protected]>
AuthorDate: Wed Oct 11 23:01:08 2023 +0800
[opt](Nereids): new Date/Datetime parser (#24523)
cherry picked from commit bf808e9aa68c3f1761dfdcec8c9ffae8231ec0d5
---
.../doris/nereids/parser/LogicalPlanBuilder.java | 2 +-
.../expression/rules/FoldConstantRuleOnFE.java | 19 +-
.../doris/nereids/trees/expressions/Cast.java | 19 +-
.../functions/BuiltinFunctionBuilder.java | 2 +-
.../expressions/functions/scalar/ConvertTz.java | 16 +-
.../trees/expressions/literal/DateLiteral.java | 208 ++++++++--
.../trees/expressions/literal/DateTimeLiteral.java | 227 ++++-------
.../expressions/literal/DateTimeV2Literal.java | 85 ++--
.../trees/expressions/literal/DateV2Literal.java | 15 +-
.../apache/doris/nereids/types/DateTimeV2Type.java | 7 +-
.../doris/nereids/types/coercion/DateLikeType.java | 27 ++
.../doris/nereids/util/DateTimeFormatterUtils.java | 68 +++-
.../doris/nereids/util/StandardDateFormat.java | 53 +++
.../rules/SimplifyComparisonPredicateSqlTest.java | 159 ++++++++
.../rules/SimplifyComparisonPredicateTest.java | 16 +-
.../trees/expressions/LiteralEqualTest.java | 45 +++
.../trees/expressions/literal/DateLiteralTest.java | 134 +++++++
.../expressions/literal/DateTimeLiteralTest.java | 437 +++++++++++++++++++++
.../doris/nereids/types/AbstractDataTypeTest.java | 8 +-
.../nereids/util/DateTimeFormatterUtilsTest.java | 92 +++--
.../correctness/test_time_diff_microseconds.out | 2 +-
.../array_functions/test_array_with_scale_type.out | 10 +-
.../cast_function/test_cast_with_scale_type.out | 6 +-
.../test_simplify_comparison.groovy | 51 ---
24 files changed, 1354 insertions(+), 354 deletions(-)
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 5fb76afcca5..0fe54f9069b 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
@@ -1111,7 +1111,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
List<String> types = typedVisit(ctx.dataType());
DataType dataType = DataType.convertPrimitiveFromStrings(types, true);
Expression cast = ParserUtils.withOrigin(ctx, () ->
- new Cast(getExpression(ctx.expression()), dataType));
+ new Cast(getExpression(ctx.expression()), dataType, true));
if (dataType.isStringLikeType() && ((CharacterType) dataType).getLen()
>= 0) {
List<Expression> args = ImmutableList.of(
cast,
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
index c427ea8ec99..008db0dbce2 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
@@ -18,6 +18,7 @@
package org.apache.doris.nereids.rules.expression.rules;
import org.apache.doris.cluster.ClusterNamespace;
+import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.rules.expression.AbstractExpressionRewriteRule;
import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
import org.apache.doris.nereids.trees.expressions.AggregateExpression;
@@ -63,10 +64,12 @@ 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.Literal;
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
import org.apache.doris.nereids.types.BooleanType;
import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.nereids.types.coercion.DateLikeType;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.qe.GlobalVariable;
@@ -310,12 +313,24 @@ public class FoldConstantRuleOnFE extends
AbstractExpressionRewriteRule {
return checkedExpr.get();
}
Expression child = cast.child();
+ DataType dataType = cast.getDataType();
// todo: process other null case
if (child.isNullLiteral()) {
- return new NullLiteral(cast.getDataType());
+ return new NullLiteral(dataType);
+ } else if (child instanceof StringLikeLiteral && dataType instanceof
DateLikeType) {
+ try {
+ return ((DateLikeType)
dataType).fromString(((StringLikeLiteral) child).getStringValue());
+ } catch (AnalysisException t) {
+ if (cast.isExplicitType()) {
+ return new NullLiteral(dataType);
+ } else {
+ // If cast is from type coercion, we don't use NULL
literal and will throw exception.
+ throw t;
+ }
+ }
}
try {
- Expression castResult = child.checkedCastTo(cast.getDataType());
+ Expression castResult = child.checkedCastTo(dataType);
if (!Objects.equals(castResult, cast) &&
!Objects.equals(castResult, child)) {
castResult = rewrite(castResult, context);
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java
index c450e7f7a20..e50acf7d01e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java
@@ -33,16 +33,31 @@ import java.util.Objects;
*/
public class Cast extends Expression implements UnaryExpression {
+ // CAST can be from SQL Query or Type Coercion.
+ private final boolean isExplicitType;
+
private final DataType targetType;
+ public Cast(Expression child, DataType targetType, boolean isExplicitType)
{
+ super(ImmutableList.of(child));
+ this.targetType = Objects.requireNonNull(targetType, "targetType can
not be null");
+ this.isExplicitType = isExplicitType;
+ }
+
public Cast(Expression child, DataType targetType) {
super(ImmutableList.of(child));
this.targetType = Objects.requireNonNull(targetType, "targetType can
not be null");
+ this.isExplicitType = false;
}
- private Cast(List<Expression> child, DataType targetType) {
+ private Cast(List<Expression> child, DataType targetType, boolean
isExplicitType) {
super(child);
this.targetType = Objects.requireNonNull(targetType, "targetType can
not be null");
+ this.isExplicitType = isExplicitType;
+ }
+
+ public boolean isExplicitType() {
+ return isExplicitType;
}
@Override
@@ -72,7 +87,7 @@ public class Cast extends Expression implements
UnaryExpression {
@Override
public Cast withChildren(List<Expression> children) {
Preconditions.checkArgument(children.size() == 1);
- return new Cast(children, getDataType());
+ return new Cast(children, targetType, isExplicitType);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BuiltinFunctionBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BuiltinFunctionBuilder.java
index 23bc62e5f04..74c4a918cf0 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BuiltinFunctionBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BuiltinFunctionBuilder.java
@@ -83,7 +83,7 @@ public class BuiltinFunctionBuilder extends FunctionBuilder {
if (isVariableLength) {
return
builderMethod.newInstance(toVariableLengthArguments(arguments));
} else {
- return
builderMethod.newInstance(arguments.stream().toArray(Object[]::new));
+ return builderMethod.newInstance(arguments.toArray());
}
} catch (Throwable t) {
String argString = arguments.stream()
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTz.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTz.java
index 17a0ccfb334..75270fa022b 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTz.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTz.java
@@ -18,9 +18,12 @@
package org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeType;
@@ -49,7 +52,18 @@ public class ConvertTz extends ScalarFunction
* constructor with 3 arguments.
*/
public ConvertTz(Expression arg0, Expression arg1, Expression arg2) {
- super("convert_tz", arg0, arg1, arg2);
+ super("convert_tz", castDateTime(arg0), arg1, arg2);
+ }
+
+ private static Expression castDateTime(Expression arg0) {
+ //
https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_convert-tz
+ // convert_tz() should be explicit cast, so we create a explicit cast
here
+ try {
+ return arg0 instanceof StringLikeLiteral ? new Cast(arg0,
DateTimeV2Type.forTypeFromString(
+ ((StringLikeLiteral) arg0).getStringValue()), true) : arg0;
+ } catch (Exception e) {
+ return new NullLiteral(DateTimeV2Type.SYSTEM_DEFAULT);
+ }
}
/**
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 e4447e18477..eef80b6020c 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
@@ -27,14 +27,13 @@ import org.apache.doris.nereids.types.DateType;
import org.apache.doris.nereids.types.coercion.DateLikeType;
import org.apache.doris.nereids.util.DateTimeFormatterUtils;
import org.apache.doris.nereids.util.DateUtils;
+import org.apache.doris.nereids.util.StandardDateFormat;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.time.LocalDateTime;
import java.time.Year;
-import java.time.format.DateTimeFormatter;
-import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
@@ -44,10 +43,7 @@ import java.time.temporal.TemporalAccessor;
public class DateLiteral extends Literal {
public static final String JAVA_DATE_FORMAT = "yyyy-MM-dd";
- protected static DateTimeFormatter DATE_FORMATTER = null;
- protected static DateTimeFormatter DATE_FORMATTER_TWO_DIGIT = null;
// for cast datetime type to date type.
- protected static DateTimeFormatter DATE_TIME_FORMATTER = null;
private static final LocalDateTime startOfAD = LocalDateTime.of(0, 1, 1,
0, 0, 0);
private static final LocalDateTime endOfAD = LocalDateTime.of(9999, 12,
31, 23, 59, 59);
private static final Logger LOG = LogManager.getLogger(DateLiteral.class);
@@ -55,26 +51,11 @@ public class DateLiteral extends Literal {
private static final DateLiteral MIN_DATE = new DateLiteral(0000, 1, 1);
private static final DateLiteral MAX_DATE = new DateLiteral(9999, 12, 31);
private static final int[] DAYS_IN_MONTH = new int[] {0, 31, 28, 31, 30,
31, 30, 31, 31, 30, 31, 30, 31};
- private static final int DATEKEY_LENGTH = 8;
protected long year;
protected long month;
protected long day;
- static {
- try {
- DATE_FORMATTER = DateUtils.formatBuilder("%Y-%m-%d").toFormatter()
- .withResolverStyle(ResolverStyle.STRICT);
- DATE_FORMATTER_TWO_DIGIT =
DateUtils.formatBuilder("%y-%m-%d").toFormatter()
- .withResolverStyle(ResolverStyle.STRICT);
- DATE_TIME_FORMATTER = DateUtils.formatBuilder("%Y-%m-%d
%H:%i:%s").toFormatter()
- .withResolverStyle(ResolverStyle.STRICT);
- } catch (AnalysisException e) {
- LOG.error("invalid date format", e);
- System.exit(-1);
- }
- }
-
public DateLiteral(String s) throws AnalysisException {
this(DateType.INSTANCE, s);
}
@@ -115,27 +96,176 @@ public class DateLiteral extends Literal {
this.day = other.day;
}
- protected void init(String s) throws AnalysisException {
+ // normalize yymmdd -> yyyymmdd
+ static String normalizeBasic(String s) {
+ java.util.function.UnaryOperator<String> normalizeTwoDigit = (input)
-> {
+ String yy = input.substring(0, 2);
+ int year = Integer.parseInt(yy);
+ if (year >= 0 && year <= 69) {
+ input = "20" + input;
+ } else if (year >= 70 && year <= 99) {
+ input = "19" + input;
+ }
+ return input;
+ };
+
+ // s.len == 6, assume it is "yymmdd"
+ // 'T' exists, assume it is "yymmddT......"
+ if (s.length() == 6
+ || (s.length() > 6 && s.charAt(6) == 'T')) {
+ // check s index 0 - 6 all is digit char
+ for (int i = 0; i < 6; i++) {
+ if (!Character.isDigit(s.charAt(i))) {
+ return s;
+ }
+ }
+ return normalizeTwoDigit.apply(s);
+ }
+
+ // handle yymmddHHMMSS
+ if (s.length() >= 12) {
+ // check s index 0 - 11 all is digit char
+ for (int i = 0; i < 12; i++) {
+ if (!Character.isDigit(s.charAt(i))) {
+ return s;
+ }
+ }
+ if (s.length() == 12 || !Character.isDigit(s.charAt(12))) {
+ return normalizeTwoDigit.apply(s);
+ }
+ }
+
+ return s;
+ }
+
+ static String normalize(String s) {
+ StringBuilder sb = new StringBuilder();
+
+ int i = 0;
+
+ // handle two digit year
+ if (s.charAt(2) != '-' && s.charAt(4) != '-') {
+ throw new AnalysisException("date/datetime literal [" + s + "] is
invalid");
+ }
+ if (s.charAt(2) == '-') {
+ String yy = s.substring(0, 2);
+ int year = Integer.parseInt(yy);
+ if (year >= 0 && year <= 69) {
+ sb.append("20");
+ } else if (year >= 70 && year <= 99) {
+ sb.append("19");
+ }
+ sb.append(yy);
+ i = 2;
+ }
+
+ // normalized leading 0
+ while (i < s.length()) {
+ char c = s.charAt(i);
+
+ if (c == '.') {
+ // skip .microsecond, such as .0001 .000001
+ sb.append(c); // Append the dot itself
+ i += 1; // Skip the dot
+
+ // skip the microsecond part
+ while (i < s.length() && Character.isDigit(s.charAt(i))) {
+ sb.append(s.charAt(i));
+ i += 1;
+ }
+ } else if (Character.isDigit(c)) {
+ // find consecutive digit
+ int j = i + 1;
+ while (j < s.length() && Character.isDigit(s.charAt(j))) {
+ j += 1;
+ }
+ int len = j - i;
+ if (len == 4 || len == 2) {
+ for (int k = i; k < j; k++) {
+ sb.append(s.charAt(k));
+ }
+ } else if (len == 1) {
+ sb.append('0');
+ sb.append(c);
+ } else {
+ throw new AnalysisException("date/datetime literal [" + s
+ "] is invalid");
+ }
+ i = j;
+ } else {
+ sb.append(c);
+ i += 1;
+ }
+ }
+
+ int len = sb.length();
+ // Replace delimiter 'T' with ' '
+ if (len > 10 && sb.charAt(10) == 'T') {
+ sb.setCharAt(10, ' ');
+ }
+
+ // add missing Minute Second in Time part
+ if (len > 10 && sb.charAt(10) == ' ') {
+ if (len == 13 || len > 13 && sb.charAt(13) != ':') {
+ sb.insert(13, ":00:00");
+ } else if (len == 16 || (len > 16 && sb.charAt(16) != ':')) {
+ sb.insert(16, ":00");
+ }
+ }
+
+ len = sb.length();
+ int signIdx = sb.indexOf("+", 10); // from index:10, skip date part
(it contains '-')
+ signIdx = signIdx == -1 ? sb.indexOf("-", 10) : signIdx;
+ if (signIdx != -1 && len - signIdx == 3) {
+ sb.append(":00");
+ }
+
+ return sb.toString();
+ }
+
+ protected static TemporalAccessor parse(String s) {
+ String originalString = s;
try {
TemporalAccessor dateTime;
+
+ // parse condition without '-' and ':'
if (!s.contains("-") && !s.contains(":")) {
- dateTime =
DateTimeFormatterUtils.BASIC_DATE_TIME_FORMATTER.parse(s);
- } else if (s.split("-")[0].length() == 2) {
- dateTime = DATE_FORMATTER_TWO_DIGIT.parse(s);
- } else if (s.length() == 19) {
- dateTime = DATE_TIME_FORMATTER.parse(s);
+ s = normalizeBasic(s);
+ // mysql reject "20200219 010101" "200219 010101", can't use '
' spilt basic date time.
+ if (!s.contains("T")) {
+ dateTime =
DateTimeFormatterUtils.BASIC_FORMATTER_WITHOUT_T.parse(s);
+ } else {
+ dateTime =
DateTimeFormatterUtils.BASIC_DATE_TIME_FORMATTER.parse(s);
+ }
+ return dateTime;
+ }
+
+ s = normalize(s);
+
+ if (!s.contains(" ")) {
+ dateTime = DateTimeFormatterUtils.ZONE_DATE_FORMATTER.parse(s);
} else {
- dateTime = DATE_FORMATTER.parse(s);
+ dateTime =
DateTimeFormatterUtils.ZONE_DATE_TIME_FORMATTER.parse(s);
}
- year = DateUtils.getOrDefault(dateTime, ChronoField.YEAR);
- month = DateUtils.getOrDefault(dateTime,
ChronoField.MONTH_OF_YEAR);
- day = DateUtils.getOrDefault(dateTime, ChronoField.DAY_OF_MONTH);
+
+ // if Year is not present, throw exception
+ if (!dateTime.isSupported(ChronoField.YEAR)) {
+ throw new AnalysisException("date/datetime literal [" +
originalString + "] is invalid");
+ }
+
+ return dateTime;
} catch (Exception ex) {
- throw new AnalysisException("date literal [" + s + "] is invalid");
+ throw new AnalysisException("date/datetime literal [" +
originalString + "] is invalid");
}
+ }
+
+ protected void init(String s) throws AnalysisException {
+ TemporalAccessor dateTime = parse(s);
+ year = DateUtils.getOrDefault(dateTime, ChronoField.YEAR);
+ month = DateUtils.getOrDefault(dateTime, ChronoField.MONTH_OF_YEAR);
+ day = DateUtils.getOrDefault(dateTime, ChronoField.DAY_OF_MONTH);
if (checkRange() || checkDate()) {
- throw new AnalysisException("date literal [" + s + "] is out of
range");
+ throw new AnalysisException("date/datetime literal [" + s + "] is
out of range");
}
}
@@ -204,16 +334,18 @@ public class DateLiteral extends Literal {
return day;
}
- public Expression plusDays(int days) {
- return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusDays(days));
+ public Expression plusDays(long days) {
+ return
fromJavaDateType(DateUtils.getTime(StandardDateFormat.DATE_FORMATTER,
getStringValue()).plusDays(days));
}
- public Expression plusMonths(int months) {
- return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusMonths(months));
+ public Expression plusMonths(long months) {
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_FORMATTER,
getStringValue()).plusMonths(months));
}
- public Expression plusYears(int years) {
- return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusYears(years));
+ public Expression plusYears(long years) {
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_FORMATTER,
getStringValue()).plusYears(years));
}
public LocalDateTime toJavaDateType() {
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 1b3d415553d..2d5ea77072b 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
@@ -24,31 +24,24 @@ import
org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeType;
import org.apache.doris.nereids.types.coercion.DateLikeType;
-import org.apache.doris.nereids.util.DateTimeFormatterUtils;
import org.apache.doris.nereids.util.DateUtils;
+import org.apache.doris.nereids.util.StandardDateFormat;
-import com.google.common.base.Preconditions;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import java.time.Instant;
import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeFormatterBuilder;
-import java.time.format.ResolverStyle;
+import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
-import java.util.Collections;
+import java.time.temporal.TemporalQueries;
import java.util.Objects;
-import java.util.regex.Pattern;
/**
* date time literal.
*/
public class DateTimeLiteral extends DateLiteral {
- protected static DateTimeFormatter DATE_TIME_FORMATTER_TO_HOUR = null;
- protected static DateTimeFormatter DATE_TIME_FORMATTER_TO_MINUTE = null;
- protected static DateTimeFormatter DATE_TIME_FORMATTER_TWO_DIGIT = null;
- protected static DateTimeFormatter DATE_TIME_FORMATTER_TO_MICRO_SECOND =
null;
protected static final int MAX_MICROSECOND = 999999;
private static final DateTimeLiteral MIN_DATETIME = new
DateTimeLiteral(0000, 1, 1, 0, 0, 0);
@@ -56,35 +49,11 @@ public class DateTimeLiteral extends DateLiteral {
private static final Logger LOG =
LogManager.getLogger(DateTimeLiteral.class);
- private static final Pattern HAS_OFFSET_PART =
Pattern.compile("[\\+\\-]\\d{2}:\\d{2}");
-
protected long hour;
protected long minute;
protected long second;
protected long microSecond;
- static {
- try {
- DATE_TIME_FORMATTER = DateUtils.formatBuilder("%Y-%m-%d %H:%i:%s")
- .toFormatter().withResolverStyle(ResolverStyle.STRICT);
- DATE_TIME_FORMATTER_TO_HOUR = DateUtils.formatBuilder("%Y-%m-%d
%H")
- .toFormatter().withResolverStyle(ResolverStyle.STRICT);
- DATE_TIME_FORMATTER_TO_MINUTE = DateUtils.formatBuilder("%Y-%m-%d
%H:%i")
- .toFormatter().withResolverStyle(ResolverStyle.STRICT);
- DATE_TIME_FORMATTER_TWO_DIGIT = DateUtils.formatBuilder("%y-%m-%d
%H:%i:%s")
- .toFormatter().withResolverStyle(ResolverStyle.STRICT);
-
- DATE_TIME_FORMATTER_TO_MICRO_SECOND = new
DateTimeFormatterBuilder()
- .appendPattern("uuuu-MM-dd HH:mm:ss")
- .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true)
- .toFormatter()
- .withResolverStyle(ResolverStyle.STRICT);
- } catch (AnalysisException e) {
- LOG.error("invalid date format", e);
- System.exit(-1);
- }
- }
-
public DateTimeLiteral(String s) {
this(DateTimeType.INSTANCE, s);
}
@@ -128,95 +97,60 @@ public class DateTimeLiteral extends DateLiteral {
return hour == 0 && minute == 0 && second == 0 && microSecond == 0;
}
- @Override
- protected void init(String s) throws AnalysisException {
- try {
- TemporalAccessor dateTime = null;
- // parse timezone
- if (haveTimeZoneOffset(s) || haveTimeZoneName(s)) {
- if (haveTimeZoneName(s)) { // GMT, UTC+8, Z[, CN,
Asia/Shanghai]
- int split = getTimeZoneSplitPos(s);
- Preconditions.checkArgument(split > 0);
- s = s.substring(0, split);
- } else { // +04:30
- Preconditions.checkArgument(s.charAt(s.length() - 6) ==
'-' || s.charAt(s.length() - 6) == '+');
- s = s.substring(0, s.length() - 6);
- }
+ /**
+ * determine scale by datetime string
+ */
+ public static int determineScale(String s) {
+ if (!s.contains("-") && !s.contains(":")) {
+ return 0;
+ }
+ s = normalize(s);
+ if (s.length() <= 19 || s.charAt(19) != '.') {
+ return 0;
+ }
+ // from index 19 find the index of first char which is not digit
+ int scale = 0;
+ for (int i = 20; i < s.length(); i++) {
+ if (!Character.isDigit(s.charAt(i))) {
+ break;
}
- if (!s.contains("-") && !s.contains(":")) {
- dateTime =
DateTimeFormatterUtils.BASIC_DATE_TIME_FORMATTER.parse(s);
- } else {
- String[] datePart = s.contains(" ") ? s.split("
")[0].split("-") : s.split("-");
- DateTimeFormatterBuilder builder = new
DateTimeFormatterBuilder();
- if (datePart.length != 3) {
- throw new AnalysisException("datetime literal [" + s + "]
is invalid");
- }
- for (int i = 0; i < datePart.length; i++) {
- switch (i) {
- case 0:
- if (datePart[i].length() == 2) {
- // If year is represented by two digits,
number bigger than 70 will be prefixed
- // with 19 otherwise 20. e.g. 69 -> 2069, 70
-> 1970.
- builder.appendValueReduced(ChronoField.YEAR,
2, 2, 1970);
- } else {
- builder.appendPattern(String.join("",
Collections.nCopies(datePart[i].length(), "u")));
- }
- break;
- case 1:
- builder.appendPattern(String.join("",
Collections.nCopies(datePart[i].length(), "M")));
- break;
- case 2:
- builder.appendPattern(String.join("",
Collections.nCopies(datePart[i].length(), "d")));
- break;
- default:
- throw new AnalysisException("two many parts in
date format " + s);
- }
- if (i < datePart.length - 1) {
- builder.appendLiteral("-");
- }
- }
- if (s.contains(" ")) {
- builder.appendLiteral(" ");
- }
- String[] timePart = s.contains(" ") ? s.split("
")[1].split(":") : new String[]{};
- for (int i = 0; i < timePart.length; i++) {
- switch (i) {
- case 0:
- builder.appendPattern(String.join("",
Collections.nCopies(timePart[i].length(), "H")));
- break;
- case 1:
- builder.appendPattern(String.join("",
Collections.nCopies(timePart[i].length(), "m")));
- break;
- case 2:
- builder.appendPattern(String.join("",
Collections.nCopies(timePart[i].contains(".")
- ? timePart[i].split("\\.")[0].length() :
timePart[i].length(), "s")));
- if (timePart[i].contains(".")) {
-
builder.appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true);
- }
- break;
- default:
- throw new AnalysisException("too many parts in
time format " + s);
- }
- if (i < timePart.length - 1) {
- builder.appendLiteral(":");
- }
- }
- // The default resolver style is 'SMART', which parses
"2022-06-31" as "2022-06-30"
- // and does not throw an exception. 'STRICT' is used here.
- DateTimeFormatter formatter =
builder.toFormatter().withResolverStyle(ResolverStyle.STRICT);
- dateTime = formatter.parse(s);
+ scale++;
+ }
+ // trim the tailing zero
+ for (int i = 19 + scale; i >= 19; i--) {
+ if (s.charAt(i) != '0') {
+ break;
}
+ scale--;
+ }
+ return scale;
+ }
- year = DateUtils.getOrDefault(dateTime, ChronoField.YEAR);
- month = DateUtils.getOrDefault(dateTime,
ChronoField.MONTH_OF_YEAR);
- day = DateUtils.getOrDefault(dateTime, ChronoField.DAY_OF_MONTH);
- hour = DateUtils.getOrDefault(dateTime, ChronoField.HOUR_OF_DAY);
- minute = DateUtils.getOrDefault(dateTime,
ChronoField.MINUTE_OF_HOUR);
- second = DateUtils.getOrDefault(dateTime,
ChronoField.SECOND_OF_MINUTE);
- microSecond = DateUtils.getOrDefault(dateTime,
ChronoField.MICRO_OF_SECOND);
-
- } catch (Exception ex) {
- throw new AnalysisException("datetime literal [" + s + "] is
invalid");
+ @Override
+ protected void init(String s) throws AnalysisException {
+ TemporalAccessor temporal = parse(s);
+
+ year = DateUtils.getOrDefault(temporal, ChronoField.YEAR);
+ month = DateUtils.getOrDefault(temporal, ChronoField.MONTH_OF_YEAR);
+ day = DateUtils.getOrDefault(temporal, ChronoField.DAY_OF_MONTH);
+ hour = DateUtils.getOrDefault(temporal, ChronoField.HOUR_OF_DAY);
+ minute = DateUtils.getOrDefault(temporal, ChronoField.MINUTE_OF_HOUR);
+ second = DateUtils.getOrDefault(temporal,
ChronoField.SECOND_OF_MINUTE);
+ microSecond = DateUtils.getOrDefault(temporal,
ChronoField.MICRO_OF_SECOND);
+
+ ZoneId zoneId = temporal.query(TemporalQueries.zone());
+ if (zoneId != null) {
+ int offset =
DateUtils.getTimeZone().getRules().getOffset(Instant.now()).getTotalSeconds()
+ -
zoneId.getRules().getOffset(Instant.now()).getTotalSeconds();
+ if (offset != 0) {
+ DateTimeLiteral result = (DateTimeLiteral)
this.plusSeconds(offset);
+ this.second = result.second;
+ this.minute = result.minute;
+ this.hour = result.hour;
+ this.day = result.day;
+ this.month = result.month;
+ this.year = result.year;
+ }
}
if (checkRange() || checkDate()) {
@@ -265,28 +199,34 @@ 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) {
- return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusYears(years));
+ public Expression plusYears(long years) {
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER,
getStringValue()).plusYears(years));
}
- public Expression plusMonths(int months) {
- return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusMonths(months));
+ public Expression plusMonths(long months) {
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER,
getStringValue()).plusMonths(months));
}
- public Expression plusDays(int days) {
- return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusDays(days));
+ public Expression plusDays(long days) {
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER,
getStringValue()).plusDays(days));
}
- public Expression plusHours(int hours) {
- return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusHours(hours));
+ public Expression plusHours(long hours) {
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER,
getStringValue()).plusHours(hours));
}
- public Expression plusMinutes(int minutes) {
- return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusMinutes(minutes));
+ public Expression plusMinutes(long minutes) {
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER,
getStringValue()).plusMinutes(minutes));
}
public Expression plusSeconds(long seconds) {
- return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER,
getStringValue()).plusSeconds(seconds));
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER,
getStringValue()).plusSeconds(seconds));
}
public long getHour() {
@@ -328,27 +268,4 @@ public class DateTimeLiteral extends DateLiteral {
: new DateTimeLiteral(dateTime.getYear(),
dateTime.getMonthValue(), dateTime.getDayOfMonth(),
dateTime.getHour(), dateTime.getMinute(),
dateTime.getSecond());
}
-
- private static boolean haveTimeZoneOffset(String arg) {
- Preconditions.checkArgument(arg.length() > 6);
- return HAS_OFFSET_PART.matcher(arg.substring(arg.length() -
6)).matches();
- }
-
- private static boolean haveTimeZoneName(String arg) {
- for (char ch : arg.toCharArray()) {
- if (Character.isUpperCase(ch) && ch != 'T') {
- return true;
- }
- }
- return false;
- }
-
- private static int getTimeZoneSplitPos(String arg) {
- int split = arg.length() - 1;
- for (; !Character.isAlphabetic(arg.charAt(split)); split--) {
- } // skip +8 of UTC+8
- for (; split >= 0 && (Character.isUpperCase(arg.charAt(split)) ||
arg.charAt(split) == '/'); split--) {
- }
- return split + 1;
- }
}
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 ef09399a5aa..87b881074f1 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
@@ -23,6 +23,9 @@ import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.util.DateUtils;
+import org.apache.doris.nereids.util.StandardDateFormat;
+
+import com.google.common.base.Preconditions;
import java.time.LocalDateTime;
@@ -37,6 +40,7 @@ public class DateTimeV2Literal extends DateTimeLiteral {
public DateTimeV2Literal(DateTimeV2Type dateType, String s) {
super(dateType, s);
+ roundMicroSecond(dateType.getScale());
}
public DateTimeV2Literal(long year, long month, long day, long hour, long
minute, long second) {
@@ -47,9 +51,30 @@ public class DateTimeV2Literal extends DateTimeLiteral {
super(DateTimeV2Type.SYSTEM_DEFAULT, year, month, day, hour, minute,
second, microSecond);
}
- public DateTimeV2Literal(DateTimeV2Type dataType,
+ public DateTimeV2Literal(DateTimeV2Type dateType,
long year, long month, long day, long hour, long minute, long
second, long microSecond) {
- super(dataType, year, month, day, hour, minute, second, microSecond);
+ super(dateType, year, month, day, hour, minute, second, microSecond);
+ roundMicroSecond(dateType.getScale());
+ }
+
+ private void roundMicroSecond(int scale) {
+ Preconditions.checkArgument(scale >= 0 && scale <=
DateTimeV2Type.MAX_SCALE,
+ "invalid datetime v2 scale: %s", scale);
+ double factor = Math.pow(10, 6 - scale);
+
+ this.microSecond = Math.round(this.microSecond / factor) * (int)
factor;
+
+ if (this.microSecond >= 1000000) {
+ LocalDateTime localDateTime =
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND,
+ getStringValue()).plusSeconds(1);
+ this.year = localDateTime.getYear();
+ this.month = localDateTime.getMonthValue();
+ this.day = localDateTime.getDayOfMonth();
+ this.hour = localDateTime.getHour();
+ this.minute = localDateTime.getMinute();
+ this.second = localDateTime.getSecond();
+ this.microSecond -= 1000000;
+ }
}
@Override
@@ -76,50 +101,57 @@ public class DateTimeV2Literal extends DateTimeLiteral {
@Override
public String getStringValue() {
return String.format("%04d-%02d-%02d %02d:%02d:%02d"
- + (getDataType().getScale() > 0 ? ".%0" +
getDataType().getScale() + "d" : ""),
+ + (getDataType().getScale() > 0 ? ".%0" +
getDataType().getScale() + "d" : ""),
year, month, day, hour, minute, second,
(int) (microSecond / Math.pow(10, DateTimeV2Type.MAX_SCALE -
getDataType().getScale())));
}
@Override
- public Expression plusYears(int years) {
- return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
- .plusYears(years), getDataType().getScale());
+ public Expression plusYears(long years) {
+ return fromJavaDateType(
+
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
+ .plusYears(years), getDataType().getScale());
}
@Override
- public Expression plusMonths(int months) {
- return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
- .plusMonths(months), getDataType().getScale());
+ public Expression plusMonths(long months) {
+ return fromJavaDateType(
+
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
+ .plusMonths(months), getDataType().getScale());
}
@Override
- public Expression plusDays(int days) {
- return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
- .plusDays(days), getDataType().getScale());
+ public Expression plusDays(long days) {
+ return fromJavaDateType(
+
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
+ .plusDays(days), getDataType().getScale());
}
@Override
- public Expression plusHours(int hours) {
- return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
- .plusHours(hours), getDataType().getScale());
+ public Expression plusHours(long hours) {
+ return fromJavaDateType(
+
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
+ .plusHours(hours), getDataType().getScale());
}
@Override
- public Expression plusMinutes(int minutes) {
- return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
- .plusMinutes(minutes), getDataType().getScale());
+ public Expression plusMinutes(long minutes) {
+ return fromJavaDateType(
+
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
+ .plusMinutes(minutes), getDataType().getScale());
}
@Override
public Expression plusSeconds(long seconds) {
- return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
- .plusSeconds(seconds), getDataType().getScale());
+ return fromJavaDateType(
+
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
+ .plusSeconds(seconds), getDataType().getScale());
}
- public Expression plusMicroSeconds(int microSeconds) {
- return
fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
- .plusNanos(microSeconds * 1000L), getDataType().getScale());
+ public Expression plusMicroSeconds(long microSeconds) {
+ return fromJavaDateType(
+
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND,
getStringValue())
+ .plusNanos(microSeconds * 1000L),
getDataType().getScale());
}
/**
@@ -155,11 +187,8 @@ public class DateTimeV2Literal extends DateTimeLiteral {
}
public DateTimeV2Literal roundFloor(int newScale) {
- long newMicroSecond = Double.valueOf(
- microSecond / (int) (Math.pow(10, 6 - newScale)) *
(Math.pow(10, 6 - newScale)))
- .longValue();
- return new DateTimeV2Literal(DateTimeV2Type.of(newScale), year, month,
day, hour, minute,
- second, newMicroSecond);
+ // use roundMicroSecond in constructor
+ return new DateTimeV2Literal(DateTimeV2Type.of(newScale), year, month,
day, hour, minute, second, microSecond);
}
public static Expression fromJavaDateType(LocalDateTime dateTime) {
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 928b7ca0f24..c57607892e3 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
@@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateV2Type;
import org.apache.doris.nereids.util.DateUtils;
+import org.apache.doris.nereids.util.StandardDateFormat;
import java.time.LocalDateTime;
@@ -50,16 +51,18 @@ public class DateV2Literal extends DateLiteral {
return visitor.visitDateV2Literal(this, context);
}
- public Expression plusDays(int days) {
- return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusDays(days));
+ public Expression plusDays(long days) {
+ return
fromJavaDateType(DateUtils.getTime(StandardDateFormat.DATE_FORMATTER,
getStringValue()).plusDays(days));
}
- public Expression plusMonths(int months) {
- return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusMonths(months));
+ public Expression plusMonths(long months) {
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_FORMATTER,
getStringValue()).plusMonths(months));
}
- public Expression plusYears(int years) {
- return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER,
getStringValue()).plusYears(years));
+ public Expression plusYears(long years) {
+ return fromJavaDateType(
+ DateUtils.getTime(StandardDateFormat.DATE_FORMATTER,
getStringValue()).plusYears(years));
}
public static Expression fromJavaDateType(LocalDateTime dateTime) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeV2Type.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeV2Type.java
index 6a57c7a218c..0b8f64316bd 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeV2Type.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeV2Type.java
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.types;
import org.apache.doris.catalog.ScalarType;
import org.apache.doris.catalog.Type;
import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
import org.apache.doris.nereids.types.coercion.AbstractDataType;
import org.apache.doris.nereids.types.coercion.DateLikeType;
import org.apache.doris.nereids.types.coercion.IntegralType;
@@ -83,10 +84,8 @@ public class DateTimeV2Type extends DateLikeType {
* may be we need to check for validity?
*/
public static DateTimeV2Type forTypeFromString(String s) {
- if (!s.contains(String.valueOf("."))) {
- return DateTimeV2Type.SYSTEM_DEFAULT;
- }
- return DateTimeV2Type.of(s.length() - s.lastIndexOf(".") - 1);
+ int scale = DateTimeLiteral.determineScale(s);
+ return DateTimeV2Type.of(scale);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/DateLikeType.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/DateLikeType.java
index 18b207fdaf7..ff728a73ace 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/DateLikeType.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/DateLikeType.java
@@ -17,6 +17,16 @@
package org.apache.doris.nereids.types.coercion;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
+import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
+import org.apache.doris.nereids.types.DateTimeType;
+import org.apache.doris.nereids.types.DateTimeV2Type;
+import org.apache.doris.nereids.types.DateType;
+import org.apache.doris.nereids.types.DateV2Type;
+
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
@@ -43,4 +53,21 @@ public abstract class DateLikeType extends PrimitiveType {
Calendar from = toCalendar(low);
return ChronoUnit.DAYS.between(from.toInstant(), to.toInstant());
}
+
+ /**
+ * parse string to date like literal.
+ */
+ public DateLiteral fromString(String s) {
+ if (this instanceof DateType) {
+ return new DateLiteral(s);
+ } else if (this instanceof DateV2Type) {
+ return new DateV2Literal(s);
+ } else if (this instanceof DateTimeType) {
+ return new DateTimeLiteral(s);
+ } else if (this instanceof DateTimeV2Type) {
+ return new DateTimeV2Literal((DateTimeV2Type) this, s);
+ } else {
+ throw new AnalysisException("unknown date like type");
+ }
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateTimeFormatterUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateTimeFormatterUtils.java
index 1925e100aca..e88aabc018b 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateTimeFormatterUtils.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateTimeFormatterUtils.java
@@ -39,42 +39,68 @@ import java.time.temporal.ChronoField;
* Note incomplete times 'hh:mm:ss', 'hh:mm', 'D hh:mm', 'D hh', or 'ss'
*/
public class DateTimeFormatterUtils {
- // Date: %Y-%m-%d
- public static DateTimeFormatter DATE_FORMATTER = new
DateTimeFormatterBuilder()
- .appendOptional(
- new
DateTimeFormatterBuilder().appendValue(ChronoField.YEAR, 4).toFormatter())
- .appendOptional(
- new
DateTimeFormatterBuilder().appendValueReduced(ChronoField.YEAR, 2, 2,
1970).toFormatter())
+ public static final DateTimeFormatter ZONE_FORMATTER = new
DateTimeFormatterBuilder()
+ .optionalStart()
+ // .appendZoneText(TextStyle.FULL)
+ .appendZoneOrOffsetId()
+ .optionalEnd()
+ .toFormatter()
+ .withResolverStyle(ResolverStyle.STRICT);
+ public static final DateTimeFormatter DATE_FORMATTER = new
DateTimeFormatterBuilder()
+ .appendValue(ChronoField.YEAR, 4)
.appendLiteral('-').appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendLiteral('-').appendValue(ChronoField.DAY_OF_MONTH, 2)
.toFormatter().withResolverStyle(ResolverStyle.STRICT);
- // Date without delimiter: %Y%m%d
- public static DateTimeFormatter BASIC_DATE_FORMATTER = new
DateTimeFormatterBuilder()
- .appendValue(ChronoField.YEAR, 4)
- .appendValue(ChronoField.MONTH_OF_YEAR, 2)
- .appendValue(ChronoField.DAY_OF_MONTH, 2)
- .toFormatter().withResolverStyle(ResolverStyle.STRICT);
- // Time: %H:%i:%s
- public static DateTimeFormatter TIME_FORMATTER = new
DateTimeFormatterBuilder()
+ // HH[:mm][:ss][.microsecond]
+ public static final DateTimeFormatter TIME_FORMATTER = new
DateTimeFormatterBuilder()
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendLiteral(':').appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.appendLiteral(':').appendValue(ChronoField.SECOND_OF_MINUTE, 2)
+ .appendOptional(new DateTimeFormatterBuilder()
+ .appendFraction(ChronoField.MICRO_OF_SECOND, 1, 6,
true).toFormatter())
.toFormatter().withResolverStyle(ResolverStyle.STRICT);
- // Time without delimiter: HHmmss[microsecond]
- public static DateTimeFormatter BASIC_TIME_FORMATTER = new
DateTimeFormatterBuilder()
+ // Time without delimiter: HHmmss[.microsecond]
+ private static final DateTimeFormatter BASIC_TIME_FORMATTER = new
DateTimeFormatterBuilder()
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
.appendOptional(new DateTimeFormatterBuilder()
.appendFraction(ChronoField.MICRO_OF_SECOND, 1, 6,
true).toFormatter())
.toFormatter().withResolverStyle(ResolverStyle.STRICT);
-
+ // yyyymmdd
+ private static final DateTimeFormatter BASIC_DATE_FORMATTER = new
DateTimeFormatterBuilder()
+ .appendValue(ChronoField.YEAR, 4)
+ .appendValue(ChronoField.MONTH_OF_YEAR, 2)
+ .appendValue(ChronoField.DAY_OF_MONTH, 2)
+ .toFormatter().withResolverStyle(ResolverStyle.STRICT);
// Date without delimiter
- public static DateTimeFormatter BASIC_DATE_TIME_FORMATTER = new
DateTimeFormatterBuilder()
+ public static final DateTimeFormatter BASIC_DATE_TIME_FORMATTER = new
DateTimeFormatterBuilder()
.append(BASIC_DATE_FORMATTER)
- .optionalStart()
- .appendOptional(new
DateTimeFormatterBuilder().appendLiteral('T').toFormatter())
+ .appendLiteral('T')
.append(BASIC_TIME_FORMATTER)
- .optionalEnd()
+ .toFormatter().withResolverStyle(ResolverStyle.STRICT);
+ // Date without delimiter
+ public static final DateTimeFormatter BASIC_FORMATTER_WITHOUT_T = new
DateTimeFormatterBuilder()
+ .append(BASIC_DATE_FORMATTER)
+ .appendOptional(BASIC_TIME_FORMATTER)
+ .toFormatter().withResolverStyle(ResolverStyle.STRICT);
+
+ // Datetime
+ public static final DateTimeFormatter DATE_TIME_FORMATTER = new
DateTimeFormatterBuilder()
+ .append(DATE_FORMATTER)
+ .appendLiteral(' ')
+ .append(TIME_FORMATTER)
+ .toFormatter().withResolverStyle(ResolverStyle.STRICT);
+ public static final DateTimeFormatter ZONE_DATE_FORMATTER = new
DateTimeFormatterBuilder()
+ .appendValue(ChronoField.YEAR, 4)
+ .appendLiteral('-').appendValue(ChronoField.MONTH_OF_YEAR, 2)
+ .appendLiteral('-').appendValue(ChronoField.DAY_OF_MONTH, 2)
+ .append(ZONE_FORMATTER)
+ .toFormatter().withResolverStyle(ResolverStyle.STRICT);
+ public static final DateTimeFormatter ZONE_DATE_TIME_FORMATTER = new
DateTimeFormatterBuilder()
+ .append(DATE_FORMATTER)
+ .appendLiteral(' ')
+ .append(TIME_FORMATTER)
+ .append(ZONE_FORMATTER)
.toFormatter().withResolverStyle(ResolverStyle.STRICT);
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/StandardDateFormat.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/StandardDateFormat.java
new file mode 100644
index 00000000000..aa421eeab43
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/StandardDateFormat.java
@@ -0,0 +1,53 @@
+// 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.
+
+package org.apache.doris.nereids.util;
+
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.ResolverStyle;
+import java.time.temporal.ChronoField;
+
+/**
+ * Following format is *standard* format of date/datetime. It will keep format
of memory/output/printf is standard.
+ */
+public class StandardDateFormat {
+ // Date: %Y-%m-%d
+ public static DateTimeFormatter DATE_FORMATTER = new
DateTimeFormatterBuilder()
+ .appendValue(ChronoField.YEAR, 4)
+ .appendLiteral('-').appendValue(ChronoField.MONTH_OF_YEAR, 2)
+ .appendLiteral('-').appendValue(ChronoField.DAY_OF_MONTH, 2)
+ .toFormatter().withResolverStyle(ResolverStyle.STRICT);
+ // %H:%i:%s
+ public static DateTimeFormatter TIME_FORMATTER = new
DateTimeFormatterBuilder()
+ .appendValue(ChronoField.HOUR_OF_DAY, 2)
+ .appendLiteral(':').appendValue(ChronoField.MINUTE_OF_HOUR, 2)
+ .appendLiteral(':').appendValue(ChronoField.SECOND_OF_MINUTE, 2)
+ .toFormatter().withResolverStyle(ResolverStyle.STRICT);
+ public static DateTimeFormatter DATE_TIME_FORMATTER = new
DateTimeFormatterBuilder()
+ .append(DATE_FORMATTER)
+ .appendLiteral(' ')
+ .append(TIME_FORMATTER)
+ .toFormatter().withResolverStyle(ResolverStyle.STRICT);
+ // "%Y-%m-%d %H:%i:%s.%f"
+ public static DateTimeFormatter DATE_TIME_FORMATTER_TO_MICRO_SECOND = new
DateTimeFormatterBuilder()
+ .append(DATE_FORMATTER)
+ .appendLiteral(' ')
+ .append(TIME_FORMATTER)
+ .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true) //
Notice: min size is 0
+ .toFormatter().withResolverStyle(ResolverStyle.STRICT);
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyComparisonPredicateSqlTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyComparisonPredicateSqlTest.java
new file mode 100644
index 00000000000..b3a2b5c9af0
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyComparisonPredicateSqlTest.java
@@ -0,0 +1,159 @@
+// 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.
+
+package org.apache.doris.nereids.rules.expression.rules;
+
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.types.DateTimeV2Type;
+import org.apache.doris.nereids.util.MemoPatternMatchSupported;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class SimplifyComparisonPredicateSqlTest extends TestWithFeService implements
MemoPatternMatchSupported {
+
+ @Override
+ protected void runBeforeAll() throws Exception {
+ createDatabase("test");
+ connectContext.setDatabase("default_cluster:test");
+ createTables(
+ "CREATE TABLE IF NOT EXISTS `log_items_test` (\n"
+ + " a DATETIME(0) NOT NULL,\n"
+ + " b decimal(10,2)\n"
+ + " ) ENGINE=OLAP\n"
+ + " UNIQUE KEY (`a`)\n"
+ + " DISTRIBUTED BY HASH(`a`) BUCKETS 120\n"
+ + " PROPERTIES (\n"
+ + " \"replication_num\" = \"1\",\n"
+ + " \"in_memory\" = \"false\",\n"
+ + " \"compression\" = \"LZ4\",\n"
+ + " \"storage_cooldown_time\" =
\"9999-12-31 23:59:59\",\n"
+ + " \"enable_unique_key_merge_on_write\" =
\"true\"\n"
+ + " );"
+ );
+ }
+
+ @Test
+ void testSql() {
+ PlanChecker.from(connectContext)
+ .analyze("select * from log_items_test where a < '2023-06-15
23:59:59.999' and b < 111.111;")
+ .rewrite()
+ .matches(
+ logicalFilter()
+ .when(f -> f.getConjuncts().stream().anyMatch(e ->
e.toSql().equals("(a < 2023-06-16 00:00:00)")))
+ .when(f -> f.getConjuncts().stream().anyMatch(e ->
e.toSql().equals("(b < 111.12)")))
+ );
+
+ PlanChecker.from(connectContext)
+ .analyze("select * from log_items_test where a <= '2023-06-15
23:59:59.999' and b <= 111.111;")
+ .rewrite()
+ .matches(
+ logicalFilter()
+ .when(f ->
f.getConjuncts().stream().anyMatch(e -> e.toSql().equals("(a <= 2023-06-16
00:00:00)")))
+ .when(f ->
f.getConjuncts().stream().anyMatch(e -> e.toSql().equals("(b <= 111.11)")))
+ );
+
+ PlanChecker.from(connectContext)
+ .analyze("select * from log_items_test where a = '2023-06-15
23:59:59.999' and b = 111.111;")
+ .rewrite()
+ .matches(
+ logicalEmptyRelation()
+ );
+
+ PlanChecker.from(connectContext)
+ .analyze("select * from log_items_test where a > '2023-06-15
23:59:59.999' and b > 111.111;")
+ .rewrite()
+ .matches(
+ logicalFilter()
+ .when(f ->
f.getConjuncts().stream().anyMatch(e -> e.toSql().equals("(a > 2023-06-16
00:00:00)")))
+ .when(f ->
f.getConjuncts().stream().anyMatch(e -> e.toSql().equals("(b > 111.11)")))
+ );
+
+ PlanChecker.from(connectContext)
+ .analyze("select * from log_items_test where a >= '2023-06-15
23:59:59.999' and b >= 111.111;")
+ .rewrite()
+ .matches(
+ logicalFilter()
+ .when(f ->
f.getConjuncts().stream().anyMatch(e -> e.toSql().equals("(a >= 2023-06-16
00:00:00)")))
+ .when(f ->
f.getConjuncts().stream().anyMatch(e -> e.toSql().equals("(b >= 111.12)")))
+ );
+ }
+
+ @Test
+ void dateLikeOverflow() {
+ PlanChecker.from(connectContext)
+ .analyze("select CAST('2021-01-32 00:00:00' AS DATETIME(6))")
+ .rewrite()
+ .matches(
+ logicalResultSink(
+ logicalOneRowRelation().when(p ->
p.getProjects().get(0).child(0).equals(new NullLiteral(DateTimeV2Type.of(6))))
+ )
+ );
+
+ PlanChecker.from(connectContext)
+ .analyze("select CONVERT_TZ('2021-01-32 00:00:00', '+08:00',
'America/London') = '2021-01-30'")
+ .rewrite()
+ .matches(
+ logicalResultSink(
+ logicalOneRowRelation().when(p ->
p.getProjects().get(0).child(0) instanceof NullLiteral)
+ )
+ );
+
+ PlanChecker.from(connectContext)
+ .analyze("select CONVERT_TZ('2021-01-32 00:00:00', '+08:00',
'America/London')")
+ .rewrite()
+ .matches(
+ logicalResultSink(
+ logicalOneRowRelation().when(p ->
p.getProjects().get(0).child(0) instanceof NullLiteral)
+ )
+ );
+
+ PlanChecker.from(connectContext)
+ .analyze("select CONVERT_TZ('2021-01-32 00:00:00.0000001',
'+08:00', 'America/London')")
+ .rewrite()
+ .matches(
+ logicalResultSink(
+ logicalOneRowRelation().when(p ->
p.getProjects().get(0).child(0) instanceof NullLiteral)
+ )
+ );
+
+ PlanChecker.from(connectContext)
+ .analyze("select CONVERT_TZ('2021-01-32 00:00:00.001',
'+08:00', 'America/London') = '2021-01-30'")
+ .rewrite()
+ .matches(
+ logicalResultSink(
+ logicalOneRowRelation().when(p ->
p.getProjects().get(0).child(0) instanceof NullLiteral)
+ )
+ );
+
+ Assertions.assertThrows(AnalysisException.class, () ->
PlanChecker.from(connectContext)
+ .analyze("select CAST('2021-01-32 00:00:00' AS DATETIME(6)) =
'2021-01-32 00:00:00'")
+ .rewrite()
+ );
+ Assertions.assertThrows(AnalysisException.class, () ->
PlanChecker.from(connectContext)
+ .analyze("select CAST('2021-01-32 00:00:00' AS DATETIME(6)) =
'2021-01-32 23:00:00'")
+ .rewrite()
+ );
+ Assertions.assertThrows(AnalysisException.class, () ->
PlanChecker.from(connectContext)
+ .analyze("select CAST('2021-01-32 00:00:00' AS DATETIME(6)) =
'1000'")
+ .rewrite()
+ );
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyComparisonPredicateTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyComparisonPredicateTest.java
index 898bc2b619d..6735e31192f 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyComparisonPredicateTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyComparisonPredicateTest.java
@@ -36,7 +36,6 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class SimplifyComparisonPredicateTest extends ExpressionRewriteTestHelper {
-
@Test
void testSimplifyComparisonPredicateRule() {
executor = new ExpressionRuleExecutor(
@@ -112,4 +111,19 @@ class SimplifyComparisonPredicateTest extends
ExpressionRewriteTestHelper {
rewrittenExpression = executor.rewrite(typeCoercion(expression),
context);
Assertions.assertEquals(left.getDataType(),
rewrittenExpression.child(0).getDataType());
}
+
+ @Test
+ void testRound() {
+ executor = new ExpressionRuleExecutor(
+ ImmutableList.of(SimplifyCastRule.INSTANCE,
SimplifyComparisonPredicate.INSTANCE));
+
+ Expression left = new Cast(new DateTimeLiteral("2021-01-02
00:00:00.00"), DateTimeV2Type.of(1));
+ Expression right = new DateTimeV2Literal("2021-01-01 23:59:59.99");
+ // (cast(2021-01-02 00:00:00.00 as DATETIMEV2(1)) > 2021-01-01
23:59:59.99)
+ Expression expression = new GreaterThan(left, right);
+ Expression rewrittenExpression =
executor.rewrite(typeCoercion(expression), context);
+
+ // right should round to be 2021-01-02 00:00:00.00
+ Assertions.assertEquals(new DateTimeV2Literal("2021-01-02 00:00:00"),
rewrittenExpression.child(1));
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralEqualTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralEqualTest.java
new file mode 100644
index 00000000000..84422510fd2
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralEqualTest.java
@@ -0,0 +1,45 @@
+// 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.
+
+package org.apache.doris.nereids.trees.expressions;
+
+import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class LiteralEqualTest {
+
+ @Test
+ void testEqual() {
+ IntegerLiteral one = new IntegerLiteral(1);
+ IntegerLiteral anotherOne = new IntegerLiteral(1);
+ IntegerLiteral two = new IntegerLiteral(2);
+ Assertions.assertNotEquals(one, two);
+ Assertions.assertEquals(one, anotherOne);
+ StringLiteral str1 = new StringLiteral("hello");
+ Assertions.assertNotEquals(str1, one);
+ Assertions.assertTrue(Literal.of("world") instanceof StringLiteral);
+ Assertions.assertTrue(Literal.of(null) instanceof NullLiteral);
+ Assertions.assertTrue(Literal.of(1) instanceof IntegerLiteral);
+ Assertions.assertTrue(Literal.of(false) instanceof BooleanLiteral);
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java
new file mode 100644
index 00000000000..a03010e583e
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java
@@ -0,0 +1,134 @@
+// 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.
+
+package org.apache.doris.nereids.trees.expressions.literal;
+
+import org.apache.doris.nereids.exceptions.AnalysisException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.function.Consumer;
+
+class DateLiteralTest {
+ @Test
+ void reject() {
+ // TODO: reject them.
+ // Now parse them as date + offset.
+ // PG parse them as date + offset, MySQL parse them as date + time
(rubbish behavior!)
+ // So strange! reject these strange case.
+ // Assertions.assertThrows(AnalysisException.class, () -> new
DateLiteral("2022-01-01-01"));
+ // Assertions.assertThrows(AnalysisException.class, () -> new
DateLiteral("2022-01-01-1"));
+ // Assertions.assertThrows(AnalysisException.class, () -> new
DateLiteral("2022-01-01+01"));
+ // Assertions.assertThrows(AnalysisException.class, () -> new
DateLiteral("2022-01-01+1"));
+ }
+
+ @Test
+ void testNormalize() {
+ String s = DateLiteral.normalize("2021-5");
+ Assertions.assertEquals("2021-05", s);
+ s = DateLiteral.normalize("2021-5-1");
+ Assertions.assertEquals("2021-05-01", s);
+ s = DateLiteral.normalize("2021-5-01");
+ Assertions.assertEquals("2021-05-01", s);
+
+ s = DateLiteral.normalize("2021-5-01 0:0:0");
+ Assertions.assertEquals("2021-05-01 00:00:00", s);
+ s = DateLiteral.normalize("2021-5-01 0:0:0.001");
+ Assertions.assertEquals("2021-05-01 00:00:00.001", s);
+
+ s = DateLiteral.normalize("2021-5-01 0:0:0.001+8:0");
+ Assertions.assertEquals("2021-05-01 00:00:00.001+08:00", s);
+ s = DateLiteral.normalize("2021-5-01 0:0:0.001+8:0:0");
+ Assertions.assertEquals("2021-05-01 00:00:00.001+08:00:00", s);
+
+ s = DateLiteral.normalize("2021-5-01 0:0:0.001UTC+8:0");
+ Assertions.assertEquals("2021-05-01 00:00:00.001UTC+08:00", s);
+ s = DateLiteral.normalize("2021-5-01 0:0:0.001UTC+8:0:0");
+ Assertions.assertEquals("2021-05-01 00:00:00.001UTC+08:00:00", s);
+
+ }
+
+ @Test
+ void testDate() {
+ new DateLiteral("220101");
+ new DateLiteral("22-01-01");
+ new DateLiteral("22-01-1");
+ new DateLiteral("22-1-1");
+
+ new DateLiteral("2022-01-01");
+ new DateLiteral("2022-01-1");
+ new DateLiteral("2022-1-1");
+ new DateLiteral("20220101");
+
+ Assertions.assertThrows(AnalysisException.class, () -> new
DateLiteral("-01-01"));
+ }
+
+ @Test
+ void testZone() {
+ new DateLiteral("2022-01-01Z");
+ new DateLiteral("2022-01-01UTC");
+ new DateLiteral("2022-01-01GMT");
+ new DateLiteral("2022-01-01UTC+08");
+ new DateLiteral("2022-01-01UTC-06");
+ new DateLiteral("2022-01-01UTC+08:00");
+ new DateLiteral("2022-01-01UTC-06:00");
+ new DateLiteral("2022-01-01Europe/London");
+ }
+
+ @Test
+ void testOffset() {
+ new DateLiteral("2022-01-01+01:00:00");
+ new DateLiteral("2022-01-01+01:00");
+ new DateLiteral("2022-01-01+01");
+ new DateLiteral("2022-01-01+1:0:0");
+ new DateLiteral("2022-01-01+1:0");
+ new DateLiteral("2022-01-01+1");
+
+ new DateLiteral("2022-01-01-01:00:00");
+ new DateLiteral("2022-01-01-01:00");
+ new DateLiteral("2022-01-01-1:0:0");
+ new DateLiteral("2022-01-01-1:0");
+ }
+
+ @Test
+ void testIrregularDate() {
+ Consumer<DateLiteral> assertFunc = (DateLiteral dateLiteral) -> {
+ Assertions.assertEquals("2016-07-02", dateLiteral.toString());
+ };
+ DateLiteral dateLiteral;
+
+ dateLiteral = new DateLiteral("2016-07-02");
+ assertFunc.accept(dateLiteral);
+
+ dateLiteral = new DateLiteral("2016-7-02");
+ assertFunc.accept(dateLiteral);
+ dateLiteral = new DateLiteral("2016-07-2");
+ assertFunc.accept(dateLiteral);
+ dateLiteral = new DateLiteral("2016-7-2");
+ assertFunc.accept(dateLiteral);
+
+ dateLiteral = new DateLiteral("2016-07-02");
+ assertFunc.accept(dateLiteral);
+ dateLiteral = new DateLiteral("2016-07-2");
+ assertFunc.accept(dateLiteral);
+ dateLiteral = new DateLiteral("2016-7-02");
+ assertFunc.accept(dateLiteral);
+ dateLiteral = new DateLiteral("2016-7-2");
+ assertFunc.accept(dateLiteral);
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteralTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteralTest.java
new file mode 100644
index 00000000000..950a3c953b9
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteralTest.java
@@ -0,0 +1,437 @@
+// 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.
+
+package org.apache.doris.nereids.trees.expressions.literal;
+
+import org.apache.doris.nereids.types.DateTimeV2Type;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.function.Consumer;
+
+class DateTimeLiteralTest {
+ @Test
+ void reject() {
+ // Assertions.assertThrows(IllegalArgumentException.class, () -> {
+ // new DateTimeV2Literal("2022-08-01T01:01:01-00:00");
+ // });
+ }
+
+ @Test
+ void testBasic() {
+ Consumer<DateTimeV2Literal> assertFunc = (datetime) -> {
+ Assertions.assertEquals(2022, datetime.year);
+ Assertions.assertEquals(8, datetime.month);
+ Assertions.assertEquals(1, datetime.day);
+ Assertions.assertEquals(1, datetime.hour);
+ Assertions.assertEquals(1, datetime.minute);
+ Assertions.assertEquals(1, datetime.second);
+ };
+
+ assertFunc.accept(new DateTimeV2Literal("20220801010101"));
+ assertFunc.accept(new DateTimeV2Literal("20220801T010101"));
+ assertFunc.accept(new DateTimeV2Literal("220801010101"));
+ assertFunc.accept(new DateTimeV2Literal("220801T010101"));
+ }
+
+ @Test
+ void testMicrosecond() {
+ DateTimeV2Literal literal;
+ literal = new DateTimeV2Literal("2016-07-02 00:00:00.123");
+ Assertions.assertEquals(123000, literal.microSecond);
+ literal = new DateTimeV2Literal("2016-07-02 00:00:00.123456");
+ Assertions.assertEquals(123456, literal.microSecond);
+ literal = new DateTimeV2Literal("2016-07-02 00:00:00.1");
+ Assertions.assertEquals(100000, literal.microSecond);
+ literal = new DateTimeV2Literal("2016-07-02 00:00:00.000001");
+ Assertions.assertEquals(1, literal.microSecond);
+ literal = new DateTimeV2Literal("2016-07-02 00:00:00.12345");
+ Assertions.assertEquals(123450, literal.microSecond);
+ }
+
+ @Test
+ void testWithoutZoneOrOffset() {
+ new DateTimeV2Literal("2022-08-01");
+
+ new DateTimeV2Literal("2022-08-01 01:01:01");
+ new DateTimeV2Literal("2022-08-01 01:01");
+ new DateTimeV2Literal("2022-08-01 01");
+
+ new DateTimeV2Literal("2022-08-01T01:01:01");
+ new DateTimeV2Literal("2022-08-01T01:01");
+ new DateTimeV2Literal("2022-08-01T01");
+
+ new DateTimeV2Literal("22-08-01T01:01:01");
+ new DateTimeV2Literal("22-08-01T01:01");
+ new DateTimeV2Literal("22-08-01T01");
+ }
+
+ @Test
+ void testDetermineScale() {
+ int scale = DateTimeLiteral.determineScale("2022-08-01T01:01:01.0");
+ Assertions.assertEquals(0, scale);
+ scale = DateTimeLiteral.determineScale("2022-08-01T01:01:01.00000");
+ Assertions.assertEquals(0, scale);
+ scale = DateTimeLiteral.determineScale("2022-08-01T01:01:01.000001");
+ Assertions.assertEquals(6, scale);
+ scale = DateTimeLiteral.determineScale("2022-08-01T01:01:01.123456");
+ Assertions.assertEquals(6, scale);
+ scale = DateTimeLiteral.determineScale("2022-08-01T01:01:01.0001");
+ Assertions.assertEquals(4, scale);
+ scale = DateTimeLiteral.determineScale("2022-08-01T01:01:01.00010");
+ Assertions.assertEquals(4, scale);
+ scale = DateTimeLiteral.determineScale("2022-08-01T01:01:01.12010");
+ Assertions.assertEquals(4, scale);
+ scale = DateTimeLiteral.determineScale("2022-08-01T01:01:01.02010");
+ Assertions.assertEquals(4, scale);
+ }
+
+ @Test
+ void testTwoDigitYear() {
+ new DateTimeV2Literal("22-08-01T01");
+ new DateTimeV2Literal("22-08-01 01");
+ new DateTimeV2Literal("22-08-01T01:01");
+ new DateTimeV2Literal("22-08-01 01:01");
+ new DateTimeV2Literal("22-08-01T01:01:01");
+ new DateTimeV2Literal("22-08-01 01:01:01");
+ new DateTimeV2Literal("22-08-01T01");
+ new DateTimeV2Literal("22-08-01 01");
+ new DateTimeV2Literal("22-08-01T01:01");
+ new DateTimeV2Literal("22-08-01 01:01");
+ new DateTimeV2Literal("22-08-01T01:01:01");
+ new DateTimeV2Literal("22-08-01 01:01:01");
+ }
+
+ @Test
+ void testZone() {
+ new DateTimeV2Literal("2022-08-01 01:01:01UTC");
+ new DateTimeV2Literal("2022-08-01 01:01:01UT");
+ new DateTimeV2Literal("2022-08-01 01:01:01GMT");
+ new DateTimeV2Literal("2022-08-01 01:01:01Z");
+ new DateTimeV2Literal("2022-08-01 01:01:01Europe/London");
+ new DateTimeV2Literal("2022-08-01 01:01:01America/New_York");
+ new DateTimeV2Literal("2022-08-01 01:01:01Z");
+ new DateTimeV2Literal("2022-08-01 01:01:01Europe/Berlin");
+ new DateTimeV2Literal("2022-08-01 01:01:01Europe/London");
+ }
+
+ @Test
+ void testZoneOrOffsetRight() {
+ java.util.function.BiConsumer<DateTimeV2Literal, Long> assertHour =
(dateTimeV2Literal, expectHour) -> {
+ Assertions.assertEquals(dateTimeV2Literal.hour, expectHour);
+ };
+ DateTimeV2Literal dateTimeV2Literal;
+ dateTimeV2Literal = new DateTimeV2Literal("2022-08-01
00:00:00Europe/London"); // +01:00
+ assertHour.accept(dateTimeV2Literal, 7L);
+ dateTimeV2Literal = new DateTimeV2Literal("2022-08-01
00:00:00America/New_York"); // -04:00
+ assertHour.accept(dateTimeV2Literal, 12L);
+ dateTimeV2Literal = new DateTimeV2Literal("2022-08-01
00:00:00Asia/Shanghai");
+ assertHour.accept(dateTimeV2Literal, 0L);
+ dateTimeV2Literal = new DateTimeV2Literal("2022-08-01 00:00:00+01:00");
+ assertHour.accept(dateTimeV2Literal, 7L);
+ dateTimeV2Literal = new DateTimeV2Literal("2022-08-01 00:00:00-01:00");
+ assertHour.accept(dateTimeV2Literal, 9L);
+ }
+
+ @Test
+ void testTwoDigitalYearZone() {
+ new DateTimeV2Literal("22-08-01 01:01:01UTC");
+ new DateTimeV2Literal("22-08-01 01:01:01UT");
+ new DateTimeV2Literal("22-08-01 01:01:01GMT");
+ new DateTimeV2Literal("22-08-01 01:01:01Z");
+ new DateTimeV2Literal("22-08-01 01:01:01Europe/London");
+ new DateTimeV2Literal("22-08-01 01:01:01UTC");
+ new DateTimeV2Literal("22-08-01 01:01:01America/New_York");
+ new DateTimeV2Literal("22-08-01 01:01:01Z");
+ new DateTimeV2Literal("22-08-01 01:01:01Europe/Berlin");
+ new DateTimeV2Literal("22-08-01 01:01:01Europe/London");
+ }
+
+ @Test
+ void testZoneOffset() {
+ new DateTimeV2Literal("2022-08-01 01:01:01UTC+01:01:01");
+ new DateTimeV2Literal("2022-08-01 01:01:01UTC+1:1:1");
+
+ new DateTimeV2Literal("2022-08-01 01:01:01UTC+01:01");
+
+ new DateTimeV2Literal("2022-08-01 01:01:01UTC+01");
+ new DateTimeV2Literal("2022-08-01 01:01:01UTC+1");
+ }
+
+ @Test
+ void testTwoDigitalYearZoneOffset() {
+ new DateTimeV2Literal("22-08-01 01:01:01UTC+01:01:01");
+ new DateTimeV2Literal("22-08-01 01:01:01UTC+1:1:1");
+
+ new DateTimeV2Literal("22-08-01 01:01:01UTC+01:01");
+
+ new DateTimeV2Literal("22-08-01 01:01:01UTC+01");
+ new DateTimeV2Literal("22-08-01 01:01:01UTC+1");
+ }
+
+ @Test
+ void testOffset() {
+ new DateTimeV2Literal("2022-08-01 01:01:01+01:01:01");
+ new DateTimeV2Literal("2022-08-01 01:01:01+01:01");
+ new DateTimeV2Literal("2022-08-01 01:01:01+01");
+ new DateTimeV2Literal("2022-08-01 01:01:01+01:1:01");
+ new DateTimeV2Literal("2022-08-01 01:01:01+01:1");
+ new DateTimeV2Literal("2022-08-01 01:01:01+01:01:1");
+ new DateTimeV2Literal("2022-08-01 01:01:01+1:1:1");
+ new DateTimeV2Literal("2022-08-01 01:01:01+1:1");
+ new DateTimeV2Literal("2022-08-01 01:01:01+1");
+
+ new DateTimeV2Literal("2022-05-01 01:02:55+02:30");
+ new DateTimeV2Literal("2022-05-01 01:02:55.123-02:30");
+ new DateTimeV2Literal("2022-06-01T01:02:55+04:30");
+ new DateTimeV2Literal("2022-06-01 01:02:55.123-07:30");
+ new DateTimeV2Literal("2022-05-01 01:02:55+02:30");
+
+ new DateTimeV2Literal("2022-05-01 01:02:55.123-02:30");
+ new DateTimeV2Literal("2022-06-01T01:02:55+04:30");
+ new DateTimeV2Literal("2022-06-01 01:02:55.123-07:30");
+ // new DateTimeV2Literal("20220701010255+07:00");
+ // new DateTimeV2Literal("20220701010255-05:00");
+ }
+
+ @Test
+ void testDateTime() {
+ new DateTimeV2Literal("2022-08-01 01:01:01UTC+1:1:1");
+ new DateTimeV2Literal("2022-08-01 01:01:01UTC+1:1");
+ new DateTimeV2Literal("2022-08-01 01:01:01UTC+1");
+
+ new DateTimeV2Literal("0001-01-01 00:01:01");
+ new DateTimeV2Literal("0001-01-01 00:01:01.001");
+ new DateTimeV2Literal("0001-01-01 00:01:01.00305");
+
+ new DateTimeV2Literal("2022-01-01 01:02:55");
+ new DateTimeV2Literal("2022-01-01 01:02:55.123");
+ new DateTimeV2Literal("2022-02-01 01:02:55Z");
+ new DateTimeV2Literal("2022-02-01 01:02:55.123Z");
+ new DateTimeV2Literal("2022-03-01 01:02:55UTC+8");
+ new DateTimeV2Literal("2022-03-01 01:02:55.123UTC");
+ new DateTimeV2Literal("2022-04-01 01:02:55UTC-6");
+ new DateTimeV2Literal("2022-04-01T01:02:55UTC-6");
+ new DateTimeV2Literal("2022-04-01T01:02:55.123UTC+6");
+
+ new DateTimeV2Literal("2022-01-01 01:02:55");
+ new DateTimeV2Literal("2022-01-01 01:02:55.123");
+ new DateTimeV2Literal("2022-02-01 01:02:55Z");
+ new DateTimeV2Literal("2022-02-01 01:02:55.123Z");
+ new DateTimeV2Literal("2022-03-01 01:02:55UTC+8");
+ new DateTimeV2Literal("2022-03-01 01:02:55.123UTC");
+ new DateTimeV2Literal("2022-04-01T01:02:55UTC-6");
+ new DateTimeV2Literal("2022-04-01T01:02:55.123UTC+6");
+
+ new DateTimeV2Literal("0001-01-01");
+ // new DateTimeV2Literal("20220801GMT+5");
+ // new DateTimeV2Literal("20220801GMT-3");
+ }
+
+ @Test
+ void testIrregularDateTime() {
+ new DateLiteral("2016-07-02 01:01:00");
+
+ new DateLiteral("2016-7-02 01:01:00");
+ new DateLiteral("2016-07-2 01:01:00");
+ new DateLiteral("2016-7-2 01:01:00");
+
+ new DateLiteral("2016-07-02 1:01:00");
+ new DateLiteral("2016-07-02 01:1:00");
+ new DateLiteral("2016-07-02 01:01:0");
+ new DateLiteral("2016-07-02 1:1:00");
+ new DateLiteral("2016-07-02 1:01:0");
+ new DateLiteral("2016-07-02 10:1:0");
+ new DateLiteral("2016-07-02 1:1:0");
+
+ new DateLiteral("2016-7-2 1:1:0");
+ new DateLiteral("2016-7-02 1:01:0");
+ new DateLiteral("2016-07-2 1:1:0");
+ new DateLiteral("2016-7-02 01:01:0");
+ new DateLiteral("2016-7-2 01:1:0");
+ }
+
+ @Test
+ void testIrregularDateTimeHour() {
+ new DateTimeV2Literal("2016-07-02 01");
+ new DateTimeV2Literal("2016-07-02 1");
+
+ new DateTimeV2Literal("2016-7-02 1");
+ new DateTimeV2Literal("2016-7-02 01");
+
+ new DateTimeV2Literal("2016-07-2 1");
+ new DateTimeV2Literal("2016-07-2 01");
+
+ new DateTimeV2Literal("2016-7-2 1");
+ new DateTimeV2Literal("2016-7-2 01");
+ }
+
+ @Test
+ void testIrregularDateTimeHourMinute() {
+ new DateTimeV2Literal("2016-07-02 01:01");
+ new DateTimeV2Literal("2016-07-02 1:01");
+ new DateTimeV2Literal("2016-07-02 01:1");
+ new DateTimeV2Literal("2016-07-02 1:1");
+
+ new DateTimeV2Literal("2016-7-02 01:01");
+ new DateTimeV2Literal("2016-7-02 1:01");
+ new DateTimeV2Literal("2016-7-02 01:1");
+ new DateTimeV2Literal("2016-7-02 1:1");
+
+ new DateTimeV2Literal("2016-07-2 01:01");
+ new DateTimeV2Literal("2016-07-2 1:01");
+ new DateTimeV2Literal("2016-07-2 01:1");
+ new DateTimeV2Literal("2016-07-2 1:1");
+
+ new DateTimeV2Literal("2016-7-2 01:01");
+ new DateTimeV2Literal("2016-7-2 1:01");
+ new DateTimeV2Literal("2016-7-2 01:1");
+ new DateTimeV2Literal("2016-7-2 1:1");
+ }
+
+ @Test
+ void testIrregularDateTimeHourMinuteSecond() {
+ new DateTimeV2Literal("2016-07-02 01:01:01");
+ new DateTimeV2Literal("2016-07-02 1:01:01");
+ new DateTimeV2Literal("2016-07-02 01:1:01");
+ new DateTimeV2Literal("2016-07-02 1:1:01");
+ new DateTimeV2Literal("2016-07-02 01:01:1");
+ new DateTimeV2Literal("2016-07-02 1:01:1");
+ new DateTimeV2Literal("2016-07-02 01:1:1");
+ new DateTimeV2Literal("2016-07-02 1:1:1");
+
+ new DateTimeV2Literal("2016-7-02 01:01:01");
+ new DateTimeV2Literal("2016-7-02 1:01:01");
+ new DateTimeV2Literal("2016-7-02 01:1:01");
+ new DateTimeV2Literal("2016-7-02 1:1:01");
+ new DateTimeV2Literal("2016-7-02 01:01:1");
+ new DateTimeV2Literal("2016-7-02 1:01:1");
+ new DateTimeV2Literal("2016-7-02 01:1:1");
+ new DateTimeV2Literal("2016-7-02 1:1:1");
+
+ new DateTimeV2Literal("2016-07-2 01:01:01");
+ new DateTimeV2Literal("2016-07-2 1:01:01");
+ new DateTimeV2Literal("2016-07-2 01:1:01");
+ new DateTimeV2Literal("2016-07-2 1:1:01");
+ new DateTimeV2Literal("2016-07-2 01:01:1");
+ new DateTimeV2Literal("2016-07-2 1:01:1");
+ new DateTimeV2Literal("2016-07-2 01:1:1");
+ new DateTimeV2Literal("2016-07-2 1:1:1");
+
+ new DateTimeV2Literal("2016-7-2 01:01:01");
+ new DateTimeV2Literal("2016-7-2 1:01:01");
+ new DateTimeV2Literal("2016-7-2 01:1:01");
+ new DateTimeV2Literal("2016-7-2 1:1:01");
+ new DateTimeV2Literal("2016-7-2 01:01:1");
+ new DateTimeV2Literal("2016-7-2 1:01:1");
+ new DateTimeV2Literal("2016-7-2 01:1:1");
+ new DateTimeV2Literal("2016-7-2 1:1:1");
+ }
+
+ @Test
+ void testIrregularDateTimeHourMinuteSecondMicrosecond() {
+ new DateTimeV2Literal("2016-07-02 01:01:01.1");
+ new DateTimeV2Literal("2016-07-02 1:01:01.1");
+ new DateTimeV2Literal("2016-07-02 01:1:01.1");
+ new DateTimeV2Literal("2016-07-02 1:1:01.1");
+ new DateTimeV2Literal("2016-07-02 01:01:1.1");
+ new DateTimeV2Literal("2016-07-02 1:01:1.1");
+ new DateTimeV2Literal("2016-07-02 01:1:1.1");
+ new DateTimeV2Literal("2016-07-02 1:1:1.1");
+
+ new DateTimeV2Literal("2016-7-02 01:01:01.1");
+ new DateTimeV2Literal("2016-7-02 1:01:01.1");
+ new DateTimeV2Literal("2016-7-02 01:1:01.1");
+ new DateTimeV2Literal("2016-7-02 1:1:01.1");
+ new DateTimeV2Literal("2016-7-02 01:01:1.1");
+ new DateTimeV2Literal("2016-7-02 1:01:1.1");
+ new DateTimeV2Literal("2016-7-02 01:1:1.1");
+ new DateTimeV2Literal("2016-7-02 1:1:1.1");
+
+ new DateTimeV2Literal("2016-07-2 01:01:01.1");
+ new DateTimeV2Literal("2016-07-2 1:01:01.1");
+ new DateTimeV2Literal("2016-07-2 01:1:01.1");
+ new DateTimeV2Literal("2016-07-2 1:1:01.1");
+ new DateTimeV2Literal("2016-07-2 01:01:1.1");
+ new DateTimeV2Literal("2016-07-2 1:01:1.1");
+ new DateTimeV2Literal("2016-07-2 01:1:1.1");
+ new DateTimeV2Literal("2016-07-2 1:1:1.1");
+
+ new DateTimeV2Literal("2016-7-2 01:01:01.1");
+ new DateTimeV2Literal("2016-7-2 1:01:01.1");
+ new DateTimeV2Literal("2016-7-2 01:1:01.1");
+ new DateTimeV2Literal("2016-7-2 1:1:01.1");
+ new DateTimeV2Literal("2016-7-2 01:01:1.1");
+ new DateTimeV2Literal("2016-7-2 1:01:1.1");
+ new DateTimeV2Literal("2016-7-2 01:1:1.1");
+ new DateTimeV2Literal("2016-7-2 1:1:1.1");
+
+ // Testing with microsecond of length 2
+ new DateTimeV2Literal("2016-07-02 01:01:01.12");
+ new DateTimeV2Literal("2016-7-02 01:01:01.12");
+
+ // Testing with microsecond of length 3
+ new DateTimeV2Literal("2016-07-02 01:01:01.123");
+ new DateTimeV2Literal("2016-7-02 01:01:01.123");
+
+ // Testing with microsecond of length 4
+ new DateTimeV2Literal("2016-07-02 01:01:01.1234");
+ new DateTimeV2Literal("2016-7-02 01:01:01.1234");
+
+ // Testing with microsecond of length 5
+ new DateTimeV2Literal("2016-07-02 01:01:01.12345");
+ new DateTimeV2Literal("2016-7-02 01:01:01.12345");
+
+ // Testing with microsecond of length 6
+ new DateTimeV2Literal("2016-07-02 01:01:01.123456");
+ new DateTimeV2Literal("2016-7-02 01:01:01.123456");
+ }
+
+ @Test
+ void testDateTimeV2Scale() {
+ Assertions.assertEquals(
+ new DateTimeV2Literal(DateTimeV2Type.of(3), "2016-07-02
00:00:00.123"),
+ new DateTimeV2Literal("2016-07-02 00:00:00.123"));
+
+ Assertions.assertEquals(
+ new DateTimeV2Literal(DateTimeV2Type.of(3), "2016-07-02
00:00:00.123456"),
+ new DateTimeV2Literal("2016-07-02 00:00:00.123"));
+
+ Assertions.assertEquals(
+ new DateTimeV2Literal(DateTimeV2Type.of(4), "2016-07-02
00:00:00.12345"),
+ new DateTimeV2Literal("2016-07-02 00:00:00.12345"));
+
+ Assertions.assertEquals(
+ new DateTimeV2Literal(DateTimeV2Type.of(0), "2016-07-02
00:00:00.12345"),
+ new DateTimeV2Literal("2016-07-02 00:00:00.0"));
+
+ Assertions.assertEquals(
+ new DateTimeV2Literal(DateTimeV2Type.of(0), "2016-07-02
00:00:00.5123"),
+ new DateTimeV2Literal("2016-07-02 00:00:01.0"));
+
+ Assertions.assertEquals(
+ new DateTimeV2Literal(DateTimeV2Type.of(5), "2016-07-02
00:00:00.999999"),
+ new DateTimeV2Literal("2016-07-02 00:00:01.0"));
+
+ // test overflow
+ Assertions.assertEquals(
+ new DateTimeV2Literal(DateTimeV2Type.of(5), "2016-12-31
23:59:59.999999"),
+ new DateTimeV2Literal("2017-01-01 00:00:00.0"));
+ }
+}
+
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/types/AbstractDataTypeTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/AbstractDataTypeTest.java
index 776b80ec47c..3438f168a52 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/types/AbstractDataTypeTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/AbstractDataTypeTest.java
@@ -352,8 +352,8 @@ public class AbstractDataTypeTest {
int scale = Math.min(precision, Math.abs(new Random().nextInt() %
DecimalV2Type.MAX_SCALE));
Assertions.assertFalse(dataType.acceptsType(new
DecimalV2Type(precision, scale)));
Assertions.assertTrue(dataType.acceptsType(new CharType(new
Random().nextInt())));
- Assertions.assertFalse(dataType.acceptsType(new VarcharType(new
Random().nextInt())));
- Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE));
+ Assertions.assertTrue(dataType.acceptsType(new VarcharType(new
Random().nextInt())));
+ Assertions.assertTrue(dataType.acceptsType(StringType.INSTANCE));
Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE));
Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE));
}
@@ -373,7 +373,7 @@ public class AbstractDataTypeTest {
int precision = Math.abs(new Random().nextInt() %
(DecimalV2Type.MAX_PRECISION - 1)) + 1;
int scale = Math.min(precision, Math.abs(new Random().nextInt() %
DecimalV2Type.MAX_SCALE));
Assertions.assertFalse(dataType.acceptsType(new
DecimalV2Type(precision, scale)));
- Assertions.assertFalse(dataType.acceptsType(new CharType(new
Random().nextInt())));
+ Assertions.assertTrue(dataType.acceptsType(new CharType(new
Random().nextInt())));
Assertions.assertTrue(dataType.acceptsType(new VarcharType(new
Random().nextInt())));
Assertions.assertTrue(dataType.acceptsType(StringType.INSTANCE));
Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE));
@@ -395,7 +395,7 @@ public class AbstractDataTypeTest {
int precision = Math.abs(new Random().nextInt() %
(DecimalV2Type.MAX_PRECISION - 1)) + 1;
int scale = Math.min(precision, Math.abs(new Random().nextInt() %
DecimalV2Type.MAX_SCALE));
Assertions.assertFalse(dataType.acceptsType(new
DecimalV2Type(precision, scale)));
- Assertions.assertFalse(dataType.acceptsType(new CharType(new
Random().nextInt())));
+ Assertions.assertTrue(dataType.acceptsType(new CharType(new
Random().nextInt())));
Assertions.assertTrue(dataType.acceptsType(new VarcharType(new
Random().nextInt())));
Assertions.assertTrue(dataType.acceptsType(StringType.INSTANCE));
Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE));
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/DateTimeFormatterUtilsTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/DateTimeFormatterUtilsTest.java
index c40f5c4a85f..853c8e11b41 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/DateTimeFormatterUtilsTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/DateTimeFormatterUtilsTest.java
@@ -24,8 +24,23 @@ import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
+import java.util.function.Consumer;
class DateTimeFormatterUtilsTest {
+ @Test
+ void test() {
+ DateTimeFormatter formatter = DateTimeFormatterUtils.ZONE_FORMATTER;
+
+ formatter.parse("");
+
+ formatter.parse("UTC+01:00");
+ formatter.parse("UTC+01:00:00");
+
+ formatter.parse("GMT+01:00");
+ formatter.parse("Asia/Shanghai");
+ formatter.parse("Z");
+ }
+
private void assertDatePart(TemporalAccessor dateTime) {
Assertions.assertEquals(2020, dateTime.get(ChronoField.YEAR));
Assertions.assertEquals(2, dateTime.get(ChronoField.MONTH_OF_YEAR));
@@ -34,49 +49,66 @@ class DateTimeFormatterUtilsTest {
@Test
void testBasicDateTimeFormatter() {
- DateTimeFormatter formatter =
DateTimeFormatterUtils.BASIC_DATE_TIME_FORMATTER;
+ DateTimeFormatter formatter =
DateTimeFormatterUtils.BASIC_FORMATTER_WITHOUT_T;
TemporalAccessor dateTime = formatter.parse("20200219");
assertDatePart(dateTime);
dateTime = formatter.parse("20200219010101");
assertDatePart(dateTime);
- dateTime = formatter.parse("20200219T010101");
+ dateTime = formatter.parse("20200219010101.1");
assertDatePart(dateTime);
- // failed case
- Assertions.assertThrows(DateTimeParseException.class, () ->
formatter.parse("20200219 010101"));
-
- // microsecond
dateTime = formatter.parse("20200219010101.000001");
assertDatePart(dateTime);
- dateTime = formatter.parse("20200219T010101.000001");
- assertDatePart(dateTime);
dateTime = formatter.parse("20200219010101.1");
assertDatePart(dateTime);
+
+ formatter = DateTimeFormatterUtils.BASIC_DATE_TIME_FORMATTER;
+ dateTime = formatter.parse("20200219T010101");
+ assertDatePart(dateTime);
dateTime = formatter.parse("20200219T010101.1");
assertDatePart(dateTime);
- Assertions.assertThrows(DateTimeParseException.class, () ->
formatter.parse("20200219010101."));
- Assertions.assertThrows(DateTimeParseException.class, () ->
formatter.parse("20200219010101.0000001"));
- Assertions.assertThrows(DateTimeParseException.class, () ->
formatter.parse("20200219T010101."));
- Assertions.assertThrows(DateTimeParseException.class, () ->
formatter.parse("20200219T010101.0000001"));
+ dateTime = formatter.parse("20200219T010101.000001");
+ assertDatePart(dateTime);
+ dateTime = formatter.parse("20200219T010101.1");
+ assertDatePart(dateTime);
+
+ // failed case
+ DateTimeFormatter withT =
DateTimeFormatterUtils.BASIC_DATE_TIME_FORMATTER;
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withT.parse("20200219 010101"));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withT.parse("20200219010101."));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withT.parse("20200219010101.0000001"));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withT.parse("20200219T010101."));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withT.parse("20200219T010101.0000001"));
+ DateTimeFormatter withoutT =
DateTimeFormatterUtils.BASIC_FORMATTER_WITHOUT_T;
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withoutT.parse("20200219 010101"));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withoutT.parse("20200219010101."));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withoutT.parse("20200219010101.0000001"));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withoutT.parse("20200219T010101."));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
withoutT.parse("20200219T010101.0000001"));
+ }
+
+ @Test
+ void testDateTimeFormatter() {
+ DateTimeFormatter formatter =
DateTimeFormatterUtils.DATE_TIME_FORMATTER;
+ TemporalAccessor dateTime = formatter.parse("2020-02-19 01:01:01");
+ assertDatePart(dateTime);
+ Assertions.assertThrows(DateTimeParseException.class, () ->
formatter.parse("2020-02-19T01:01:01"));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
formatter.parse("2020-02-1901:01:01"));
}
@Test
- void testTwoDigitalDate() {
- DateTimeFormatter formatter = DateTimeFormatterUtils.DATE_FORMATTER;
- // Year values in the range 00-69 become 2000-2069.
- // Year values in the range 70-99 become 1970-199
- for (int i = 0; i < 100; i++) {
- String str;
- if (i < 10) {
- str = "0" + i + "-02-19";
- } else {
- str = i + "-02-19";
- }
- TemporalAccessor dateTime = formatter.parse(str);
- if (i < 70) {
- Assertions.assertEquals(2000 + i,
dateTime.get(ChronoField.YEAR));
- } else {
- Assertions.assertEquals(1900 + i,
dateTime.get(ChronoField.YEAR));
- }
- }
+ void testTimeFormatter() {
+ // use lambda function to assert time is correct.
+ Consumer<TemporalAccessor> assertTime = (dateTime) ->
+ Assertions.assertEquals(1,
dateTime.get(ChronoField.HOUR_OF_DAY));
+
+ DateTimeFormatter timeFormatter =
DateTimeFormatterUtils.TIME_FORMATTER;
+ TemporalAccessor dateTime = timeFormatter.parse("01:01:01.000001");
+ assertTime.accept(dateTime);
+ dateTime = timeFormatter.parse("01:01:01.1");
+ assertTime.accept(dateTime);
+ dateTime = timeFormatter.parse("01:01:01");
+ assertTime.accept(dateTime);
+ Assertions.assertThrows(DateTimeParseException.class, () ->
timeFormatter.parse("01:01"));
+ Assertions.assertThrows(DateTimeParseException.class, () ->
timeFormatter.parse("01"));
}
}
diff --git a/regression-test/data/correctness/test_time_diff_microseconds.out
b/regression-test/data/correctness/test_time_diff_microseconds.out
index ce4c6c10f51..dbeeb067f26 100644
--- a/regression-test/data/correctness/test_time_diff_microseconds.out
+++ b/regression-test/data/correctness/test_time_diff_microseconds.out
@@ -25,5 +25,5 @@
48:00:00.114514
-- !select8 --
-48:00:00.11400
+48:00:00.11500
diff --git
a/regression-test/data/query_p0/sql_functions/array_functions/test_array_with_scale_type.out
b/regression-test/data/query_p0/sql_functions/array_functions/test_array_with_scale_type.out
index b887cdf11bc..c20f9977c3e 100644
---
a/regression-test/data/query_p0/sql_functions/array_functions/test_array_with_scale_type.out
+++
b/regression-test/data/query_p0/sql_functions/array_functions/test_array_with_scale_type.out
@@ -3,7 +3,7 @@
2022-12-02T22:23:24.999999
-- !select --
-2022-12-02T22:23:24.999
+2022-12-02T22:23:25
-- !select --
2022-12-01T22:23:24.999
@@ -13,7 +13,7 @@
2022-12-02T22:23:24.999999
-- !select --
-2022-12-02T22:23:24.999
+2022-12-02T22:23:25
-- !select --
2022-12-01T23:23:24.999
@@ -80,11 +80,11 @@
[2022-12-02 22:23:24.999]
-- !select --
-[2022-12-02 22:23:24.999, 2022-12-02 22:23:23.997]
-[2022-12-02 22:23:24.999, 2022-12-02 22:23:23.997]
+[2022-12-02 22:23:25.000, 2022-12-02 22:23:23.998]
+[2022-12-02 22:23:25.000, 2022-12-02 22:23:23.998]
-- !select --
-[2022-12-02 22:23:24.999, 2022-12-02 22:23:23.997]
+[2022-12-02 22:23:25, 2022-12-02 22:23:23.998]
-- !select --
[]
diff --git
a/regression-test/data/query_p0/sql_functions/cast_function/test_cast_with_scale_type.out
b/regression-test/data/query_p0/sql_functions/cast_function/test_cast_with_scale_type.out
index 4fc3d0d1f1d..876b45e501a 100644
---
a/regression-test/data/query_p0/sql_functions/cast_function/test_cast_with_scale_type.out
+++
b/regression-test/data/query_p0/sql_functions/cast_function/test_cast_with_scale_type.out
@@ -8,9 +8,9 @@
2 2022-12-02T22:23:24.999 2022-12-02T22:23:25
-- !select3 --
-2022-12-02T22:23:24.999 2022-12-02T22:23:23.999
-2022-12-02T22:23:24.999 2022-12-02T22:23:23.999
+2022-12-02T22:23:25 2022-12-02T22:23:24
+2022-12-02T22:23:25 2022-12-02T22:23:24
-- !select4 --
-2022-12-02T22:23:24.999 2022-12-02T22:23:23.999
+2022-12-02T22:23:25 2022-12-02T22:23:24
diff --git
a/regression-test/suites/nereids_syntax_p0/test_simplify_comparison.groovy
b/regression-test/suites/nereids_syntax_p0/test_simplify_comparison.groovy
index 4b3cd3bdca1..9a0a2c5bf59 100644
--- a/regression-test/suites/nereids_syntax_p0/test_simplify_comparison.groovy
+++ b/regression-test/suites/nereids_syntax_p0/test_simplify_comparison.groovy
@@ -19,57 +19,6 @@ suite("test_simplify_comparison") {
sql "set enable_nereids_planner=true"
sql 'set enable_fallback_to_original_planner=false;'
sql 'drop table if exists log_items_test'
- sql """CREATE TABLE IF NOT EXISTS `log_items_test` (
- a DATETIME NOT NULL,
- b decimal(10,2)
- ) ENGINE=OLAP
- UNIQUE KEY (`a`)
- DISTRIBUTED BY HASH(`a`) BUCKETS 120
- PROPERTIES (
- "replication_num" = "1",
- "in_memory" = "false",
- "compression" = "LZ4",
- "storage_cooldown_time" = "9999-12-31 23:59:59",
- "enable_unique_key_merge_on_write" = "true"
- );"""
- sql """insert into log_items_test values( "2023-06-06", 111.11 );"""
-
- explain {
- sql "verbose select * from log_items_test where a < '2023-06-15
23:59:59.999' and b < 111.111;"
- notContains "CAST"
- contains "< 111.12"
- contains "< '2023-06-16 00:00:00'"
- }
-
- explain {
- sql "verbose select * from log_items_test where a <= '2023-06-15
23:59:59.999' and b <= 111.111;"
- notContains "CAST"
- contains "<= 111.11"
- contains "<= '2023-06-15 23:59:59'"
- }
-
- explain {
- sql "verbose select * from log_items_test where a = '2023-06-15
23:59:59.999' and b = 111.111;"
- notContains "CAST"
- notContains "111.12"
- notContains "2023-06-16 00:00:00"
- notContains "111.11"
- notContains "2023-06-15 23:59:59"
- }
-
- explain {
- sql "verbose select * from log_items_test where a > '2023-06-15
23:59:59.999' and b > 111.111;"
- notContains "CAST"
- contains "> 111.11"
- contains "> '2023-06-15 23:59:59'"
- }
-
- explain {
- sql "verbose select * from log_items_test where a >= '2023-06-15
23:59:59.999' and b >= 111.111;"
- notContains "CAST"
- contains ">= 111.12"
- contains ">= '2023-06-16 00:00:00'"
- }
sql "select cast('1234' as decimalv3(18,4)) > 2000;"
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]