[Bug] function str_to_date()'s behavior on BE and FE is inconsistent (#4495)

Main CL:
1. Copy the code from BE to implement the `str_to_date()` function in FE. 
2. `str_to_date("2020-08-08", "%Y-%m-%d %H:%i:%s")` will return `2020-08-08 00:00:00` instead of `2020-08-08`.
This commit is contained in:
Mingyu Chen
2020-09-03 17:16:19 +08:00
committed by GitHub
parent d0d394ad7e
commit 5166a6c6bc
8 changed files with 588 additions and 46 deletions

View File

@ -1073,6 +1073,8 @@ static int check_word(const char* lib[], const char* str, const char* end, const
return pos;
}
// this method is exaclty same as fromDateFormatStr() in DateLiteral.java in FE
// change this method should also change that.
bool DateTimeValue::from_date_format_str(
const char* format, int format_len,
const char* value, int value_len,
@ -1315,14 +1317,14 @@ bool DateTimeValue::from_date_format_str(
date_part_used = true;
break;
case 'r':
if (from_date_format_str("%I:%i:%S %p", 11, val, val_end - val, &tmp)) {
if (!from_date_format_str("%I:%i:%S %p", 11, val, val_end - val, &tmp)) {
return false;
}
val = tmp;
time_part_used = true;
break;
case 'T':
if (from_date_format_str("%H:%i:%S", 8, val, val_end - val, &tmp)) {
if (!from_date_format_str("%H:%i:%S", 8, val, val_end - val, &tmp)) {
return false;
}
time_part_used = true;
@ -1363,6 +1365,33 @@ bool DateTimeValue::from_date_format_str(
}
}
// continue to iterate pattern if has
// to find out if it has time part.
while (ptr < end) {
if (*ptr == '%' && ptr + 1 < end) {
ptr++;
switch (*ptr++) {
case 'H':
case 'h':
case 'I':
case 'i':
case 'k':
case 'l':
case 'r':
case 's':
case 'S':
case 'p':
case 'T':
time_part_used = true;
break;
default:
break;
}
} else {
ptr++;
}
}
if (usa_time) {
if (_hour > 12 || _hour < 1) {
return false;
@ -1371,7 +1400,7 @@ bool DateTimeValue::from_date_format_str(
}
if (sub_val_end) {
*sub_val_end = val;
return 0;
return true;
}
// Year day
if (yearday > 0) {

View File

@ -521,7 +521,7 @@ TEST_F(DateTimeValueTest, from_date_format_str) {
value.to_string(str);
ASSERT_STREQ("2015-01-05 12:34:56", str);
// hour
// hour
format_str = "%Y-%m-%d %H %i %s";
value_str = "88-2-1 03 4 5";
ASSERT_TRUE(value.from_date_format_str(

View File

@ -58,6 +58,14 @@ mysql> select str_to_date('200442 Monday', '%X%V %W');
+-----------------------------------------+
| 2004-10-18 |
+-----------------------------------------+
mysql> select str_to_date("2020-09-01", "%Y-%m-%d %H:%i:%s");
+------------------------------------------------+
| str_to_date('2020-09-01', '%Y-%m-%d %H:%i:%s') |
+------------------------------------------------+
| 2020-09-01 00:00:00 |
+------------------------------------------------+
1 row in set (0.01 sec)
```
## keyword

View File

@ -30,7 +30,6 @@ under the License.
`DATETIME STR_TO_DATE(VARCHAR str, VARCHAR format)`
通过format指定的方式将str转化为DATE类型,如果转化结果不对返回NULL
支持的format格式与date_format一致
@ -58,6 +57,14 @@ mysql> select str_to_date('200442 Monday', '%X%V %W');
+-----------------------------------------+
| 2004-10-18 |
+-----------------------------------------+
mysql> select str_to_date("2020-09-01", "%Y-%m-%d %H:%i:%s");
+------------------------------------------------+
| str_to_date('2020-09-01', '%Y-%m-%d %H:%i:%s') |
+------------------------------------------------+
| 2020-09-01 00:00:00 |
+------------------------------------------------+
1 row in set (0.01 sec)
```
## keyword

View File

@ -22,12 +22,15 @@ import org.apache.doris.catalog.PrimitiveType;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.FeMetaVersion;
import org.apache.doris.common.InvalidFormatException;
import org.apache.doris.common.util.TimeUtils;
import org.apache.doris.thrift.TDateLiteral;
import org.apache.doris.thrift.TExprNode;
import org.apache.doris.thrift.TExprNodeType;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -41,7 +44,10 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.time.Year;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.util.regex.Pattern;
@ -66,6 +72,12 @@ public class DateLiteral extends LiteralExpr {
private static DateTimeFormatter DATE_TIME_FORMATTER_TWO_DIGIT = null;
private static DateTimeFormatter DATE_FORMATTER_TWO_DIGIT = null;
private static Map<String, Integer> MONTH_NAME_DICT = Maps.newHashMap();
private static Map<String, Integer> MONTH_ABBR_NAME_DICT = Maps.newHashMap();
private static Map<String, Integer> WEEK_DAY_NAME_DICT = Maps.newHashMap();
private static Map<String, Integer> WEEK_DAY_ABBR_NAME_DICT = Maps.newHashMap();
private static List<Integer> DAYS_IN_MONTH = Lists.newArrayList(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
static {
try {
DATE_TIME_FORMATTER = formatBuilder("%Y-%m-%d %H:%i:%s").toFormatter();
@ -76,10 +88,52 @@ public class DateLiteral extends LiteralExpr {
LOG.error("invalid date format", e);
System.exit(-1);
}
MONTH_NAME_DICT.put("january", 1);
MONTH_NAME_DICT.put("february", 2);
MONTH_NAME_DICT.put("march", 3);
MONTH_NAME_DICT.put("april", 4);
MONTH_NAME_DICT.put("may", 5);
MONTH_NAME_DICT.put("june", 6);
MONTH_NAME_DICT.put("july", 7);
MONTH_NAME_DICT.put("august", 8);
MONTH_NAME_DICT.put("september", 9);
MONTH_NAME_DICT.put("october", 10);
MONTH_NAME_DICT.put("november", 11);
MONTH_NAME_DICT.put("december", 12);
MONTH_ABBR_NAME_DICT.put("jan", 1);
MONTH_ABBR_NAME_DICT.put("feb", 2);
MONTH_ABBR_NAME_DICT.put("mar", 3);
MONTH_ABBR_NAME_DICT.put("apr", 4);
MONTH_ABBR_NAME_DICT.put("may", 5);
MONTH_ABBR_NAME_DICT.put("jun", 6);
MONTH_ABBR_NAME_DICT.put("jul", 7);
MONTH_ABBR_NAME_DICT.put("aug", 8);
MONTH_ABBR_NAME_DICT.put("sep", 9);
MONTH_ABBR_NAME_DICT.put("oct", 10);
MONTH_ABBR_NAME_DICT.put("nov", 11);
MONTH_ABBR_NAME_DICT.put("dec", 12);
WEEK_DAY_NAME_DICT.put("monday", 0);
WEEK_DAY_NAME_DICT.put("tuesday", 1);
WEEK_DAY_NAME_DICT.put("wednesday", 2);
WEEK_DAY_NAME_DICT.put("thursday", 3);
WEEK_DAY_NAME_DICT.put("friday", 4);
WEEK_DAY_NAME_DICT.put("saturday", 5);
WEEK_DAY_NAME_DICT.put("sunday", 6);
MONTH_ABBR_NAME_DICT.put("mon", 0);
MONTH_ABBR_NAME_DICT.put("tue", 1);
MONTH_ABBR_NAME_DICT.put("wed", 2);
MONTH_ABBR_NAME_DICT.put("thu", 3);
MONTH_ABBR_NAME_DICT.put("fri", 4);
MONTH_ABBR_NAME_DICT.put("sat", 5);
MONTH_ABBR_NAME_DICT.put("sun", 6);
}
//Regex used to determine if the TIME field exists int date_format
private static final Pattern HAS_TIME_PART = Pattern.compile("^.*[HhIiklrSsT]+.*$");
private static final Pattern HAS_TIME_PART = Pattern.compile("^.*[HhIiklrSsTp]+.*$");
//Date Literal persist type in meta
private enum DateLiteralType {
DATETIME(0),
@ -93,9 +147,9 @@ public class DateLiteral extends LiteralExpr {
public int value() {
return value;
}
}
}
private DateLiteral() {
public DateLiteral() {
super();
}
@ -423,7 +477,8 @@ public class DateLiteral extends LiteralExpr {
}
public static DateLiteral dateParser(String date, String pattern) throws AnalysisException {
LocalDateTime dateTime = formatBuilder(pattern).toFormatter().parseLocalDateTime(date);
DateTimeFormatter formatter = formatBuilder(pattern).toFormatter();
LocalDateTime dateTime = formatter.parseLocalDateTime(date);
DateLiteral dateLiteral = new DateLiteral(
dateTime.getYear(),
dateTime.getMonthOfYear(),
@ -632,4 +687,435 @@ public class DateLiteral extends LiteralExpr {
public int hashCode() {
return 31 * super.hashCode() + Objects.hashCode(unixTimestamp(TimeZone.getDefault()));
}
// parset the date string value in 'value' by 'format' pattern.
// return the next position to parse if hasSubVal is true.
// throw InvalidFormatException if encounter errors.
// this method is exaclty same as from_date_format_str() in be/src/runtime/datetime_value.cpp
// change this method should also change that.
public int fromDateFormatStr(String format, String value, boolean hasSubVal) throws InvalidFormatException {
int fp = 0; // pointer to the current format string
int fend = format.length(); // end of format string
int vp = 0; // pointer to the date string value
int vend = value.length(); // end of date string value
boolean datePartUsed = false;
boolean timePartUsed = false;
int dayPart = 0;
long weekday = -1;
long yearday = -1;
long weekNum = -1;
boolean strictWeekNumber = false;
boolean sundayFirst = false;
boolean strictWeekNumberYearType = false;
long strictWeekNumberYear = -1;
boolean usaTime = false;
char f;
while (fp < fend && vp < vend) {
// Skip space character
while (vp < vend && Character.isSpaceChar(value.charAt(vp))) {
vp++;
}
if (vp >= vend) {
break;
}
// Check switch
f = format.charAt(fp);
if (f == '%' && fp + 1 < fend) {
int tmp = 0;
long intValue = 0;
fp++;
f = format.charAt(fp);
fp++;
switch (f) {
// Year
case 'y':
// Year, numeric (two digits)
tmp = vp + Math.min(2, vend - vp);
intValue = strToLong(value.substring(vp, tmp));
intValue += intValue >= 70 ? 1900 : 2000;
this.year = intValue;
vp = tmp;
datePartUsed = true;
break;
case 'Y':
// Year, numeric, four digits
tmp = vp + Math.min(4, vend - vp);
intValue = strToLong(value.substring(vp, tmp));
if (tmp - vp <= 2) {
intValue += intValue >= 70 ? 1900 : 2000;
}
this.year = intValue;
vp = tmp;
datePartUsed = true;
break;
// Month
case 'm':
case 'c':
tmp = vp + Math.min(2, vend - vp);
intValue = strToLong(value.substring(vp, tmp));
this.month = intValue;
vp = tmp;
datePartUsed = true;
break;
case 'M': {
int nextPos = findWord(value, vp);
intValue = checkWord(MONTH_NAME_DICT, value.substring(vp, nextPos));
this.month = intValue;
vp = nextPos;
break;
}
case 'b': {
int nextPos = findWord(value, vp);
intValue = checkWord(MONTH_ABBR_NAME_DICT, value.substring(vp, nextPos));
this.month = intValue;
vp = nextPos;
break;
}
// Day
case 'd':
case 'e':
tmp = vp + Math.min(2, vend - vp);
intValue = strToLong(value.substring(vp, tmp));
this.day = intValue;
vp = tmp;
datePartUsed = true;
break;
case 'D':
tmp = vp + Math.min(2, vend - vp);
intValue = strToLong(value.substring(vp, tmp));
this.day = intValue;
vp = tmp + Math.min(2, vend - tmp);
datePartUsed = true;
break;
// Hour
case 'h':
case 'I':
case 'l':
usaTime = true;
// Fall through
case 'k':
case 'H':
tmp = findNumber(value, vp, 2);
intValue = strToLong(value.substring(vp, tmp));
this.hour = intValue;
vp = tmp;
timePartUsed = true;
break;
// Minute
case 'i':
tmp = vp + Math.min(2, vend - vp);
intValue = strToLong(value.substring(vp, tmp));
this.minute = intValue;
vp = tmp;
timePartUsed = true;
break;
// Second
case 's':
case 'S':
tmp = vp + Math.min(2, vend - vp);
intValue = strToLong(value.substring(vp, tmp));
this.second = intValue;
vp = tmp;
timePartUsed = true;
break;
// Micro second
case 'f':
// micro second is not supported, so just eat it and go one.
tmp = vp + Math.min(6, vend - vp);
vp = tmp;
break;
// AM/PM
case 'p':
if ((vend - vp) < 2 || Character.toUpperCase(value.charAt(vp + 1)) != 'M' || !usaTime) {
throw new InvalidFormatException("Invalid %p format");
}
if (Character.toUpperCase(value.charAt(vp)) == 'P') {
// PM
dayPart = 12;
}
timePartUsed = true;
vp += 2;
break;
// Weekday
case 'W': {
int nextPos = findWord(value, vp);
intValue = checkWord(WEEK_DAY_NAME_DICT, value.substring(vp, nextPos));
intValue++;
weekday = intValue;
datePartUsed = true;
break;
}
case 'a': {
int nextPos = findWord(value, vp);
intValue = checkWord(WEEK_DAY_NAME_DICT, value.substring(vp, nextPos));
intValue++;
weekday = intValue;
datePartUsed = true;
break;
}
case 'w':
tmp = vp + Math.min(1, vend - vp);
intValue = strToLong(value.substring(vp, tmp));
if (intValue >= 7) {
throw new InvalidFormatException("invalid day of week: " + intValue);
}
if (intValue == 0) {
intValue = 7;
}
weekday = intValue;
vp = tmp;
datePartUsed = true;
break;
case 'j':
tmp = vp + Math.min(3, vend - vp);
intValue = strToLong(value.substring(vp, tmp));
yearday = intValue;
vp = tmp;
datePartUsed = true;
break;
case 'u':
case 'v':
case 'U':
case 'V':
sundayFirst = (format.charAt(fp - 1) == 'U' || format.charAt(fp - 1) == 'V');
// Used to check if there is %x or %X
strictWeekNumber = (format.charAt(fp - 1) == 'V' || format.charAt(fp - 1) == 'v');
tmp = vp + Math.min(2, vend - vp);
intValue = Long.valueOf(value.substring(vp, tmp));
weekNum = intValue;
if (weekNum > 53 || (strictWeekNumber && weekNum == 0)) {
throw new InvalidFormatException("invalid num of week: " + weekNum);
}
vp = tmp;
datePartUsed = true;
break;
// strict week number, must be used with %V or %v
case 'x':
case 'X':
strictWeekNumberYearType = (format.charAt(fp - 1) == 'X');
tmp = vp + Math.min(4, vend - vp);
intValue = Long.valueOf(value.substring(vp, tmp));
strictWeekNumberYear = intValue;
vp = tmp;
datePartUsed = true;
break;
case 'r':
tmp = fromDateFormatStr("%I:%i:%S %p", value.substring(vp, vend), true);
vp = tmp;
timePartUsed = true;
break;
case 'T':
tmp = fromDateFormatStr("%H:%i:%S", value.substring(vp, vend), true);
vp = tmp;
timePartUsed = true;
break;
case '.':
while (vp < vend && Character.toString(value.charAt(vp)).matches("\\p{Punct}")) {
vp++;
}
break;
case '@':
while (vp < vend && Character.isLetter(value.charAt(vp))) {
vp++;
}
break;
case '#':
while (vp < vend && Character.isDigit(value.charAt(vp))) {
vp++;
}
break;
case '%': // %%, escape the %
if ('%' != value.charAt(vp)) {
throw new InvalidFormatException("invalid char after %: " + value.charAt(vp));
}
vp++;
break;
default:
throw new InvalidFormatException("Invalid format pattern: " + f);
}
} else if (format.charAt(fp) != ' ') {
if (format.charAt(fp) != value.charAt(vp)) {
throw new InvalidFormatException("Invalid char: " + value.charAt(vp) + ", expected: " + format.charAt(fp));
}
fp++;
vp++;
} else {
fp++;
}
}
// continue to iterate pattern if has
// to find out if it has time part.
while (fp < fend) {
f = format.charAt(fp);
if (f == '%' && fp + 1 < fend) {
fp++;
f = format.charAt(fp);
fp++;
switch (f) {
case 'H':
case 'h':
case 'I':
case 'i':
case 'k':
case 'l':
case 'r':
case 's':
case 'S':
case 'p':
case 'T':
timePartUsed = true;
break;
default:
break;
}
} else {
fp++;
}
}
if (usaTime) {
if (this.hour > 12 || this.hour < 1) {
throw new InvalidFormatException("Invalid hour: " + hour);
}
this.hour = (this.hour % 12) + dayPart;
}
if (hasSubVal) {
return vp;
}
// Year day
if (yearday > 0) {
long days = calcDaynr(this.year, 1, 1) + yearday - 1;
getDateFromDaynr(days);
}
// weekday
if (weekNum >= 0 && weekday > 0) {
// Check
if ((strictWeekNumber && (strictWeekNumberYear < 0
|| strictWeekNumberYearType != sundayFirst))
|| (!strictWeekNumber && strictWeekNumberYear >= 0)) {
throw new InvalidFormatException("invalid week number");
}
long days = calcDaynr(strictWeekNumber ? strictWeekNumberYear : this.year, 1, 1);
long weekday_b = calcWeekday(days, sundayFirst);
if (sundayFirst) {
days += ((weekday_b == 0) ? 0 : 7) - weekday_b + (weekNum - 1) * 7 + weekday % 7;
} else {
days += ((weekday_b <= 3) ? 0 : 7) - weekday_b + (weekNum - 1) * 7 + weekday - 1;
}
getDateFromDaynr(days);
}
// Compute timestamp type
if (datePartUsed) {
if (timePartUsed) {
this.type = Type.DATETIME;
} else {
this.type = Type.DATE;
}
}
return 0;
}
private long strToLong(String l) throws InvalidFormatException {
try {
return Long.valueOf(l);
} catch (NumberFormatException e) {
throw new InvalidFormatException(e.getMessage());
}
}
// calculate the number of days from year 0000-00-00 to year-month-day
private long calcDaynr(long year, long month, long day) {
long delsum = 0;
long y = year;
if (year == 0 && month == 0) {
return 0;
}
/* Cast to int to be able to handle month == 0 */
delsum = 365 * y + 31 * (month - 1) + day;
if (month <= 2) {
// No leap year
y--;
} else {
// This is great!!!
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
// 0, 0, 3, 3, 4, 4, 5, 5, 5, 6, 7, 8
delsum -= (month * 4 + 23) / 10;
}
// Every 400 year has 97 leap year, 100, 200, 300 are not leap year.
return delsum + y / 4 - y / 100 + y / 400;
}
private long calcWeekday(long dayNr, boolean isSundayFirstDay) {
return (dayNr + 5L + (isSundayFirstDay ? 1L : 0L)) % 7;
}
private void getDateFromDaynr(long daynr) throws InvalidFormatException {
if (daynr <= 0 || daynr > 3652424) {
throw new InvalidFormatException("Invalid days to year: " + daynr);
}
this.year = daynr / 365;
long daysBeforeYear = 0;
while (daynr < (daysBeforeYear = calcDaynr(this.year, 1, 1))) {
this.year--;
}
long daysOfYear = daynr - daysBeforeYear + 1;
int leapDay = 0;
if (Year.isLeap(this.year)) {
if (daysOfYear > 31 + 28) {
daysOfYear--;
if (daysOfYear == 31 + 28) {
leapDay = 1;
}
}
}
this.month = 1;
while (daysOfYear > DAYS_IN_MONTH.get((int) this.month)) {
daysOfYear -= DAYS_IN_MONTH.get((int) this.month);
this.month++;
}
this.day = daysOfYear + leapDay;
}
// find a word start from 'start' from value.
private int findWord(String value, int start) {
int p = start;
while (p < value.length() && Character.isLetter(value.charAt(p))) {
p++;
}
return p;
}
// find a number start from 'start' from value.
private int findNumber(String value, int start, int maxLen) {
int p = start;
int left = maxLen;
while (p < value.length() && Character.isDigit(value.charAt(p)) && left > 0) {
p++;
left--;
}
return p;
}
// check if the given value exist in dict, return dict value.
private int checkWord(Map<String, Integer> dict, String value) throws InvalidFormatException {
Integer i = dict.get(value.toLowerCase());
if (i != null) {
return i;
}
throw new InvalidFormatException("'" + value + "' is invalid");
}
}

View File

@ -0,0 +1,24 @@
// 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.common;
public class InvalidFormatException extends Exception {
public InvalidFormatException(String msg) {
super(msg);
}
}

View File

@ -27,6 +27,7 @@ import org.apache.doris.analysis.NullLiteral;
import org.apache.doris.analysis.StringLiteral;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.InvalidFormatException;
import org.apache.doris.common.util.TimeUtils;
import com.google.common.base.Preconditions;
@ -121,7 +122,14 @@ public class FEFunctions {
@FEFunction(name = "str_to_date", argTypes = { "VARCHAR", "VARCHAR" }, returnType = "DATETIME")
public static DateLiteral dateParse(StringLiteral date, StringLiteral fmtLiteral) throws AnalysisException {
return DateLiteral.dateParser(date.getStringValue(), fmtLiteral.getStringValue());
DateLiteral dateLiteral = new DateLiteral();
try {
dateLiteral.fromDateFormatStr(fmtLiteral.getStringValue(), date.getStringValue(), false);
return dateLiteral;
} catch (InvalidFormatException e) {
e.printStackTrace();
throw new AnalysisException(e.getMessage());
}
}
@FEFunction(name = "date_sub", argTypes = { "DATETIME", "INT" }, returnType = "DATETIME")

View File

@ -17,6 +17,9 @@
package org.apache.doris.rewrite;
import mockit.Expectations;
import mockit.Mocked;
import org.apache.doris.analysis.DateLiteral;
import org.apache.doris.analysis.DecimalLiteral;
import org.apache.doris.analysis.FloatLiteral;
@ -37,8 +40,6 @@ import java.time.ZoneId;
import java.util.Locale;
import java.util.TimeZone;
import mockit.Expectations;
import mockit.Mocked;
import static org.junit.Assert.fail;
public class FEFunctionsTest {
@ -190,6 +191,7 @@ public class FEFunctionsTest {
@Test
public void dateParseTest() {
try {
Assert.assertEquals("2019-05-09 00:00:00", FEFunctions.dateParse(new StringLiteral("2019-05-09"), new StringLiteral("%Y-%m-%d %H:%i:%s")).getStringValue());
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());
@ -202,47 +204,25 @@ public class FEFunctionsTest {
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());
Assert.assertEquals("0000-00-20", FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%D")).getStringValue());
Assert.assertEquals("0000-00-00", FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%U")).getStringValue());
Assert.assertEquals("0000-00-00", FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%u")).getStringValue());
Assert.assertEquals("0000-00-00", FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%V")).getStringValue());
Assert.assertEquals("0000-00-00", FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%w")).getStringValue());
Assert.assertEquals("0000-00-00", FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%x")).getStringValue());
Assert.assertEquals("0000-00-00", FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%X")).getStringValue());
Assert.assertEquals("2013-05-17 20:07:05", FEFunctions.dateParse(new StringLiteral("2013-05-17 08:07:05 PM"), new StringLiteral("%Y-%m-%d %r")).getStringValue());
Assert.assertEquals("2013-05-17 08:07:05", FEFunctions.dateParse(new StringLiteral("2013-05-17 08:07:05"), new StringLiteral("%Y-%m-%d %T")).getStringValue());
} catch (AnalysisException e) {
fail("Junit test dateParse fail");
e.printStackTrace();
fail("Junit test dateParse fail");
}
try {
FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%D"));
FEFunctions.dateParse(new StringLiteral("2013-05-17"), new StringLiteral("%W"));
fail("Junit test dateParse fail");
} catch (AnalysisException e) {
Assert.assertEquals(e.getMessage(),
"errCode = 2, detailMessage = %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(), "errCode = 2, detailMessage = %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(), "errCode = 2, detailMessage = %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(), "errCode = 2, detailMessage = %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(), "errCode = 2, detailMessage = %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(), "errCode = 2, detailMessage = %X not supported in date format string");
Assert.assertEquals(e.getMessage(), "errCode = 2, detailMessage = '' is invalid");
}
}