in fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java [1278:1658]
public int fromDateFormatStr(String format, String value, boolean hasSubVal) throws InvalidFormatException {
int pFormat = 0; // pointer to the current format string
int endFormat = format.length(); // end of format string
int pValue = 0; // pointer to the date string value
int endValue = value.length(); // end of date string value
int partUsed = 0;
final int yearPart = 1 << 0;
final int monthPart = 1 << 1;
final int dayPart = 1 << 2;
final int weekdayPart = 1 << 3;
final int yeardayPart = 1 << 4;
final int weekNumPart = 1 << 5;
final int normalDatePart = yearPart | monthPart | dayPart;
final int specialDatePart = weekdayPart | yeardayPart | weekNumPart;
final int datePart = normalDatePart | specialDatePart;
final int hourPart = 1 << 6;
final int minutePart = 1 << 7;
final int secondPart = 1 << 8;
final int fracPart = 1 << 9;
final int timePart = hourPart | minutePart | secondPart | fracPart;
int halfDay = 0; // 0 for am/none, 12 for pm.
long weekday = -1;
long yearday = -1;
long weekNum = -1;
boolean strictWeekNumber = false;
boolean sundayFirst = false;
boolean strictWeekNumberYearType = false;
long strictWeekNumberYear = -1;
boolean hourSystem12 = false; // hour in [0..12] and with am/pm
char now;
while (pFormat < endFormat && pValue < endValue) {
// Skip space character
while (pValue < endValue && Character.isSpaceChar(value.charAt(pValue))) {
pValue++;
}
if (pValue >= endValue) {
break;
}
// Check switch
now = format.charAt(pFormat);
if (now == '%' && pFormat + 1 < endFormat) {
int tmp = 0;
long intValue = 0;
pFormat++;
now = format.charAt(pFormat);
pFormat++;
switch (now) {
// Year
case 'y':
// Year, numeric (two digits)
tmp = pValue + Math.min(2, endValue - pValue);
intValue = strToLong(value.substring(pValue, tmp));
intValue += intValue >= 70 ? 1900 : 2000;
this.year = intValue;
pValue = tmp;
partUsed |= yearPart;
break;
case 'Y':
// Year, numeric, four digits
tmp = pValue + Math.min(4, endValue - pValue);
intValue = strToLong(value.substring(pValue, tmp));
if (tmp - pValue <= 2) {
intValue += intValue >= 70 ? 1900 : 2000;
}
this.year = intValue;
pValue = tmp;
partUsed |= yearPart;
break;
// Month
case 'm':
case 'c':
tmp = pValue + Math.min(2, endValue - pValue);
intValue = strToLong(value.substring(pValue, tmp));
this.month = intValue;
pValue = tmp;
partUsed |= monthPart;
break;
case 'M': {
int nextPos = findWord(value, pValue);
intValue = checkWord(MONTH_NAME_DICT, value.substring(pValue, nextPos));
this.month = intValue;
pValue = nextPos;
partUsed |= monthPart;
break;
}
case 'b': {
int nextPos = findWord(value, pValue);
intValue = checkWord(MONTH_ABBR_NAME_DICT, value.substring(pValue, nextPos));
this.month = intValue;
pValue = nextPos;
partUsed |= monthPart;
break;
}
// Day
case 'd':
case 'e':
tmp = pValue + Math.min(2, endValue - pValue);
intValue = strToLong(value.substring(pValue, tmp));
this.day = intValue;
pValue = tmp;
partUsed |= dayPart;
break;
case 'D':
tmp = pValue + Math.min(2, endValue - pValue);
intValue = strToLong(value.substring(pValue, tmp));
this.day = intValue;
pValue = tmp + Math.min(2, endValue - tmp);
partUsed |= dayPart;
break;
// Hour
case 'h':
case 'I':
case 'l':
hourSystem12 = true;
partUsed |= hourPart;
case 'k': // CHECKSTYLE IGNORE THIS LINE: Fall through
case 'H':
tmp = findNumber(value, pValue, 2);
intValue = strToLong(value.substring(pValue, tmp));
this.hour = intValue;
pValue = tmp;
partUsed |= hourPart;
break;
// Minute
case 'i':
tmp = pValue + Math.min(2, endValue - pValue);
intValue = strToLong(value.substring(pValue, tmp));
this.minute = intValue;
pValue = tmp;
partUsed |= minutePart;
break;
// Second
case 's':
case 'S':
tmp = pValue + Math.min(2, endValue - pValue);
intValue = strToLong(value.substring(pValue, tmp));
this.second = intValue;
pValue = tmp;
partUsed |= secondPart;
break;
// Micro second
case 'f':
tmp = pValue;
// when there's still something to the end, fix the scale of ms.
while (tmp < endValue && Character.isDigit(value.charAt(tmp))) {
tmp += 1;
}
if (tmp - pValue > 6) {
int tmp2 = pValue + 6;
intValue = strToLong(value.substring(pValue, tmp2));
} else {
intValue = strToLong(value.substring(pValue, tmp));
}
this.microsecond = (long) (intValue * Math.pow(10, 6 - Math.min(6, tmp - pValue)));
partUsed |= fracPart;
pValue = tmp;
break;
// AM/PM
case 'p':
if ((endValue - pValue) < 2 || Character.toUpperCase(value.charAt(pValue + 1)) != 'M'
|| !hourSystem12) {
throw new InvalidFormatException("Invalid %p format");
}
if (Character.toUpperCase(value.charAt(pValue)) == 'P') {
// PM
halfDay = 12;
}
pValue += 2;
break;
// Weekday
case 'W': {
int nextPos = findWord(value, pValue);
intValue = checkWord(WEEK_DAY_NAME_DICT, value.substring(pValue, nextPos));
intValue++;
weekday = intValue;
partUsed |= weekdayPart;
break;
}
case 'a': {
int nextPos = findWord(value, pValue);
intValue = checkWord(WEEK_DAY_NAME_DICT, value.substring(pValue, nextPos));
intValue++;
weekday = intValue;
partUsed |= weekdayPart;
break;
}
case 'w':
tmp = pValue + Math.min(1, endValue - pValue);
intValue = strToLong(value.substring(pValue, tmp));
if (intValue >= 7) {
throw new InvalidFormatException("invalid day of week: " + intValue);
}
if (intValue == 0) {
intValue = 7;
}
weekday = intValue;
pValue = tmp;
partUsed |= weekdayPart;
break;
case 'j':
tmp = pValue + Math.min(3, endValue - pValue);
intValue = strToLong(value.substring(pValue, tmp));
yearday = intValue;
pValue = tmp;
partUsed |= yeardayPart;
break;
case 'u':
case 'v':
case 'U':
case 'V':
sundayFirst = (format.charAt(pFormat - 1) == 'U' || format.charAt(pFormat - 1) == 'V');
// Used to check if there is %x or %X
strictWeekNumber = (format.charAt(pFormat - 1) == 'V' || format.charAt(pFormat - 1) == 'v');
tmp = pValue + Math.min(2, endValue - pValue);
intValue = Long.valueOf(value.substring(pValue, tmp));
weekNum = intValue;
if (weekNum > 53 || (strictWeekNumber && weekNum == 0)) {
throw new InvalidFormatException("invalid num of week: " + weekNum);
}
pValue = tmp;
partUsed |= weekNumPart;
break;
// strict week number, must be used with %V or %v
case 'x':
case 'X':
strictWeekNumberYearType = (format.charAt(pFormat - 1) == 'X');
tmp = pValue + Math.min(4, endValue - pValue);
intValue = Long.valueOf(value.substring(pValue, tmp));
strictWeekNumberYear = intValue;
pValue = tmp;
partUsed |= weekNumPart;
break;
case 'r':
tmp = fromDateFormatStr("%I:%i:%S %p", value.substring(pValue, endValue), true);
pValue = tmp;
partUsed |= timePart;
break;
case 'T':
tmp = fromDateFormatStr("%H:%i:%S", value.substring(pValue, endValue), true);
pValue = tmp;
partUsed |= timePart;
break;
case '.':
while (pValue < endValue && Character.toString(value.charAt(pValue)).matches("\\p{Punct}")) {
pValue++;
}
break;
case '@':
while (pValue < endValue && Character.isLetter(value.charAt(pValue))) {
pValue++;
}
break;
case '#':
while (pValue < endValue && Character.isDigit(value.charAt(pValue))) {
pValue++;
}
break;
case '%': // %%, escape the %
if ('%' != value.charAt(pValue)) {
throw new InvalidFormatException("invalid char after %: " + value.charAt(pValue));
}
pValue++;
break;
default:
throw new InvalidFormatException("Invalid format pattern: " + now);
}
} else if (format.charAt(pFormat) != ' ') {
if (format.charAt(pFormat) != value.charAt(pValue)) {
throw new InvalidFormatException("Invalid char: " + value.charAt(pValue) + ", expected: "
+ format.charAt(pFormat));
}
pFormat++;
pValue++;
} else {
pFormat++;
}
}
// continue to iterate pattern if has
// to find out if it has time part.
while (pFormat < endFormat) {
now = format.charAt(pFormat);
if (now == '%' && pFormat + 1 < endFormat) {
pFormat++;
now = format.charAt(pFormat);
pFormat++;
switch (now) {
case 'H':
case 'h':
case 'I':
case 'i':
case 'k':
case 'l':
case 'r':
case 's':
case 'S':
case 'p':
case 'T':
partUsed |= timePart;
break;
default:
break;
}
} else {
pFormat++;
}
}
if (partUsed == 0) {
throw new InvalidFormatException("Nothing for legal Date: " + value);
}
if (hourSystem12) {
if (this.hour > 12 || this.hour < 1) {
throw new InvalidFormatException("Invalid hour: " + hour);
}
this.hour = (this.hour % 12) + halfDay;
}
if (hasSubVal) {
return pValue;
}
// 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 weekdayB = calcWeekday(days, sundayFirst);
if (sundayFirst) {
days += ((weekdayB == 0) ? 0 : 7) - weekdayB + (weekNum - 1) * 7 + weekday % 7;
} else {
days += ((weekdayB <= 3) ? 0 : 7) - weekdayB + (weekNum - 1) * 7 + weekday - 1;
}
getDateFromDaynr(days);
}
// complete default month/day
if ((partUsed & ~normalDatePart) == 0) { // only date here
if ((partUsed & dayPart) == 0) {
day = 1;
if ((partUsed & monthPart) == 0) {
month = 1;
}
}
}
// Compute timestamp type
if ((partUsed & datePart) != 0) { // Ymd part only
if ((partUsed & fracPart) != 0) {
this.type = Type.DATETIMEV2_WITH_MAX_SCALAR;
} else if ((partUsed & timePart) != 0) {
this.type = ScalarType.getDefaultDateType(Type.DATETIME);
} else {
this.type = ScalarType.getDefaultDateType(Type.DATE);
}
}
if (checkRange() || checkDate()) {
throw new InvalidFormatException("Invalid format");
}
return 0;
}