in sql_utils/public/interval_value.cc [819:965]
absl::StatusOr<IntervalValue> Parse(absl::string_view input) {
input_ = input;
char c = GetChar();
if (c != 'P') {
return MakeIntervalParsingError(input)
<< ": Interval must start with 'P'";
}
if (input_.empty()) {
return MakeIntervalParsingError(input)
<< ": At least one datetime part must be defined in the interval";
}
absl::Status status;
// When true - parsing time part (after T), when false - parsing date part.
bool in_time_part = false;
int64_t years = 0;
int64_t months = 0;
int64_t weeks = 0;
int64_t days = 0;
int64_t hours = 0;
int64_t minutes = 0;
int64_t seconds = 0;
int64_t nano_fractions = 0;
for (;;) {
int64_t sign = false;
c = PeekChar();
if (c == kEof) {
break;
}
if (!std::isdigit(c)) {
GetChar();
if (c == '-') {
// Proceed to parse the number and make it negative later
sign = true;
} else if (c == 'T') {
// Switching from date to time part
if (in_time_part) {
return MakeIntervalParsingError(input)
<< ": Unexpected duplicate time separator 'T'";
}
in_time_part = true;
continue;
} else {
return MakeIntervalParsingError(input)
<< ": Unexpected " << PrintChar(c);
}
}
// We now expect to see positive number (possibly with fractional digits)
// followed by datetime part letter.
SQL_RETURN_IF_ERROR(ParseNumber());
int64_t number;
if (!absl::SimpleAtoi(digits_, &number)) {
return MakeIntervalParsingError(input)
<< ": Cannot convert '" << digits_ << "' to integer";
}
// number couldn't have been negative, so no worries about underflow
// of int64_t::min
if (sign) number = -number;
c = GetChar();
if (!in_time_part) {
switch (c) {
case 'Y':
if (!functions::Add(years, number, &years, &status)) {
return status;
}
break;
case 'M':
if (!functions::Add(months, number, &months, &status)) {
return status;
}
break;
case 'W':
if (!functions::Add(weeks, number, &weeks, &status)) {
return status;
}
break;
case 'D':
if (!functions::Add(days, number, &days, &status)) {
return status;
}
break;
default:
return MakeIntervalParsingError(input)
<< ": Unexpected " << PrintChar(c)
<< " in the date portion of interval";
}
} else {
switch (c) {
case 'H':
if (!functions::Add(hours, number, &hours, &status)) {
return status;
}
break;
case 'M':
if (!functions::Add(minutes, number, &minutes, &status)) {
return status;
}
break;
case 'S':
if (!functions::Add(seconds, number, &seconds, &status)) {
return status;
}
if (!decimal_point_.empty()) {
SQL_ASSIGN_OR_RETURN(
number, NanosFromFractionDigits(input_, decimal_digits_));
if (sign) number = -number;
nano_fractions += number;
}
break;
default:
return MakeIntervalParsingError(input)
<< ": Unexpected " << PrintChar(c)
<< " in the time portion of interval";
}
}
if (!decimal_point_.empty() && c != 'S') {
return MakeIntervalParsingError(input)
<< ": Fractional values are only allowed for "
"seconds part 'S', but were used for "
<< PrintChar(c);
}
}
int64_t year_months;
if (!functions::Multiply(IntervalValue::kMonthsInYear, years, &year_months,
&status)) {
return status;
}
if (!functions::Add(months, year_months, &months, &status)) {
return status;
}
int64_t week_days;
if (!functions::Multiply(IntervalValue::kDaysInWeek, weeks, &week_days,
&status)) {
return status;
}
if (!functions::Add(days, week_days, &days, &status)) {
return status;
}
// Int128 math cannot overflow
__int128 nanos = IntervalValue::kNanosInHour * hours +
IntervalValue::kNanosInMinute * minutes +
IntervalValue::kNanosInSecond * seconds + nano_fractions;
return IntervalValue::FromMonthsDaysNanos(months, days, nanos);
}