Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions fe/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
210 changes: 209 additions & 1 deletion fe/src/main/java/org/apache/doris/rewrite/FEFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@
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.
Expand Down Expand Up @@ -65,7 +69,7 @@ public static DateLiteral dateAdd(LiteralExpr date, LiteralExpr day) throws Anal

@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);
}

Expand Down Expand Up @@ -121,6 +125,210 @@ private static long getTime(LiteralExpr expr) throws AnalysisException {
}
}


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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Locale.US?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this statement, AM will be changed to "上午"

}

/**
------------------------------------------------------------------------------
*/
Expand Down
46 changes: 46 additions & 0 deletions fe/src/test/java/org/apache/doris/rewrite/FEFunctionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

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;

Expand All @@ -41,4 +42,49 @@ public void unixtimestampTest() {
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();
}
}
}