Fix date_format function in fe (#1082)
This commit is contained in:
@ -231,6 +231,13 @@ under the License.
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.baidu/jprotobuf -->
|
||||
<dependency>
|
||||
<groupId>com.baidu</groupId>
|
||||
|
||||
@ -35,8 +35,12 @@ import org.apache.logging.log4j.Logger;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.ParseException;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.DateTimeFormatterBuilder;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* compute functions in FE.
|
||||
@ -65,7 +69,7 @@ public class FEFunctions {
|
||||
|
||||
@FEFunction(name = "date_format", argTypes = { "DATETIME", "VARCHAR" }, returnType = "VARCHAR")
|
||||
public static StringLiteral dateFormat(LiteralExpr date, StringLiteral fmtLiteral) throws AnalysisException {
|
||||
String result = DateFormatUtils.format(new Date(getTime(date)), fmtLiteral.getStringValue());
|
||||
String result = dateFormat(new Date(getTime(date)), fmtLiteral.getStringValue());
|
||||
return new StringLiteral(result);
|
||||
}
|
||||
|
||||
@ -121,6 +125,210 @@ public class FEFunctions {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static int calFirstWeekDay(int year, int firstWeekDay) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(year, Calendar.JANUARY,1);
|
||||
int firstDay = 1;
|
||||
calendar.set(Calendar.DAY_OF_MONTH, firstDay);
|
||||
while (calendar.get(Calendar.DAY_OF_WEEK) != firstWeekDay) {
|
||||
calendar.set(Calendar.DAY_OF_MONTH, ++firstDay);
|
||||
}
|
||||
return firstDay;
|
||||
}
|
||||
|
||||
private static String dateFormat(Date date, String pattern) {
|
||||
DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder();
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
boolean escaped = false;
|
||||
for (int i = 0; i < pattern.length(); i++) {
|
||||
char character = pattern.charAt(i);
|
||||
if (escaped) {
|
||||
switch (character) {
|
||||
case 'a': // %a Abbreviated weekday name (Sun..Sat)
|
||||
formatterBuilder.appendDayOfWeekShortText();
|
||||
break;
|
||||
case 'b': // %b Abbreviated month name (Jan..Dec)
|
||||
formatterBuilder.appendMonthOfYearShortText();
|
||||
break;
|
||||
case 'c': // %c Month, numeric (0..12)
|
||||
formatterBuilder.appendMonthOfYear(1);
|
||||
break;
|
||||
case 'd': // %d Day of the month, numeric (00..31)
|
||||
formatterBuilder.appendDayOfMonth(2);
|
||||
break;
|
||||
case 'e': // %e Day of the month, numeric (0..31)
|
||||
formatterBuilder.appendDayOfMonth(1);
|
||||
break;
|
||||
case 'f': // %f Microseconds (000000..999999)
|
||||
formatterBuilder.appendFractionOfSecond(6, 9);
|
||||
break;
|
||||
case 'H': // %H Hour (00..23)
|
||||
formatterBuilder.appendHourOfDay(2);
|
||||
break;
|
||||
case 'h': // %h Hour (01..12)
|
||||
case 'I': // %I Hour (01..12)
|
||||
formatterBuilder.appendClockhourOfHalfday(2);
|
||||
break;
|
||||
case 'i': // %i Minutes, numeric (00..59)
|
||||
formatterBuilder.appendMinuteOfHour(2);
|
||||
break;
|
||||
case 'j': // %j Day of year (001..366)
|
||||
formatterBuilder.appendDayOfYear(3);
|
||||
break;
|
||||
case 'k': // %k Hour (0..23)
|
||||
formatterBuilder.appendHourOfDay(1);
|
||||
break;
|
||||
case 'l': // %l Hour (1..12)
|
||||
formatterBuilder.appendClockhourOfHalfday(1);
|
||||
break;
|
||||
case 'M': // %M Month name (January..December)
|
||||
formatterBuilder.appendMonthOfYearText();
|
||||
break;
|
||||
case 'm': // %m Month, numeric (00..12)
|
||||
formatterBuilder.appendMonthOfYear(2);
|
||||
break;
|
||||
case 'p': // %p AM or PM
|
||||
formatterBuilder.appendHalfdayOfDayText();
|
||||
break;
|
||||
case 'r': // %r Time, 12-hour (hh:mm:ss followed by AM or PM)
|
||||
formatterBuilder.appendClockhourOfHalfday(2)
|
||||
.appendLiteral(':')
|
||||
.appendMinuteOfHour(2)
|
||||
.appendLiteral(':')
|
||||
.appendSecondOfMinute(2)
|
||||
.appendLiteral(' ')
|
||||
.appendHalfdayOfDayText();
|
||||
break;
|
||||
case 'S': // %S Seconds (00..59)
|
||||
case 's': // %s Seconds (00..59)
|
||||
formatterBuilder.appendSecondOfMinute(2);
|
||||
break;
|
||||
case 'T': // %T Time, 24-hour (hh:mm:ss)
|
||||
formatterBuilder.appendHourOfDay(2)
|
||||
.appendLiteral(':')
|
||||
.appendMinuteOfHour(2)
|
||||
.appendLiteral(':')
|
||||
.appendSecondOfMinute(2);
|
||||
break;
|
||||
case 'V': // %V Week (01..53), where Sunday is the first day of the week; used with %X
|
||||
{
|
||||
int week;
|
||||
calendar.setTime(date);
|
||||
int firstSunday = calFirstWeekDay(calendar.get(Calendar.YEAR), Calendar.SUNDAY);
|
||||
if (calendar.get(Calendar.DATE) <= 7 && calendar.get(Calendar.MONTH) == Calendar.JANUARY
|
||||
&& calendar.get(Calendar.DATE) >= firstSunday) {
|
||||
week = 1;
|
||||
} else {
|
||||
calendar.add(Calendar.DATE, -7);
|
||||
week = calendar.get(Calendar.WEEK_OF_YEAR) +
|
||||
(calFirstWeekDay(calendar.get(Calendar.YEAR), Calendar.SUNDAY) == 1 ? 1 : 0);
|
||||
}
|
||||
formatterBuilder.appendLiteral(String.format("%02d", week));
|
||||
break;
|
||||
}
|
||||
case 'v': // %v Week (01..53), where Monday is the first day of the week; used with %x
|
||||
formatterBuilder.appendWeekOfWeekyear(2);
|
||||
break;
|
||||
case 'X': // %X Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V
|
||||
calendar.setTime(date);
|
||||
if(calendar.get(Calendar.MONTH) == Calendar.JANUARY &&
|
||||
calendar.get(Calendar.DATE) < calFirstWeekDay(calendar.get(Calendar.YEAR), Calendar.SUNDAY)) {
|
||||
formatterBuilder.appendLiteral(String.valueOf(calendar.get(Calendar.YEAR) - 1));
|
||||
} else {
|
||||
formatterBuilder.appendLiteral(String.valueOf(calendar.get(Calendar.YEAR)));
|
||||
}
|
||||
break;
|
||||
case 'x': // %x Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v
|
||||
formatterBuilder.appendWeekyear(4, 4);
|
||||
break;
|
||||
case 'W': // %W Weekday name (Sunday..Saturday)
|
||||
formatterBuilder.appendDayOfWeekText();
|
||||
break;
|
||||
case 'w': // %w Day of the week (0=Sunday..6=Saturday)
|
||||
calendar.setTime(date);
|
||||
calendar.setFirstDayOfWeek(Calendar.SUNDAY);
|
||||
formatterBuilder.appendLiteral(String.valueOf(calendar.get(Calendar.DAY_OF_WEEK) - 1));
|
||||
break;
|
||||
case 'y': // %y Year, numeric (two digits)
|
||||
int PIVOT_YEAR = 2020;
|
||||
formatterBuilder.appendTwoDigitYear(PIVOT_YEAR);
|
||||
break;
|
||||
case 'Y': // %Y Year, numeric, four digits
|
||||
formatterBuilder.appendYear(4, 4);
|
||||
break;
|
||||
case 'D': // %D Day of the month with English suffix (0th, 1st, 2nd, 3rd, …)
|
||||
calendar.setTime(date);
|
||||
int day = calendar.get(Calendar.DAY_OF_MONTH);
|
||||
if (day >= 10 && day <= 19) {
|
||||
formatterBuilder.appendLiteral(String.valueOf(day) + "th");
|
||||
} else {
|
||||
switch (day % 10) {
|
||||
case 1:
|
||||
formatterBuilder.appendLiteral(String.valueOf(day) + "st");
|
||||
break;
|
||||
case 2:
|
||||
formatterBuilder.appendLiteral(String.valueOf(day) + "nd");
|
||||
break;
|
||||
case 3:
|
||||
formatterBuilder.appendLiteral(String.valueOf(day) + "rd");
|
||||
break;
|
||||
default:
|
||||
formatterBuilder.appendLiteral(String.valueOf(day) + "th");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'U': // %U Week (00..53), where Sunday is the first day of the week
|
||||
calendar.setTime(date);
|
||||
if (calendar.get(Calendar.DATE) <= 7 && calendar.get(Calendar.MONTH) == Calendar.JANUARY) {
|
||||
int firstSunday = calFirstWeekDay(calendar.get(Calendar.YEAR), Calendar.SUNDAY);
|
||||
formatterBuilder.appendLiteral(String.format("%02d",
|
||||
((calendar.get(Calendar.DATE) < firstSunday && firstSunday != 1) ? 0 : 1)));
|
||||
} else {
|
||||
calendar.add(Calendar.DATE, -7);
|
||||
calendar.setFirstDayOfWeek(Calendar.SUNDAY);
|
||||
formatterBuilder.appendLiteral(String.format("%02d",
|
||||
calendar.get(Calendar.WEEK_OF_YEAR)
|
||||
+ (calFirstWeekDay(calendar.get(Calendar.YEAR), Calendar.SUNDAY) == 1 ? 1 : 0)));
|
||||
}
|
||||
break;
|
||||
case 'u': // %u Week (00..53), where Monday is the first day of the week
|
||||
{
|
||||
calendar.setTime(date);
|
||||
int week;
|
||||
int firstMonday = calFirstWeekDay(calendar.get(Calendar.YEAR), Calendar.MONDAY);
|
||||
if (calendar.get(Calendar.DATE) <= 7 && calendar.get(Calendar.MONTH) == Calendar.JANUARY) {
|
||||
week = (calendar.get(Calendar.DATE) >= firstMonday || firstMonday == 1) ? 1 : 0 ;
|
||||
week += (firstMonday >= 5 ? 1 : 0);
|
||||
} else {
|
||||
calendar.add(Calendar.DATE, -7);
|
||||
calendar.setFirstDayOfWeek(Calendar.MONDAY);
|
||||
week = calendar.get(Calendar.WEEK_OF_YEAR) + ((firstMonday >= 5 || firstMonday == 1) ? 1 : 0);
|
||||
}
|
||||
formatterBuilder.appendLiteral(String.format("%02d", week));
|
||||
break;
|
||||
}
|
||||
case '%': // %% A literal “%” character
|
||||
formatterBuilder.appendLiteral('%');
|
||||
break;
|
||||
default: // %<x> The literal character represented by <x>
|
||||
formatterBuilder.appendLiteral(character);
|
||||
break;
|
||||
}
|
||||
escaped = false;
|
||||
}
|
||||
else if (character == '%') {
|
||||
escaped = true;
|
||||
}
|
||||
else {
|
||||
formatterBuilder.appendLiteral(character);
|
||||
}
|
||||
}
|
||||
DateTimeFormatter formatter = formatterBuilder.toFormatter();
|
||||
return formatter.withLocale(Locale.US).print(date.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@ -19,6 +19,7 @@ package org.apache.doris.rewrite;
|
||||
|
||||
import org.apache.doris.analysis.DateLiteral;
|
||||
import org.apache.doris.analysis.IntLiteral;
|
||||
import org.apache.doris.analysis.StringLiteral;
|
||||
import org.apache.doris.catalog.Type;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
|
||||
@ -41,4 +42,49 @@ public class FEFunctionsTest {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dateFormatUtilTest() {
|
||||
try {
|
||||
Assert.assertEquals("19670102,196701,196701,0101", FEFunctions.dateFormat(new DateLiteral("1967-01-02 13:04:05", Type.DATETIME), new StringLiteral("%Y%m%d,%X%V,%x%v,%U%u")).getStringValue());
|
||||
Assert.assertEquals("19960105,199553,199601,0001", FEFunctions.dateFormat(new DateLiteral("1996-01-05 13:04:05", Type.DATETIME), new StringLiteral("%Y%m%d,%X%V,%x%v,%U%u")).getStringValue());
|
||||
|
||||
Assert.assertEquals("2017-01-01,01,00", FEFunctions.dateFormat(new DateLiteral("2017-01-01 13:04:05", Type.DATETIME), new StringLiteral("%Y-%m-%d,%U,%u")).getStringValue());
|
||||
Assert.assertEquals("201753,201752,5352", FEFunctions.dateFormat(new DateLiteral("2017-12-31 13:04:05", Type.DATETIME),new StringLiteral("%X%V,%x%v,%U%u")).getStringValue());
|
||||
|
||||
DateLiteral testDate = new DateLiteral("2001-01-09 13:04:05", Type.DATETIME);
|
||||
Assert.assertEquals("Tue", FEFunctions.dateFormat(testDate, new StringLiteral("%a")).getStringValue());
|
||||
Assert.assertEquals("Jan", FEFunctions.dateFormat(testDate, new StringLiteral("%b")).getStringValue());
|
||||
Assert.assertEquals("1", FEFunctions.dateFormat(testDate, new StringLiteral("%c")).getStringValue());
|
||||
Assert.assertEquals("09", FEFunctions.dateFormat(testDate, new StringLiteral("%d")).getStringValue());
|
||||
Assert.assertEquals("9", FEFunctions.dateFormat(testDate, new StringLiteral("%e")).getStringValue());
|
||||
Assert.assertEquals("13", FEFunctions.dateFormat(testDate, new StringLiteral("%H")).getStringValue());
|
||||
Assert.assertEquals("01", FEFunctions.dateFormat(testDate, new StringLiteral("%h")).getStringValue());
|
||||
Assert.assertEquals("01", FEFunctions.dateFormat(testDate, new StringLiteral("%I")).getStringValue());
|
||||
Assert.assertEquals("04", FEFunctions.dateFormat(testDate, new StringLiteral("%i")).getStringValue());
|
||||
Assert.assertEquals("009", FEFunctions.dateFormat(testDate, new StringLiteral("%j")).getStringValue());
|
||||
Assert.assertEquals("13", FEFunctions.dateFormat(testDate, new StringLiteral("%k")).getStringValue());
|
||||
Assert.assertEquals("1", FEFunctions.dateFormat(testDate, new StringLiteral("%l")).getStringValue());
|
||||
Assert.assertEquals("January", FEFunctions.dateFormat(testDate, new StringLiteral("%M")).getStringValue());
|
||||
Assert.assertEquals( "01", FEFunctions.dateFormat(testDate, new StringLiteral("%m")).getStringValue());
|
||||
Assert.assertEquals("PM", FEFunctions.dateFormat(testDate, new StringLiteral("%p")).getStringValue());
|
||||
Assert.assertEquals("01:04:05 PM", FEFunctions.dateFormat(testDate, new StringLiteral("%r")).getStringValue());
|
||||
Assert.assertEquals("05", FEFunctions.dateFormat(testDate, new StringLiteral("%S")).getStringValue());
|
||||
Assert.assertEquals("05", FEFunctions.dateFormat(testDate, new StringLiteral("%s")).getStringValue());
|
||||
Assert.assertEquals("13:04:05", FEFunctions.dateFormat(testDate, new StringLiteral("%T")).getStringValue());
|
||||
Assert.assertEquals("02", FEFunctions.dateFormat(testDate, new StringLiteral("%v")).getStringValue());
|
||||
Assert.assertEquals("Tuesday", FEFunctions.dateFormat(testDate, new StringLiteral("%W")).getStringValue());
|
||||
Assert.assertEquals("2", FEFunctions.dateFormat(testDate, new StringLiteral("%w")).getStringValue());
|
||||
Assert.assertEquals("2001", FEFunctions.dateFormat(testDate, new StringLiteral("%Y")).getStringValue());
|
||||
Assert.assertEquals("01", FEFunctions.dateFormat(testDate, new StringLiteral("%y")).getStringValue());
|
||||
Assert.assertEquals("%", FEFunctions.dateFormat(testDate, new StringLiteral("%%")).getStringValue());
|
||||
Assert.assertEquals("foo", FEFunctions.dateFormat(testDate, new StringLiteral("foo")).getStringValue());
|
||||
Assert.assertEquals("g", FEFunctions.dateFormat(testDate, new StringLiteral("%g")).getStringValue());
|
||||
Assert.assertEquals("4", FEFunctions.dateFormat(testDate, new StringLiteral("%4")).getStringValue());
|
||||
Assert.assertEquals("2001 02" ,FEFunctions.dateFormat(testDate, new StringLiteral("%x %v")).getStringValue());
|
||||
Assert.assertEquals("9th" ,FEFunctions.dateFormat(testDate, new StringLiteral("%D")).getStringValue());
|
||||
} catch (AnalysisException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user