diff --git a/fe/src/main/java/org/apache/doris/rewrite/FEFunctions.java b/fe/src/main/java/org/apache/doris/rewrite/FEFunctions.java index aafff0bb88..fae542a540 100644 --- a/fe/src/main/java/org/apache/doris/rewrite/FEFunctions.java +++ b/fe/src/main/java/org/apache/doris/rewrite/FEFunctions.java @@ -73,6 +73,135 @@ public class FEFunctions { return new StringLiteral(result); } + @FEFunction(name = "str_to_date", argTypes = { "VARCHAR", "VARCHAR" }, returnType = "DATETIME") + public static DateLiteral dateParse(StringLiteral date, StringLiteral fmtLiteral) throws AnalysisException { + boolean hasTimePart = false; + DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); + + String formatString = fmtLiteral.getStringValue(); + boolean escaped = false; + for (int i = 0; i < formatString.length(); i++) { + char character = formatString.charAt(i); + + if (escaped) { + switch (character) { + case 'a': // %a Abbreviated weekday name (Sun..Sat) + builder.appendDayOfWeekShortText(); + break; + case 'b': // %b Abbreviated month name (Jan..Dec) + builder.appendMonthOfYearShortText(); + break; + case 'c': // %c Month, numeric (0..12) + builder.appendMonthOfYear(1); + break; + case 'd': // %d Day of the month, numeric (00..31) + builder.appendDayOfMonth(2); + break; + case 'e': // %e Day of the month, numeric (0..31) + builder.appendDayOfMonth(1); + break; + case 'H': // %H Hour (00..23) + builder.appendHourOfDay(2); + hasTimePart = true; + break; + case 'h': // %h Hour (01..12) + case 'I': // %I Hour (01..12) + builder.appendClockhourOfHalfday(2); + hasTimePart = true; + break; + case 'i': // %i Minutes, numeric (00..59) + builder.appendMinuteOfHour(2); + hasTimePart = true; + break; + case 'j': // %j Day of year (001..366) + builder.appendDayOfYear(3); + break; + case 'k': // %k Hour (0..23) + builder.appendHourOfDay(1); + hasTimePart = true; + break; + case 'l': // %l Hour (1..12) + builder.appendClockhourOfHalfday(1); + hasTimePart = true; + break; + case 'M': // %M Month name (January..December) + builder.appendMonthOfYearText(); + break; + case 'm': // %m Month, numeric (00..12) + builder.appendMonthOfYear(2); + break; + case 'p': // %p AM or PM + builder.appendHalfdayOfDayText(); + 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(); + hasTimePart = true; + break; + case 'S': // %S Seconds (00..59) + case 's': // %s Seconds (00..59) + builder.appendSecondOfMinute(2); + hasTimePart = true; + break; + case 'T': // %T Time, 24-hour (hh:mm:ss) + builder.appendHourOfDay(2) + .appendLiteral(':') + .appendMinuteOfHour(2) + .appendLiteral(':') + .appendSecondOfMinute(2); + hasTimePart = true; + break; + case 'v': // %v Week (01..53), where Monday is the first day of the week; used with %x + builder.appendWeekOfWeekyear(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); + break; + case 'W': // %W Weekday name (Sunday..Saturday) + builder.appendDayOfWeekText(); + break; + case 'Y': // %Y Year, numeric, four digits + builder.appendYear(4, 4); + break; + case 'y': // %y Year, numeric (two digits) + builder.appendTwoDigitYear(2020); + break; + case 'f': // %f Microseconds (000000..999999) + case 'w': // %w Day of the week (0=Sunday..6=Saturday) + case 'U': // %U Week (00..53), where Sunday is the first day of the week + case 'u': // %u Week (00..53), where Monday is the first day of the week + case 'V': // %V Week (01..53), where Sunday is the first day of the week; used with %X + case 'X': // %X Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V + case 'D': // %D Day of the month with English suffix (0th, 1st, 2nd, 3rd, …) + throw new AnalysisException(String.format("%%%s not supported in date format string", character)); + case '%': // %% A literal "%" character + builder.appendLiteral('%'); + break; + default: // % The literal character represented by + builder.appendLiteral(character); + break; + } + escaped = false; + } else if (character == '%') { + escaped = true; + } else { + builder.appendLiteral(character); + } + } + + Date retDate = new Date(builder.toFormatter().parseMillis(date.getStringValue())); + if (hasTimePart) { + return new DateLiteral(DateFormatUtils.format(retDate, "yyyy-MM-dd HH:mm:ss"), Type.DATETIME); + } else { + return new DateLiteral(DateFormatUtils.format(retDate, "yyyy-MM-dd"), Type.DATE); + } + } + @FEFunction(name = "date_sub", argTypes = { "DATETIME", "INT" }, returnType = "DATETIME") public static DateLiteral dateSub(LiteralExpr date, LiteralExpr day) throws AnalysisException { Date d = new Date(getTime(date)); diff --git a/fe/src/test/java/org/apache/doris/rewrite/FEFunctionsTest.java b/fe/src/test/java/org/apache/doris/rewrite/FEFunctionsTest.java index 9413cf1961..fc645af190 100644 --- a/fe/src/test/java/org/apache/doris/rewrite/FEFunctionsTest.java +++ b/fe/src/test/java/org/apache/doris/rewrite/FEFunctionsTest.java @@ -26,6 +26,10 @@ import org.apache.doris.common.AnalysisException; import org.junit.Assert; import org.junit.Test; +import java.util.TimeZone; + +import static org.junit.Assert.fail; + /* * Author: Chenmingyu * Date: Mar 13, 2019 @@ -87,4 +91,64 @@ public class FEFunctionsTest { e.printStackTrace(); } } + + @Test + public void dateParseTest() { + TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai"); + TimeZone.setDefault(tz); + try { + Assert.assertEquals("2013-05-10", FEFunctions.dateParse(new StringLiteral("2013,05,10"), new StringLiteral("%Y,%m,%d")).getStringValue()); + Assert.assertEquals("2013-05-17 00:35:10", FEFunctions.dateParse(new StringLiteral("2013-05-17 12:35:10"), new StringLiteral("%Y-%m-%d %h:%i:%s")).getStringValue()); + Assert.assertEquals("2013-05-17 00:35:10", FEFunctions.dateParse(new StringLiteral("2013-05-17 00:35:10"), new StringLiteral("%Y-%m-%d %H:%i:%s")).getStringValue()); + Assert.assertEquals("2013-05-17 00:35:10", FEFunctions.dateParse(new StringLiteral("2013-05-17 12:35:10 AM"), new StringLiteral("%Y-%m-%d %h:%i:%s %p")).getStringValue()); + Assert.assertEquals("2013-05-17 12:35:10", FEFunctions.dateParse(new StringLiteral("2013-05-17 12:35:10 PM"), new StringLiteral("%Y-%m-%d %h:%i:%s %p")).getStringValue()); + Assert.assertEquals("2013-05-17 23:35:10", FEFunctions.dateParse(new StringLiteral("abc 2013-05-17 fff 23:35:10 xyz"), new StringLiteral("abc %Y-%m-%d fff %H:%i:%s xyz")).getStringValue()); + Assert.assertEquals("2016-01-28 23:45:46", FEFunctions.dateParse(new StringLiteral("28-JAN-16 11.45.46 PM"), new StringLiteral("%d-%b-%y %l.%i.%s %p")).getStringValue()); + Assert.assertEquals("2019-05-09", FEFunctions.dateParse(new StringLiteral("2019/May/9"), new StringLiteral("%Y/%b/%d")).getStringValue()); + Assert.assertEquals("2019-05-09", FEFunctions.dateParse(new StringLiteral("2019,129"), new StringLiteral("%Y,%j")).getStringValue()); + Assert.assertEquals("2019-05-09", FEFunctions.dateParse(new StringLiteral("2019,19,Thursday"), new StringLiteral("%x,%v,%W")).getStringValue()); + Assert.assertEquals("2019-05-09 12:10:45", FEFunctions.dateParse(new StringLiteral("12:10:45-20190509"), new StringLiteral("%T-%Y%m%d")).getStringValue()); + Assert.assertEquals("2019-05-09 09:10:45", FEFunctions.dateParse(new StringLiteral("20190509-9:10:45"), new StringLiteral("%Y%m%d-%k:%i:%S")).getStringValue()); + } catch (AnalysisException e) { + fail("Junit test dateParse fail"); + e.printStackTrace(); + } + + try { + FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%D")); + fail("Junit test dateParse fail"); + } catch (AnalysisException e) { + Assert.assertEquals(e.getMessage(), "%D not supported in date format string"); + } + try { + FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%U")); + fail("Junit test dateParse fail"); + } catch (AnalysisException e) { + Assert.assertEquals(e.getMessage(), "%U not supported in date format string"); + } + try { + FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%u")); + fail("Junit test dateParse fail"); + } catch (AnalysisException e) { + Assert.assertEquals(e.getMessage(), "%u not supported in date format string"); + } + try { + FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%V")); + fail("Junit test dateParse fail"); + } catch (AnalysisException e) { + Assert.assertEquals(e.getMessage(), "%V not supported in date format string"); + } + try { + FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%w")); + fail("Junit test dateParse fail"); + } catch (AnalysisException e) { + Assert.assertEquals(e.getMessage(), "%w not supported in date format string"); + } + try { + FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%X")); + fail("Junit test dateParse fail"); + } catch (AnalysisException e) { + Assert.assertEquals(e.getMessage(), "%X not supported in date format string"); + } + } } diff --git a/run-fe-ut.sh b/run-fe-ut.sh index 831618a0b5..010b2b94d2 100755 --- a/run-fe-ut.sh +++ b/run-fe-ut.sh @@ -73,7 +73,7 @@ rm ${DORIS_HOME}/fe/build/ -rf rm ${DORIS_HOME}/fe/output/ -rf echo "******************************" -echo " Runing PaloBe Unittest " +echo " Runing DorisFe Unittest " echo "******************************" cd ${DORIS_HOME}/fe/