in be/src/runtime/datetime-iso-sql-format-parser.cc [30:263]
bool IsoSqlFormatParser::ParseDateTime(const char* input_str, int input_len,
const DateTimeFormatContext& dt_ctx, DateTimeParseResult* result) {
DCHECK(dt_ctx.toks.size() > 0);
DCHECK(dt_ctx.current_time != nullptr);
DCHECK(dt_ctx.current_time->HasDateAndTime());
DCHECK(result != nullptr);
DCHECK(result->hour == 0);
if (input_str == nullptr || input_len <= 0) return false;
int day_in_year = -1;
Iso8601WeekBasedDateParseResult iso8601_week_based_date_values;
const char* current_pos = input_str;
const char* end_pos = input_str + input_len;
for (int i = 0; i < dt_ctx.toks.size(); ++i) {
const DateTimeFormatToken* tok = &dt_ctx.toks[i];
if (current_pos >= end_pos) {
// Accept empty text tokens at the end of the format.
if (tok->type == TEXT && tok->len == 0) continue;
return false;
}
if (tok->type == SEPARATOR) {
if (dt_ctx.fx_modifier) {
DCHECK(tok->len == 1);
if (*current_pos != *tok->val) return false;
++current_pos;
continue;
} else {
bool res = ProcessSeparatorSequence(¤t_pos, end_pos, dt_ctx, &i);
if (!res || current_pos >= end_pos) return res;
DCHECK(i < dt_ctx.toks.size());
// Next token, following the separator sequence.
tok = &dt_ctx.toks[i];
}
}
if (tok->type == TEXT) {
const char* format_it = tok->val;
const char* format_end = tok->val + tok->len;
while (format_it < format_end && current_pos < end_pos) {
char format_char_to_compare = GetNextCharFromTextToken(&format_it, tok);
if (format_char_to_compare != *current_pos) return false;
++format_it;
++current_pos;
}
if (format_it < format_end) return false;
continue;
}
const char* token_end_pos =
FindEndOfToken(current_pos, end_pos - current_pos, *tok, dt_ctx.fx_modifier);
if (token_end_pos == nullptr) return false;
int token_len = token_end_pos - current_pos;
if (dt_ctx.fx_modifier && !tok->fm_modifier && token_len != tok->len) return false;
switch(tok->type) {
case YEAR: {
if (!ParseAndValidate(current_pos, token_len, 0, 9999, &result->year)) {
return false;
}
if (token_len < 4) {
result->year = PrefixYearFromCurrentYear(result->year, token_len,
dt_ctx.current_time->date().year());
}
break;
}
case ROUND_YEAR: {
if (!ParseAndValidate(current_pos, token_len, 0, 9999, &result->year)) {
return false;
}
if (token_len == 2) {
result->year = RoundYearFromCurrentYear(result->year,
dt_ctx.current_time->date().year());
}
if (token_len == 3 || token_len == 1) {
result->year = PrefixYearFromCurrentYear(result->year, token_len,
dt_ctx.current_time->date().year());
}
break;
}
case MONTH_IN_YEAR: {
if (!ParseAndValidate(current_pos, token_len, 1, 12, &result->month)) {
return false;
}
break;
}
case MONTH_NAME:
case MONTH_NAME_SHORT: {
if (!ParseMonthNameToken(*tok, current_pos, &token_end_pos, dt_ctx.fx_modifier,
&result->month)) {
return false;
}
break;
}
case DAY_NAME:
case DAY_NAME_SHORT: {
if (!ParseDayNameToken(*tok, current_pos, &token_end_pos, dt_ctx.fx_modifier,
&iso8601_week_based_date_values.day_of_week)) {
return false;
}
break;
}
case DAY_IN_MONTH: {
if (!ParseAndValidate(current_pos, token_len, 1, 31, &result->day)) return false;
break;
}
case DAY_IN_YEAR: {
if (!ParseAndValidate(current_pos, token_len, 1, 366, &day_in_year)) return false;
// Can't figure out the value of MONTH_IN_YEAR and DAY_IN_MONTH here as YEAR
// token is also required for that and it might come later in the input string.
break;
}
case HOUR_IN_HALF_DAY: {
int hour;
if (!ParseAndValidate(current_pos, token_len, 1, 12, &hour)) return false;
if (hour == 12) hour = 0;
// Note the addition instead of assignment here. PM and HOUR_IN_HALF_DAY can be
// in any order in the format token list and PM might add another 12 hours.
result->hour += hour;
break;
}
case HOUR_IN_DAY: {
if (!ParseAndValidate(current_pos, token_len, 0, 23, &result->hour)) return false;
break;
}
case MINUTE_IN_HOUR: {
if (!ParseAndValidate(current_pos, token_len, 0, 59, &result->minute)) {
return false;
}
break;
}
case SECOND_IN_MINUTE: {
if (!ParseAndValidate(current_pos, token_len, 0, 59, &result->second)) {
return false;
}
break;
}
case SECOND_IN_DAY: {
int second_in_day;
if (!ParseAndValidate(current_pos, token_len, 0, 86399, &second_in_day)) {
return false;
}
result->second = second_in_day % 60;
int minutes_in_day = second_in_day / 60;
result->minute = minutes_in_day % 60;
result->hour = minutes_in_day / 60;
break;
}
case FRACTION: {
if (!ParseFractionToken(current_pos, token_len, result)) return false;
break;
}
case MERIDIEM_INDICATOR: {
// Input has already been validated in ParseMeridiemIndicatorFromInput().
string indicator(current_pos, token_len);
boost::to_upper(indicator);
if (indicator == "PM" || indicator == "P.M.") result->hour += 12;
break;
}
case TIMEZONE_HOUR: {
// Deliberately ignore the timezone offsets.
int dummy_result;
if (!ParseAndValidate(current_pos, token_len, -15, 15, &dummy_result)) {
return false;
}
break;
}
case TIMEZONE_MIN: {
// Deliberately ignore the timezone offsets.
int dummy_result;
if (!ParseAndValidate(current_pos, token_len, 0, 59, &dummy_result)) {
return false;
}
break;
}
case ISO8601_TIME_INDICATOR:
case ISO8601_ZULU_INDICATOR: {
DCHECK(token_len == 1);
if (toupper(*current_pos) != toupper(*tok->val)) return false;
break;
}
case ISO8601_WEEK_NUMBERING_YEAR: {
if (!ParseAndValidate(current_pos, token_len, 0, 9999,
&iso8601_week_based_date_values.year)) {
return false;
}
if (token_len < 4) {
iso8601_week_based_date_values.year = PrefixYearFromCurrentYear(
iso8601_week_based_date_values.year, token_len,
GetIso8601WeekNumberingYear(dt_ctx.current_time));
}
break;
}
case ISO8601_WEEK_OF_YEAR: {
if (!ParseAndValidate(current_pos, token_len, 1, 53,
&iso8601_week_based_date_values.week_of_year)) {
return false;
}
break;
}
case ISO8601_DAY_OF_WEEK: {
if (!ParseAndValidate(current_pos, token_len, 1, 7,
&iso8601_week_based_date_values.day_of_week)) {
return false;
}
break;
}
default: {
return false;
}
}
current_pos = token_end_pos;
}
// If the format string is over but there are tokens left in the input.
if (current_pos < end_pos) return false;
// Get month and day values from "day in year" and year tokens
if (day_in_year != -1) {
DCHECK(result->year >= 0 && result->year <= 9999);
if (!GetMonthAndDayFromDaysSinceJan1(result->year, day_in_year - 1, &result->month,
&result->day)) {
return false;
}
}
if (iso8601_week_based_date_values.IsSet()) {
if (!iso8601_week_based_date_values.ExtractYearMonthDay(result)) return false;
}
return true;
}