bool IsoSqlFormatParser::ParseDateTime()

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(&current_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;
}