static absl::Status AddTimestampInternal()

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();
}