[refactor](Nereids): new Date/Datetime parser to support more condition (#24224)
* unify all Date/Datetime use one string-parser * support microsecond & ZoneOffset both exist * add many UT case * add determineScale() to get scale of datetime, original code just get length of part after . * reject more bad condition like 2022-01-01 00:00:00., we don't allow . without microsecond. * .....
This commit is contained in:
@ -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,72 @@ public class DateLiteral extends Literal {
|
||||
this.day = other.day;
|
||||
}
|
||||
|
||||
protected void init(String s) throws AnalysisException {
|
||||
// replace 'T' with ' '
|
||||
private static String replaceDelimiterT(String s) {
|
||||
// Matcher matcher = Pattern.compile("^(\\d{2,4}-\\d{1,2}-\\d{1,2})T").matcher(s);
|
||||
// if (matcher.find()) {
|
||||
// return matcher.group(1) + " " + s.substring(matcher.end());
|
||||
// }
|
||||
// return s;
|
||||
if (s.length() <= 10) {
|
||||
return s;
|
||||
}
|
||||
if (s.charAt(10) == 'T') {
|
||||
return s.substring(0, 10) + " " + s.substring(11);
|
||||
} else if (s.charAt(8) == 'T') {
|
||||
return s.substring(0, 8) + " " + s.substring(9);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
dateTime = DATE_FORMATTER.parse(s);
|
||||
// mysql reject "20200219 010101" "200219 010101", can't use ' ' spilt basic date time.
|
||||
if (!s.contains("T")) {
|
||||
if (s.length() == 6) {
|
||||
dateTime = DateTimeFormatterUtils.BASIC_TWO_DIGIT_DATE_FORMATTER.parse(s);
|
||||
} else {
|
||||
dateTime = DateTimeFormatterUtils.BASIC_FORMATTER_WITHOUT_T.parse(s);
|
||||
}
|
||||
} else {
|
||||
dateTime = DateTimeFormatterUtils.BASIC_DATE_TIME_FORMATTER.parse(s);
|
||||
}
|
||||
return dateTime;
|
||||
}
|
||||
year = DateUtils.getOrDefault(dateTime, ChronoField.YEAR);
|
||||
month = DateUtils.getOrDefault(dateTime, ChronoField.MONTH_OF_YEAR);
|
||||
day = DateUtils.getOrDefault(dateTime, ChronoField.DAY_OF_MONTH);
|
||||
|
||||
// replace first 'T' with ' '
|
||||
s = replaceDelimiterT(s);
|
||||
if (!s.contains(" ")) {
|
||||
dateTime = DateTimeFormatterUtils.ZONE_DATE_FORMATTER.parse(s);
|
||||
} else {
|
||||
dateTime = DateTimeFormatterUtils.ZONE_DATE_TIME_FORMATTER.parse(s);
|
||||
}
|
||||
|
||||
// if Year is not present, throw exception
|
||||
if (!dateTime.isSupported(ChronoField.YEAR)) {
|
||||
throw new AnalysisException("datetime literal [" + originalString + "] is invalid");
|
||||
}
|
||||
|
||||
return dateTime;
|
||||
} catch (Exception ex) {
|
||||
throw new AnalysisException("date literal [" + s + "] is invalid");
|
||||
throw new AnalysisException("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("datetime literal [" + s + "] is out of range");
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,15 +231,17 @@ public class DateLiteral extends Literal {
|
||||
}
|
||||
|
||||
public Expression plusDays(long days) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER, getStringValue()).plusDays(days));
|
||||
return fromJavaDateType(DateUtils.getTime(StandardDateFormat.DATE_FORMATTER, getStringValue()).plusDays(days));
|
||||
}
|
||||
|
||||
public Expression plusMonths(long months) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER, getStringValue()).plusMonths(months));
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_FORMATTER, getStringValue()).plusMonths(months));
|
||||
}
|
||||
|
||||
public Expression plusYears(long years) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER, getStringValue()).plusYears(years));
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_FORMATTER, getStringValue()).plusYears(years));
|
||||
}
|
||||
|
||||
public LocalDateTime toJavaDateType() {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -124,95 +93,50 @@ public class DateTimeLiteral extends DateLiteral {
|
||||
this.day = day;
|
||||
}
|
||||
|
||||
/**
|
||||
* determine scale by datetime string
|
||||
*/
|
||||
public static int determineScale(String s) {
|
||||
TemporalAccessor dateTime = parse(s);
|
||||
int microSecond = DateUtils.getOrDefault(dateTime, ChronoField.MICRO_OF_SECOND);
|
||||
|
||||
if (microSecond == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scale = 6;
|
||||
while (microSecond % 10 == 0) {
|
||||
scale--;
|
||||
microSecond /= 10;
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
TemporalAccessor temporal = parse(s);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new AnalysisException("datetime literal [" + s + "] is invalid");
|
||||
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()) {
|
||||
@ -262,27 +186,33 @@ public class DateTimeLiteral extends DateLiteral {
|
||||
}
|
||||
|
||||
public Expression plusYears(long years) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER, getStringValue()).plusYears(years));
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER, getStringValue()).plusYears(years));
|
||||
}
|
||||
|
||||
public Expression plusMonths(long months) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER, getStringValue()).plusMonths(months));
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER, getStringValue()).plusMonths(months));
|
||||
}
|
||||
|
||||
public Expression plusDays(long days) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER, getStringValue()).plusDays(days));
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER, getStringValue()).plusDays(days));
|
||||
}
|
||||
|
||||
public Expression plusHours(long hours) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER, getStringValue()).plusHours(hours));
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER, getStringValue()).plusHours(hours));
|
||||
}
|
||||
|
||||
public Expression plusMinutes(long minutes) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER, getStringValue()).plusMinutes(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() {
|
||||
@ -324,27 +254,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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ 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 java.time.LocalDateTime;
|
||||
|
||||
@ -76,50 +77,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(long years) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusYears(years), getDataType().getScale());
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusYears(years), getDataType().getScale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression plusMonths(long months) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusMonths(months), getDataType().getScale());
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusMonths(months), getDataType().getScale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression plusDays(long days) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusDays(days), getDataType().getScale());
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusDays(days), getDataType().getScale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression plusHours(long hours) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusHours(hours), getDataType().getScale());
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusHours(hours), getDataType().getScale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression plusMinutes(long minutes) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusMinutes(minutes), getDataType().getScale());
|
||||
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(long microSeconds) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusNanos(microSeconds * 1000L), getDataType().getScale());
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_TIME_FORMATTER_TO_MICRO_SECOND, getStringValue())
|
||||
.plusNanos(microSeconds * 1000L), getDataType().getScale());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -51,15 +52,17 @@ public class DateV2Literal extends DateLiteral {
|
||||
}
|
||||
|
||||
public Expression plusDays(long days) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER, getStringValue()).plusDays(days));
|
||||
return fromJavaDateType(DateUtils.getTime(StandardDateFormat.DATE_FORMATTER, getStringValue()).plusDays(days));
|
||||
}
|
||||
|
||||
public Expression plusMonths(long months) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER, getStringValue()).plusMonths(months));
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_FORMATTER, getStringValue()).plusMonths(months));
|
||||
}
|
||||
|
||||
public Expression plusYears(long years) {
|
||||
return fromJavaDateType(DateUtils.getTime(DATE_FORMATTER, getStringValue()).plusYears(years));
|
||||
return fromJavaDateType(
|
||||
DateUtils.getTime(StandardDateFormat.DATE_FORMATTER, getStringValue()).plusYears(years));
|
||||
}
|
||||
|
||||
public static Expression fromJavaDateType(LocalDateTime dateTime) {
|
||||
|
||||
@ -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.DateLikeType;
|
||||
import org.apache.doris.nereids.types.coercion.IntegralType;
|
||||
|
||||
@ -85,7 +86,8 @@ public class DateTimeV2Type extends DateLikeType {
|
||||
if (!s.contains(".")) {
|
||||
return DateTimeV2Type.SYSTEM_DEFAULT;
|
||||
}
|
||||
return DateTimeV2Type.of(s.length() - s.lastIndexOf(".") - 1);
|
||||
int scale = DateTimeLiteral.determineScale(s);
|
||||
return DateTimeV2Type.of(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -39,8 +39,27 @@ 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()
|
||||
public static final DateTimeFormatter ZONE_FORMATTER = new DateTimeFormatterBuilder()
|
||||
.optionalStart()
|
||||
// .appendZoneText(TextStyle.FULL)
|
||||
.appendZoneOrOffsetId()
|
||||
.optionalEnd()
|
||||
// .appendOptional(
|
||||
// new DateTimeFormatterBuilder().appendOffset("+HH", "").toFormatter())
|
||||
// .appendOptional(
|
||||
// new DateTimeFormatterBuilder().appendOffset("+HH:MM", "").toFormatter())
|
||||
// .appendOptional(
|
||||
// new DateTimeFormatterBuilder().appendOffset("+HH:MM:SS", "").toFormatter())
|
||||
.toFormatter()
|
||||
.withResolverStyle(ResolverStyle.STRICT);
|
||||
// yymmdd
|
||||
public static final DateTimeFormatter BASIC_TWO_DIGIT_DATE_FORMATTER = new DateTimeFormatterBuilder()
|
||||
.appendValueReduced(ChronoField.YEAR, 2, 2, 1970)
|
||||
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
|
||||
.appendValue(ChronoField.DAY_OF_MONTH, 2)
|
||||
.toFormatter().withResolverStyle(ResolverStyle.STRICT);
|
||||
// yyyy-mm-dd
|
||||
public static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder()
|
||||
.appendOptional(
|
||||
new DateTimeFormatterBuilder().appendValue(ChronoField.YEAR, 4).toFormatter())
|
||||
.appendOptional(
|
||||
@ -48,33 +67,72 @@ public class DateTimeFormatterUtils {
|
||||
.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()
|
||||
.appendLiteral(':').appendValue(ChronoField.MINUTE_OF_HOUR, 2)
|
||||
.appendOptional(
|
||||
new DateTimeFormatterBuilder()
|
||||
.appendLiteral(':').appendValue(ChronoField.SECOND_OF_MINUTE, 2)
|
||||
.appendOptional(new DateTimeFormatterBuilder()
|
||||
.appendFraction(ChronoField.MICRO_OF_SECOND, 1, 6, true)
|
||||
.toFormatter())
|
||||
.toFormatter()
|
||||
)
|
||||
.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()
|
||||
.append(BASIC_DATE_FORMATTER)
|
||||
.optionalStart()
|
||||
.appendOptional(new DateTimeFormatterBuilder().appendLiteral('T').toFormatter())
|
||||
public static final DateTimeFormatter BASIC_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()
|
||||
.appendOptional(BASIC_DATE_FORMATTER)
|
||||
.appendOptional(BASIC_TWO_DIGIT_DATE_FORMATTER)
|
||||
.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()
|
||||
.appendOptional(
|
||||
new DateTimeFormatterBuilder().appendValue(ChronoField.YEAR, 4).toFormatter())
|
||||
.appendOptional(
|
||||
new DateTimeFormatterBuilder().appendValueReduced(ChronoField.YEAR, 2, 2, 1970).toFormatter())
|
||||
.appendLiteral('-').appendValue(ChronoField.MONTH_OF_YEAR, 2)
|
||||
.appendLiteral('-').appendValue(ChronoField.DAY_OF_MONTH, 2)
|
||||
// .optionalStart()
|
||||
// .appendZoneOrOffsetId()
|
||||
// .optionalEnd()
|
||||
.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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
// 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.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class DateLiteralTest {
|
||||
@Test
|
||||
void testDate() {
|
||||
new DateLiteral("220101");
|
||||
new DateLiteral("22-01-01");
|
||||
|
||||
new DateLiteral("2022-01-01");
|
||||
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
|
||||
@Disabled
|
||||
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");
|
||||
|
||||
Assertions.assertThrows(AnalysisException.class, () -> new DateLiteral("2022-01-01-01"));
|
||||
Assertions.assertThrows(AnalysisException.class, () -> new DateLiteral("2022-01-01-1"));
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test
|
||||
void testIrregularDate() {
|
||||
new DateLiteral("2016-07-02");
|
||||
|
||||
new DateLiteral("2016-7-02");
|
||||
new DateLiteral("2016-07-2");
|
||||
new DateLiteral("2016-7-2");
|
||||
|
||||
new DateLiteral("2016-07-02");
|
||||
new DateLiteral("2016-07-2");
|
||||
new DateLiteral("2016-7-02");
|
||||
new DateLiteral("2016-7-2");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,369 @@
|
||||
// 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.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class DateTimeLiteralTest {
|
||||
@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.assertSame(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("20220701010255+07:00");
|
||||
// new DateTimeV2Literal("20220701010255-05:00");
|
||||
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");
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@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");
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@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");
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@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");
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@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");
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@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");
|
||||
}
|
||||
}
|
||||
@ -24,8 +24,25 @@ 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");
|
||||
formatter.parse("UTC+01:00");
|
||||
formatter.parse("UTC+01:00:00");
|
||||
|
||||
// formatter.parse("GMT+01");
|
||||
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,29 +51,41 @@ 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
|
||||
@ -79,4 +108,32 @@ class DateTimeFormatterUtilsTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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 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);
|
||||
dateTime = timeFormatter.parse("01:01");
|
||||
assertTime.accept(dateTime);
|
||||
dateTime = timeFormatter.parse("01");
|
||||
assertTime.accept(dateTime);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user