This is an automated email from the ASF dual-hosted git repository. yiguolei pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new e769597fd2 [Improvement] (datetime) support microsecond for date literal (#10917) e769597fd2 is described below commit e769597fd292b5be6bd9a31147ae5a8c8f993265 Author: Gabriel <gabrielleeb...@gmail.com> AuthorDate: Mon Jul 18 21:39:39 2022 +0800 [Improvement] (datetime) support microsecond for date literal (#10917) * [Improvement] (datetime) support microsecond for date literal * remove joda dependency --- fe/fe-core/pom.xml | 5 - .../org/apache/doris/analysis/DateLiteral.java | 340 ++++++++++++--------- .../java/org/apache/doris/rewrite/FEFunctions.java | 13 +- .../org/apache/doris/analysis/DateLiteralTest.java | 49 ++- .../org/apache/doris/rewrite/FEFunctionsTest.java | 15 +- fe/pom.xml | 7 - fe/spark-dpp/pom.xml | 6 - .../apache/doris/load/loadv2/dpp/ColumnParser.java | 20 +- 8 files changed, 270 insertions(+), 185 deletions(-) diff --git a/fe/fe-core/pom.xml b/fe/fe-core/pom.xml index 6e3fd28e1f..44eabcce13 100644 --- a/fe/fe-core/pom.xml +++ b/fe/fe-core/pom.xml @@ -215,11 +215,6 @@ under the License. <artifactId>jmockit</artifactId> <scope>test</scope> </dependency> - <!-- https://mvnrepository.com/artifact/joda-time/joda-time --> - <dependency> - <groupId>joda-time</groupId> - <artifactId>joda-time</artifactId> - </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java index 52a6e58100..cc160c989b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java @@ -34,17 +34,22 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.LocalDateTime; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.DateTimeFormatterBuilder; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.nio.ByteBuffer; +import java.sql.Timestamp; +import java.time.LocalDateTime; import java.time.Year; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.TextStyle; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.TimeZone; @@ -57,23 +62,20 @@ public class DateLiteral extends LiteralExpr { private static final DateLiteral MAX_DATE = new DateLiteral(9999, 12, 31); private static final DateLiteral MIN_DATETIME = new DateLiteral(0000, 1, 1, 0, 0, 0); private static final DateLiteral MAX_DATETIME = new DateLiteral(9999, 12, 31, 23, 59, 59); + + private static final DateLiteral MIN_DATETIMEV2 + = new DateLiteral(0000, 1, 1, 0, 0, 0, 0); + private static final DateLiteral MAX_DATETIMEV2 + = new DateLiteral(9999, 12, 31, 23, 59, 59, 999999L); private static final int DATEKEY_LENGTH = 8; private static final int MAX_MICROSECOND = 999999; private static final int DATETIME_TO_MINUTE_STRING_LENGTH = 16; private static final int DATETIME_TO_HOUR_STRING_LENGTH = 13; + private static final int DATETIME_TO_SECOND_STRING_LENGTH = 19; private static DateTimeFormatter DATE_TIME_FORMATTER = null; - private static DateTimeFormatter DATE_TIME_FORMATTER_TO_HOUR = null; - private static DateTimeFormatter DATE_TIME_FORMATTER_TO_MINUTE = null; + private static DateTimeFormatter DATE_TIME_FORMATTER_TO_MICRO_SECOND = null; private static DateTimeFormatter DATE_FORMATTER = null; - /* - * Dates containing two-digit year values are ambiguous because the century is unknown. - * MySQL interprets two-digit year values using these rules: - * Year values in the range 70-99 are converted to 1970-1999. - * Year values in the range 00-69 are converted to 2000-2069. - * */ - private static DateTimeFormatter DATE_TIME_FORMATTER_TWO_DIGIT = null; - private static DateTimeFormatter DATE_FORMATTER_TWO_DIGIT = null; /* * The datekey type is widely used in data warehouses * For example, 20121229 means '2012-12-29' @@ -92,12 +94,12 @@ public class DateLiteral extends LiteralExpr { static { try { DATE_TIME_FORMATTER = formatBuilder("%Y-%m-%d %H:%i:%s").toFormatter(); - DATE_TIME_FORMATTER_TO_HOUR = formatBuilder("%Y-%m-%d %H").toFormatter(); - DATE_TIME_FORMATTER_TO_MINUTE = formatBuilder("%Y-%m-%d %H:%i").toFormatter(); DATE_FORMATTER = formatBuilder("%Y-%m-%d").toFormatter(); DATEKEY_FORMATTER = formatBuilder("%Y%m%d").toFormatter(); - DATE_TIME_FORMATTER_TWO_DIGIT = formatBuilder("%y-%m-%d %H:%i:%s").toFormatter(); - DATE_FORMATTER_TWO_DIGIT = formatBuilder("%y-%m-%d").toFormatter(); + DATE_TIME_FORMATTER_TO_MICRO_SECOND = new DateTimeFormatterBuilder() + .appendPattern("uuuu-MM-dd HH:mm:ss") + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 6, true) + .toFormatter(); } catch (AnalysisException e) { LOG.error("invalid date format", e); System.exit(-1); @@ -181,12 +183,18 @@ public class DateLiteral extends LiteralExpr { } else { copy(MIN_DATE); } - } else { + } else if (type.equals(Type.DATETIME)) { if (isMax) { copy(MAX_DATETIME); } else { copy(MIN_DATETIME); } + } else { + if (isMax) { + copy(MAX_DATETIMEV2); + } else { + copy(MIN_DATETIMEV2); + } } analysisDone(); } @@ -198,24 +206,30 @@ public class DateLiteral extends LiteralExpr { } public DateLiteral(long unixTimestamp, TimeZone timeZone, Type type) throws AnalysisException { - DateTime dt = new DateTime(unixTimestamp, DateTimeZone.forTimeZone(timeZone)); - year = dt.getYear(); - month = dt.getMonthOfYear(); - day = dt.getDayOfMonth(); - hour = dt.getHourOfDay(); - minute = dt.getMinuteOfHour(); - second = dt.getSecondOfMinute(); + Timestamp timestamp = new Timestamp(unixTimestamp); + + ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneId.of(timeZone.getID())); + year = zonedDateTime.getYear(); + month = zonedDateTime.getMonthValue(); + day = zonedDateTime.getDayOfMonth(); + hour = zonedDateTime.getHour(); + minute = zonedDateTime.getMinute(); + second = zonedDateTime.getSecond(); + microsecond = zonedDateTime.get(ChronoField.MICRO_OF_SECOND); if (type.equals(Type.DATE)) { hour = 0; minute = 0; second = 0; + microsecond = 0; this.type = Type.DATE; } else if (type.equals(Type.DATETIME)) { this.type = Type.DATETIME; + microsecond = 0; } else if (type.equals(Type.DATEV2)) { hour = 0; minute = 0; second = 0; + microsecond = 0; this.type = Type.DATEV2; } else if (type.equals(Type.DATETIMEV2)) { this.type = Type.DATETIMEV2; @@ -235,9 +249,6 @@ public class DateLiteral extends LiteralExpr { } public DateLiteral(long year, long month, long day, Type type) { - this.hour = hour; - this.minute = minute; - this.second = second; this.year = year; this.month = month; this.day = day; @@ -281,12 +292,12 @@ public class DateLiteral extends LiteralExpr { public DateLiteral(LocalDateTime dateTime, Type type) { this.year = dateTime.getYear(); - this.month = dateTime.getMonthOfYear(); + this.month = dateTime.getMonthValue(); this.day = dateTime.getDayOfMonth(); - this.hour = dateTime.getHourOfDay(); - this.minute = dateTime.getMinuteOfHour(); - this.second = dateTime.getSecondOfMinute(); - this.microsecond = dateTime.getMillisOfSecond() * 1000L; + this.hour = dateTime.getHour(); + this.minute = dateTime.getMinute(); + this.second = dateTime.getSecond(); + this.microsecond = dateTime.get(ChronoField.MICRO_OF_SECOND); this.type = type; } @@ -309,40 +320,86 @@ public class DateLiteral extends LiteralExpr { private void init(String s, Type type) throws AnalysisException { try { Preconditions.checkArgument(type.isDateType()); - LocalDateTime dateTime; - if (type.equals(Type.DATE) || type.equals(Type.DATEV2)) { - if (s.split("-")[0].length() == 2) { - dateTime = DATE_FORMATTER_TWO_DIGIT.parseLocalDateTime(s); - } else if (s.length() == DATEKEY_LENGTH && !s.contains("-")) { - // handle format like 20210106, but should not handle 2021-1-6 - dateTime = DATEKEY_FORMATTER.parseLocalDateTime(s); - } else { - dateTime = DATE_FORMATTER.parseLocalDateTime(s); - } + TemporalAccessor dateTime; + if (s.length() == DATEKEY_LENGTH && !s.contains("-")) { + // handle format like 20210106, but should not handle 2021-1-6 + dateTime = DATEKEY_FORMATTER.parse(s); } else { - if (s.split("-")[0].length() == 2) { - dateTime = DATE_TIME_FORMATTER_TWO_DIGIT.parseLocalDateTime(s); - } else { - // parse format '%Y-%m-%d %H:%i' and '%Y-%m-%d %H' - if (s.length() == DATETIME_TO_MINUTE_STRING_LENGTH) { - dateTime = DATE_TIME_FORMATTER_TO_MINUTE.parseLocalDateTime(s); - } else if (s.length() == DATETIME_TO_HOUR_STRING_LENGTH) { - dateTime = DATE_TIME_FORMATTER_TO_HOUR.parseLocalDateTime(s); - } else { - dateTime = DATE_TIME_FORMATTER.parseLocalDateTime(s); + String[] datePart = s.contains(" ") ? s.split(" ")[0].split("-") : s.split("-"); + DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); + if (datePart.length != 3) { + throw new AnalysisException("Invalid date value: " + s); + } + 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[]{}; + if (timePart.length > 0 && (type.equals(Type.DATE) || type.equals(Type.DATEV2))) { + throw new AnalysisException("Invalid date value: " + s); + } + if (timePart.length == 0 && (type.equals(Type.DATETIME) || type.equals(Type.DATETIMEV2))) { + throw new AnalysisException("Invalid datetime value: " + s); + } + 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.NANO_OF_SECOND, 0, 6, true); + } + break; + default: + throw new AnalysisException("Two many parts in time format " + s); + } + if (i < timePart.length - 1) { + builder.appendLiteral(":"); } } + DateTimeFormatter formatter = builder.toFormatter(); + dateTime = formatter.parse(s); } - year = dateTime.getYear(); - month = dateTime.getMonthOfYear(); - day = dateTime.getDayOfMonth(); - hour = dateTime.getHourOfDay(); - minute = dateTime.getMinuteOfHour(); - second = dateTime.getSecondOfMinute(); + year = getOrDefault(dateTime, ChronoField.YEAR, 0); + month = getOrDefault(dateTime, ChronoField.MONTH_OF_YEAR, 0); + day = getOrDefault(dateTime, ChronoField.DAY_OF_MONTH, 0); + hour = getOrDefault(dateTime, ChronoField.HOUR_OF_DAY, 0); + minute = getOrDefault(dateTime, ChronoField.MINUTE_OF_HOUR, 0); + second = getOrDefault(dateTime, ChronoField.SECOND_OF_MINUTE, 0); + microsecond = getOrDefault(dateTime, ChronoField.MICRO_OF_SECOND, 0); this.type = type; } catch (Exception ex) { - throw new AnalysisException("date literal [" + s + "] is invalid"); + throw new AnalysisException("date literal [" + s + "] is invalid: " + ex.getMessage()); } } @@ -369,8 +426,9 @@ public class DateLiteral extends LiteralExpr { case DATEV2: return this.getStringValue().compareTo(MIN_DATE.getStringValue()) == 0; case DATETIME: - case DATETIMEV2: return this.getStringValue().compareTo(MIN_DATETIME.getStringValue()) == 0; + case DATETIMEV2: + return this.getStringValue().compareTo(MIN_DATETIMEV2.getStringValue()) == 0; default: return false; } @@ -605,27 +663,10 @@ public class DateLiteral extends LiteralExpr { } public long unixTimestamp(TimeZone timeZone) { - DateTime dt = new DateTime((int) year, (int) month, (int) day, (int) hour, (int) minute, (int) second, - DateTimeZone.forTimeZone(timeZone)); - return dt.getMillis(); - } - - public static DateLiteral dateParser(String date, String pattern) throws AnalysisException { - DateTimeFormatter formatter = formatBuilder(pattern).toFormatter(); - LocalDateTime dateTime = formatter.parseLocalDateTime(date); - DateLiteral dateLiteral = new DateLiteral( - dateTime.getYear(), - dateTime.getMonthOfYear(), - dateTime.getDayOfMonth(), - dateTime.getHourOfDay(), - dateTime.getMinuteOfHour(), - dateTime.getSecondOfMinute()); - if (HAS_TIME_PART.matcher(pattern).matches()) { - dateLiteral.setType(Type.DATETIME); - } else { - dateLiteral.setType(Type.DATE); - } - return dateLiteral; + ZonedDateTime zonedDateTime = ZonedDateTime.of((int) year, (int) month, (int) day, (int) hour, + (int) minute, (int) second, (int) microsecond, ZoneId.of(timeZone.getID())); + Timestamp timestamp = Timestamp.from(zonedDateTime.toInstant()); + return timestamp.getTime(); } public static boolean hasTimePart(String format) { @@ -635,13 +676,16 @@ public class DateLiteral extends LiteralExpr { //Return the date stored in the dateliteral as pattern format. //eg : "%Y-%m-%d" or "%Y-%m-%d %H:%i:%s" public String dateFormat(String pattern) throws AnalysisException { - if (type.equals(Type.DATE)) { - return DATE_FORMATTER.parseLocalDateTime(getStringValue()) - .toString(formatBuilder(pattern).toFormatter()); + TemporalAccessor accessor; + if (type.equals(Type.DATE) || type.equals(Type.DATEV2)) { + accessor = DATE_FORMATTER.parse(getStringValue()); + } else if (type.isDatetimeV2()) { + accessor = DATE_TIME_FORMATTER_TO_MICRO_SECOND.parse(getStringValue()); } else { - return DATE_TIME_FORMATTER.parseLocalDateTime(getStringValue()) - .toString(formatBuilder(pattern).toFormatter()); + accessor = DATE_TIME_FORMATTER.parse(getStringValue()); } + DateTimeFormatter toFormatter = formatBuilder(pattern).toFormatter(); + return toFormatter.format(accessor); } private static DateTimeFormatterBuilder formatBuilder(String pattern) throws AnalysisException { @@ -652,84 +696,77 @@ public class DateLiteral extends LiteralExpr { if (escaped) { switch (character) { case 'a': // %a Abbreviated weekday name (Sun..Sat) - builder.appendDayOfWeekShortText(); + builder.appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT); break; case 'b': // %b Abbreviated month name (Jan..Dec) - builder.appendMonthOfYearShortText(); + builder.appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT); break; case 'c': // %c Month, numeric (0..12) - builder.appendMonthOfYear(1); + builder.appendValue(ChronoField.MONTH_OF_YEAR); break; case 'd': // %d Day of the month, numeric (00..31) - builder.appendDayOfMonth(2); + builder.appendValue(ChronoField.DAY_OF_MONTH, 2); break; case 'e': // %e Day of the month, numeric (0..31) - builder.appendDayOfMonth(1); + builder.appendValue(ChronoField.DAY_OF_MONTH); break; case 'H': // %H Hour (00..23) - builder.appendHourOfDay(2); + builder.appendValue(ChronoField.HOUR_OF_DAY, 2); break; case 'h': // %h Hour (01..12) case 'I': // %I Hour (01..12) - builder.appendClockhourOfHalfday(2); + builder.appendValue(ChronoField.HOUR_OF_AMPM, 2); break; case 'i': // %i Minutes, numeric (00..59) - builder.appendMinuteOfHour(2); + builder.appendValue(ChronoField.MINUTE_OF_HOUR, 2); break; case 'j': // %j Day of year (001..366) - builder.appendDayOfYear(3); + builder.appendValue(ChronoField.DAY_OF_YEAR, 3); break; case 'k': // %k Hour (0..23) - builder.appendHourOfDay(1); + builder.appendValue(ChronoField.HOUR_OF_DAY); break; case 'l': // %l Hour (1..12) - builder.appendClockhourOfHalfday(1); + builder.appendValue(ChronoField.HOUR_OF_AMPM); break; case 'M': // %M Month name (January..December) - builder.appendMonthOfYearText(); + builder.appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL); break; case 'm': // %m Month, numeric (00..12) - builder.appendMonthOfYear(2); + builder.appendValue(ChronoField.MONTH_OF_YEAR, 2); break; case 'p': // %p AM or PM - builder.appendHalfdayOfDayText(); + builder.appendText(ChronoField.AMPM_OF_DAY); break; case 'r': // %r Time, 12-hour (hh:mm:ss followed by AM or PM) - builder.appendClockhourOfHalfday(2) - .appendLiteral(':') - .appendMinuteOfHour(2) - .appendLiteral(':') - .appendSecondOfMinute(2) - .appendLiteral(' ') - .appendHalfdayOfDayText(); + builder.appendValue(ChronoField.HOUR_OF_AMPM, 2) + .appendPattern(":mm:ss ") + .appendText(ChronoField.AMPM_OF_DAY, TextStyle.FULL) + .toFormatter(); break; case 'S': // %S Seconds (00..59) case 's': // %s Seconds (00..59) - builder.appendSecondOfMinute(2); + builder.appendValue(ChronoField.SECOND_OF_MINUTE, 2); break; - case 'T': // %T Time, 24-hour (hh:mm:ss) - builder.appendHourOfDay(2) - .appendLiteral(':') - .appendMinuteOfHour(2) - .appendLiteral(':') - .appendSecondOfMinute(2); + case 'T': // %T Time, 24-hour (HH:mm:ss) + builder.appendPattern("HH:mm:ss"); break; case 'v': // %v Week (01..53), where Monday is the first day of the week; used with %x - builder.appendWeekOfWeekyear(2); + builder.appendValue(ChronoField.ALIGNED_WEEK_OF_YEAR, 2); break; case 'x': // %x Year for the week, where Monday is the first day of the week, // numeric, four digits; used with %v - builder.appendWeekyear(4, 4); + builder.appendValue(ChronoField.YEAR, 4); break; case 'W': // %W Weekday name (Sunday..Saturday) - builder.appendDayOfWeekText(); + builder.appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL); break; case 'Y': // %Y Year, numeric, four digits - builder.appendYear(4, 4); + builder.appendPattern("uuuu"); break; case 'y': // %y Year, numeric (two digits) - builder.appendTwoDigitYear(2020); + builder.appendValueReduced(ChronoField.YEAR, 2, 2, 1970); break; // TODO(Gabriel): support microseconds in date literal case 'f': // %f Microseconds (000000..999999) @@ -759,14 +796,29 @@ public class DateLiteral extends LiteralExpr { return builder; } - public LocalDateTime getTimeFormatter() throws AnalysisException { + private int getOrDefault(final TemporalAccessor accessor, final ChronoField field, + final int defaultValue) { + return accessor.isSupported(field) ? accessor.get(field) : defaultValue; + } + + public LocalDateTime getTimeFormatter() { + TemporalAccessor accessor; if (type.equals(Type.DATE) || type.equals(Type.DATEV2)) { - return DATE_FORMATTER.parseLocalDateTime(getStringValue()); - } else if (type.equals(Type.DATETIME) || type.equals(Type.DATETIMEV2)) { - return DATE_TIME_FORMATTER.parseLocalDateTime(getStringValue()); + accessor = DATE_FORMATTER.parse(getStringValue()); + } else if (type.isDatetimeV2()) { + accessor = DATE_TIME_FORMATTER_TO_MICRO_SECOND.parse(getStringValue()); } else { - throw new AnalysisException("Not support date literal type"); + accessor = DATE_TIME_FORMATTER.parse(getStringValue()); } + final int year = accessor.get(ChronoField.YEAR); + final int month = accessor.get(ChronoField.MONTH_OF_YEAR); + final int dayOfMonth = accessor.get(ChronoField.DAY_OF_MONTH); + final int hour = getOrDefault(accessor, ChronoField.HOUR_OF_DAY, 0); + final int minute = getOrDefault(accessor, ChronoField.MINUTE_OF_HOUR, 0); + final int second = getOrDefault(accessor, ChronoField.SECOND_OF_MINUTE, 0); + final int microSeconds = getOrDefault(accessor, ChronoField.NANO_OF_SECOND, 0); + + return LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, microSeconds); } public DateLiteral plusYears(int year) throws AnalysisException { @@ -817,6 +869,10 @@ public class DateLiteral extends LiteralExpr { return second; } + public long getMicrosecond() { + return microsecond; + } + private long year; private long month; private long day; @@ -843,6 +899,7 @@ public class DateLiteral extends LiteralExpr { boolean datePartUsed = false; boolean timePartUsed = false; + boolean microSecondPartUsed = false; int dayPart = 0; long weekday = -1; @@ -966,8 +1023,11 @@ public class DateLiteral extends LiteralExpr { break; // Micro second case 'f': - // micro second is not supported, so just eat it and go one. tmp = vp + Math.min(6, vend - vp); + intValue = strToLong(value.substring(vp, tmp)); + this.microsecond = (long) (intValue * Math.pow(10, 6 - Math.min(6, vend - vp))); + timePartUsed = true; + microSecondPartUsed = true; vp = tmp; break; // AM/PM @@ -1162,10 +1222,12 @@ public class DateLiteral extends LiteralExpr { // TODO(Gabriel): we still use old version datetime/date and change this to new version when // we think it's stable enough if (datePartUsed) { - if (timePartUsed) { - this.type = Type.DATETIME; + if (microSecondPartUsed) { + this.type = Type.DATETIMEV2; + } else if (timePartUsed) { + this.type = getDefaultDateType(Type.DATETIME); } else { - this.type = Type.DATE; + this.type = getDefaultDateType(Type.DATE); } } @@ -1421,9 +1483,9 @@ public class DateLiteral extends LiteralExpr { microsecond = dateVal[6]; if (numField == 3) { - type = Type.DATE; + type = getDefaultDateType(Type.DATE); } else { - type = Type.DATETIME; + type = getDefaultDateType(Type.DATETIME); } if (checkRange() || checkDate()) { @@ -1431,18 +1493,6 @@ public class DateLiteral extends LiteralExpr { } } - public void fromDateStr(String dateStr, Type type) throws AnalysisException { - switch (type.getPrimitiveType()) { - case DATETIME: - case DATE: - fromDateStr(dateStr); - break; - default: - fromDateStr(dateStr); - convertTypeToV2(); - } - } - public static Type getDefaultDateType(Type type) { switch (type.getPrimitiveType()) { case DATE: diff --git a/fe/fe-core/src/main/java/org/apache/doris/rewrite/FEFunctions.java b/fe/fe-core/src/main/java/org/apache/doris/rewrite/FEFunctions.java index e1d2ca5b06..b7f5fe4bd0 100755 --- a/fe/fe-core/src/main/java/org/apache/doris/rewrite/FEFunctions.java +++ b/fe/fe-core/src/main/java/org/apache/doris/rewrite/FEFunctions.java @@ -35,11 +35,10 @@ import org.apache.doris.qe.GlobalVariable; import com.google.common.base.Preconditions; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.joda.time.DateTimeZone; -import org.joda.time.LocalDateTime; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.LocalDateTime; /** * compute functions in FE. @@ -380,7 +379,7 @@ public class FEFunctions { @FEFunction(name = "now", argTypes = {}, returnType = "DATETIME") public static DateLiteral now() throws AnalysisException { - return new DateLiteral(LocalDateTime.now(DateTimeZone.forTimeZone(TimeUtils.getTimeZone())), + return new DateLiteral(LocalDateTime.now(TimeUtils.getTimeZone().toZoneId()), DateLiteral.getDefaultDateType(Type.DATETIME)); } @@ -390,8 +389,8 @@ public class FEFunctions { } @FEFunction(name = "curdate", argTypes = {}, returnType = "DATE") - public static DateLiteral curDate() throws AnalysisException { - return new DateLiteral(LocalDateTime.now(DateTimeZone.forTimeZone(TimeUtils.getTimeZone())), + public static DateLiteral curDate() { + return new DateLiteral(LocalDateTime.now(TimeUtils.getTimeZone().toZoneId()), DateLiteral.getDefaultDateType(Type.DATE)); } @@ -408,8 +407,8 @@ public class FEFunctions { } @FEFunction(name = "utc_timestamp", argTypes = {}, returnType = "DATETIME") - public static DateLiteral utcTimestamp() throws AnalysisException { - return new DateLiteral(LocalDateTime.now(DateTimeZone.forTimeZone(TimeUtils.getOrSystemTimeZone("+00:00"))), + public static DateLiteral utcTimestamp() { + return new DateLiteral(LocalDateTime.now(TimeUtils.getOrSystemTimeZone("+00:00").toZoneId()), DateLiteral.getDefaultDateType(Type.DATETIME)); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/DateLiteralTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/DateLiteralTest.java index a6f7bf41dd..60cee42cfe 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/analysis/DateLiteralTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/DateLiteralTest.java @@ -23,10 +23,11 @@ import org.apache.doris.common.AnalysisException; import org.apache.doris.common.InvalidFormatException; import org.apache.doris.common.jmockit.Deencapsulation; -import org.joda.time.LocalDateTime; import org.junit.Assert; import org.junit.Test; +import java.time.LocalDateTime; + public class DateLiteralTest { @Test @@ -108,6 +109,24 @@ public class DateLiteralTest { Assert.assertTrue(literal.toSql().contains("2020-12-13 12:00:00")); } + @Test + public void testParseDateTimeV2ToHourORMinute() throws Exception { + String s = "2020-12-13 12:13:14.123"; + Type type = ScalarType.createDatetimeV2Type(6); + DateLiteral literal = new DateLiteral(s, type); + Assert.assertTrue(literal.toSql().contains("2020-12-13 12:13:14.123")); + s = "2020-12-13 12:13"; + literal = new DateLiteral(s, type); + Assert.assertTrue(literal.toSql().contains("2020-12-13 12:13:00")); + s = "2020-12-13 12"; + literal = new DateLiteral(s, type); + Assert.assertTrue(literal.toSql().contains("2020-12-13 12:00:00")); + + String s2 = "2020-12-13 12:13:14.123456"; + DateLiteral literal2 = new DateLiteral(s2, type); + Assert.assertTrue(literal2.toSql().contains("2020-12-13 12:13:14.123456")); + } + @Test public void uncheckedCastTo() { boolean hasException = false; @@ -231,6 +250,30 @@ public class DateLiteralTest { Assert.assertFalse(hasException); } + @Test + public void testDateFormatForDatetimeV2() { + boolean hasException = false; + try { + DateLiteral literal = new DateLiteral("1997-10-7 00:00:00.123456", Type.DATETIMEV2); + Assert.assertEquals(1997, literal.getYear()); + Assert.assertEquals(123456, literal.getMicrosecond()); + + literal = new DateLiteral("2021-06-1 00:00:00.123456", Type.DATETIMEV2); + Assert.assertEquals(2021, literal.getYear()); + Assert.assertEquals(6, literal.getMonth()); + Assert.assertEquals(1, literal.getDay()); + + literal = new DateLiteral("2022-6-01 00:00:00.123456", Type.DATETIMEV2); + Assert.assertEquals(2022, literal.getYear()); + Assert.assertEquals(6, literal.getMonth()); + Assert.assertEquals(1, literal.getDay()); + } catch (AnalysisException e) { + e.printStackTrace(); + hasException = true; + } + Assert.assertFalse(hasException); + } + @Test public void testParseDateTimeToHourORMinuteForDateV2() throws Exception { String s = "2020-12-13 12:13:14"; @@ -308,7 +351,7 @@ public class DateLiteralTest { } @Test - public void testCheckDateForDateV2() throws AnalysisException { + public void testCheckDateForDateV2() { boolean hasException = false; try { DateLiteral dateLiteral = new DateLiteral(); @@ -347,7 +390,7 @@ public class DateLiteralTest { } @Test - public void testDateTimeV2Decimal() throws AnalysisException { + public void testDateTimeV2Decimal() { DateLiteral dateLiteral1 = new DateLiteral(LocalDateTime.now(), DateLiteral.getDefaultDateType(ScalarType.createDatetimeV2Type(3))); Assert.assertTrue((dateLiteral1.getDecimalNumber() >= 100 && dateLiteral1.getDecimalNumber() < 1000) diff --git a/fe/fe-core/src/test/java/org/apache/doris/rewrite/FEFunctionsTest.java b/fe/fe-core/src/test/java/org/apache/doris/rewrite/FEFunctionsTest.java index 5060fa67f3..b9aa120d7d 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/rewrite/FEFunctionsTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/rewrite/FEFunctionsTest.java @@ -29,7 +29,6 @@ import org.apache.doris.common.util.TimeUtils; import mockit.Expectations; import mockit.Mocked; -import org.joda.time.DateTime; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -37,6 +36,9 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; import java.util.Locale; import java.util.TimeZone; @@ -740,9 +742,14 @@ public class FEFunctionsTest { String curTimeString = FEFunctions.curTime().toSqlImpl().replace("'", ""); String currentTimestampString = FEFunctions.currentTimestamp().toSqlImpl().replace("'", ""); - String nowTimestampString = new DateTime().toString("yyyy-MM-dd HH:mm:ss"); - Assert.assertTrue(nowTimestampString.compareTo(currentTimestampString) >= 0); - String nowTimeString = nowTimestampString.substring(nowTimestampString.indexOf(" ") + 1); + ZonedDateTime zonedDateTime = ZonedDateTime.now(TimeUtils.getTimeZone().toZoneId()); + DateTimeFormatter formatter = new DateTimeFormatterBuilder() + .appendPattern("uuuu-MM-dd HH:mm:ss") + .toFormatter(); + + Assert.assertTrue(formatter.format(zonedDateTime).compareTo(currentTimestampString) >= 0); + String nowTimeString = formatter.format(zonedDateTime).substring( + formatter.format(zonedDateTime).indexOf(" ") + 1); Assert.assertTrue(nowTimeString.compareTo(curTimeString) >= 0); } diff --git a/fe/pom.xml b/fe/pom.xml index 0a78a457d1..0c3be8cca1 100644 --- a/fe/pom.xml +++ b/fe/pom.xml @@ -185,7 +185,6 @@ under the License. <jetty.version>6.1.14</jetty.version> <jflex.version>1.4.3</jflex.version> <jmockit.version>1.49</jmockit.version> - <joda-time.version>2.10.1</joda-time.version> <commons-io.version>2.6</commons-io.version> <json-simple.version>1.1.1</json-simple.version> <junit.version>5.8.2</junit.version> @@ -431,12 +430,6 @@ under the License. <version>${jmockit.version}</version> <scope>test</scope> </dependency> - <!-- https://mvnrepository.com/artifact/joda-time/joda-time --> - <dependency> - <groupId>joda-time</groupId> - <artifactId>joda-time</artifactId> - <version>${joda-time.version}</version> - </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> diff --git a/fe/spark-dpp/pom.xml b/fe/spark-dpp/pom.xml index 893f4ce353..1d7bff6aae 100644 --- a/fe/spark-dpp/pom.xml +++ b/fe/spark-dpp/pom.xml @@ -61,12 +61,6 @@ under the License. <artifactId>jmockit</artifactId> <scope>test</scope> </dependency> - <!-- https://mvnrepository.com/artifact/joda-time/joda-time --> - <dependency> - <groupId>joda-time</groupId> - <artifactId>joda-time</artifactId> - <scope>provided</scope> - </dependency> <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine --> <dependency> <groupId>org.junit.jupiter</groupId> diff --git a/fe/spark-dpp/src/main/java/org/apache/doris/load/loadv2/dpp/ColumnParser.java b/fe/spark-dpp/src/main/java/org/apache/doris/load/loadv2/dpp/ColumnParser.java index 2d15ab88a3..e72d471a9f 100644 --- a/fe/spark-dpp/src/main/java/org/apache/doris/load/loadv2/dpp/ColumnParser.java +++ b/fe/spark-dpp/src/main/java/org/apache/doris/load/loadv2/dpp/ColumnParser.java @@ -20,14 +20,14 @@ package org.apache.doris.load.loadv2.dpp; import org.apache.doris.common.SparkDppException; import org.apache.doris.load.loadv2.etl.EtlJobConfig; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; // Parser to validate value for different type public abstract class ColumnParser implements Serializable { @@ -35,8 +35,12 @@ public abstract class ColumnParser implements Serializable { protected static final Logger LOG = LoggerFactory.getLogger(ColumnParser.class); // thread safe formatter - public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd"); - public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); + public static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder() + .appendPattern("uuuu-MM-dd") + .toFormatter(); + public static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder() + .appendPattern("uuuu-MM-dd HH:mm:ss") + .toFormatter(); public static ColumnParser create(EtlJobConfig.EtlColumn etlColumn) throws SparkDppException { String columnType = etlColumn.columnType; @@ -166,8 +170,8 @@ class DateParser extends ColumnParser { @Override public boolean parse(String value) { try { - DATE_FORMATTER.parseDateTime(value); - } catch (IllegalArgumentException e) { + DATE_FORMATTER.parse(value); + } catch (Exception e) { return false; } return true; @@ -178,8 +182,8 @@ class DatetimeParser extends ColumnParser { @Override public boolean parse(String value) { try { - DATE_TIME_FORMATTER.parseDateTime(value); - } catch (IllegalArgumentException e) { + DATE_TIME_FORMATTER.parse(value); + } catch (Exception e) { return false; } return true; --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org