in be/src/runtime/datetime-simple-date-format-parser.cc [430:535]
bool SimpleDateFormatParser::ParseDateTime(const char* str, int str_len,
const DateTimeFormatContext& dt_ctx, DateTimeParseResult* dt_result) {
DCHECK(dt_ctx.fmt_len > 0);
DCHECK(dt_ctx.toks.size() > 0);
DCHECK(dt_result != NULL);
if (str_len <= 0 || str_len < dt_ctx.fmt_len || str == NULL) return false;
StringParser::ParseResult status;
// Keep track of the number of characters we need to shift token positions by.
// Variable-length tokens will result in values > 0;
int shift_len = 0;
for (const DateTimeFormatToken& tok: dt_ctx.toks) {
const char* tok_val = str + tok.pos + shift_len;
if (tok.type == SEPARATOR) {
if (UNLIKELY(*tok_val != *tok.val)) return false;
continue;
}
int tok_len = tok.len;
const char* str_end = str + str_len;
// In case of single-character tokens we scan ahead to the next separator.
if (UNLIKELY(tok_len == 1)) {
while ((tok_val + tok_len < str_end) && isdigit(*(tok_val + tok_len))) {
++tok_len;
++shift_len;
}
}
switch (tok.type) {
case YEAR: {
if (!ParseAndValidate(tok_val, tok_len, 0, 9999, &dt_result->year)) return false;
// Year in "Y" and "YY" format should be in the interval
// [current time - 80 years, current time + 20 years)
if (tok_len <= 2) dt_result->realign_year = true;
break;
}
case MONTH_IN_YEAR: {
if (!ParseAndValidate(tok_val, tok_len, 1, 12, &dt_result->month)) return false;
break;
}
case MONTH_NAME_SHORT: {
const char* tok_end = tok_val + tok_len;
if (!ParseMonthNameToken(tok, tok_val, &tok_end, dt_ctx.fx_modifier,
&dt_result->month)) {
return false;
}
break;
}
case DAY_IN_MONTH: {
if (!ParseAndValidate(tok_val, tok_len, 1, 31, &dt_result->day)) return false;
break;
}
case HOUR_IN_DAY: {
if (!ParseAndValidate(tok_val, tok_len, 0, 23, &dt_result->hour)) return false;
break;
}
case MINUTE_IN_HOUR: {
if (!ParseAndValidate(tok_val, tok_len, 0, 59, &dt_result->minute)) return false;
break;
}
case SECOND_IN_MINUTE: {
if (!ParseAndValidate(tok_val, tok_len, 0, 59, &dt_result->second)) return false;
break;
}
case FRACTION: {
if (!ParseFractionToken(tok_val, tok_len, dt_result)) return false;
break;
}
case TZ_OFFSET: {
if (tok_val[0] != '+' && tok_val[0] != '-') return false;
int sign = tok_val[0] == '-' ? -1 : 1;
int minute = 0;
int hour = StringParser::StringToInt<int>(tok_val + 1, 2, &status);
if (UNLIKELY(StringParser::PARSE_SUCCESS != status ||
hour < 0 || hour > 23)) {
return false;
}
switch (tok_len) {
case 6: {
// +hh:mm
minute = StringParser::StringToInt<int>(tok_val + 4, 2, &status);
break;
}
case 5: {
// +hh:mm
minute = StringParser::StringToInt<int>(tok_val + 3, 2, &status);
break;
}
case 3: {
// +hh
break;
}
default: {
// Invalid timezone offset length.
return false;
}
}
if (UNLIKELY(StringParser::PARSE_SUCCESS != status ||
minute < 0 || minute > 59)) {
return false;
}
dt_result->tz_offset = time_duration(sign * hour, sign * minute, 0, 0);
break;
}
default: DCHECK(false) << "Unknown date/time format token";
}
}
return true;
}