in sql_utils/public/functions/date_time_util.cc [953:1038]
static absl::Status AddTimestampInternal(int64_t timestamp,
TimestampScale scale,
absl::TimeZone timezone,
DateTimestampPart part,
int64_t interval, int64_t* output) {
// Expected invariant.
SQL_DCHECK(IsValidTimestamp(timestamp, scale));
SQL_RETURN_IF_ERROR(CheckValidAddTimestampPart(part, false /* is_legacy */));
// For scale == kNanoseconds, call AddTimestampNanos().
if (scale == kNanoseconds) {
return AddTimestampNanos(timestamp, timezone, part, interval, output);
}
if (part == DAY) {
// For TIMESTAMP_ADD(), the DAY interval is equivalent to 24 HOURs.
part = HOUR;
int64_t new_interval;
if (!Multiply<int64_t>(interval, 24, &new_interval, kNoError)) {
return MakeEvalError()
<< "TIMESTAMP_ADD interval value " << interval << " at "
<< DateTimestampPart_Name(part) << " precision causes overflow";
}
interval = new_interval;
}
SQL_RET_CHECK(part == HOUR || part == MINUTE || part == SECOND ||
part == MILLISECOND || part == MICROSECOND || part == NANOSECOND);
// If the unit of interval (DateTimestampPart) is HOUR .. NANOSECOND, we can
// always convert the interval to the input timestamp scale.
int64_t new_interval = 0;
switch (part) {
case HOUR:
case MINUTE:
case SECOND: {
if (part == HOUR) {
if (!Multiply<int64_t>(interval, kNaiveNumSecondsPerHour, &new_interval,
kNoError)) {
return MakeEvalError() << "TIMESTAMP_ADD interval value " << interval
<< " at " << DateTimestampPart_Name(part)
<< " precision causes overflow";
}
}
if (part == MINUTE) {
if (!Multiply<int64_t>(interval, kNaiveNumSecondsPerMinute,
&new_interval, kNoError)) {
return MakeEvalError() << "TIMESTAMP_ADD interval value " << interval
<< " at " << DateTimestampPart_Name(part)
<< " precision causes overflow";
}
}
if (part == SECOND) {
new_interval = interval;
}
// convert interval from second scale to the input timestamp scale.
SQL_RETURN_IF_ERROR(ConvertTimestampInterval(new_interval, kSeconds, scale,
&new_interval));
break;
}
case MILLISECOND:
// convert interval from millisecond scale to the input timestamp scale.
SQL_RETURN_IF_ERROR(ConvertTimestampInterval(interval, kMilliseconds, scale,
&new_interval));
break;
case MICROSECOND:
// convert interval from microsecond scale to the input timestamp scale.
SQL_RETURN_IF_ERROR(ConvertTimestampInterval(interval, kMicroseconds, scale,
&new_interval));
break;
case NANOSECOND:
// convert interval from nanosecond scale to the input timestamp scale.
SQL_RETURN_IF_ERROR(ConvertTimestampInterval(interval, kNanoseconds, scale,
&new_interval));
break;
default:
break;
}
if (!Add<int64_t>(timestamp, new_interval, output, kNoError) ||
!IsValidTimestamp(*output, scale)) {
return MakeAddTimestampOverflowError(timestamp, part, interval, scale,
timezone);
}
return absl::OkStatus();
}