absl::Status DiffDatetimes()

in sql_utils/public/functions/date_time_util.cc [3237:3325]


absl::Status DiffDatetimes(const DatetimeValue& datetime1,
                           const DatetimeValue& datetime2,
                           DateTimestampPart part, int64_t* output) {
  if (!datetime1.IsValid()) {
    return MakeEvalError() << "Invalid datetime value: "
                           << datetime1.DebugString();
  }
  if (!datetime2.IsValid()) {
    return MakeEvalError() << "Invalid datetime value: "
                           << datetime2.DebugString();
  }

  switch (part) {
    case YEAR:
      *output = absl::CivilYear(datetime1.ConvertToCivilSecond()) -
                absl::CivilYear(datetime2.ConvertToCivilSecond());
      break;
    case QUARTER: {
      // This logic is the same as the one in DiffDates()
      auto civil_time_1 = datetime1.ConvertToCivilSecond();
      auto civil_time_2 = datetime2.ConvertToCivilSecond();
      *output = (civil_time_1.year() * 12 + civil_time_1.month() - 1) / 3 -
                (civil_time_2.year() * 12 + civil_time_2.month() - 1) / 3;
      break;
    }
    case MONTH:
      *output = absl::CivilMonth(datetime1.ConvertToCivilSecond()) -
                absl::CivilMonth(datetime2.ConvertToCivilSecond());
      break;
    case WEEK:
    case WEEK_MONDAY:
    case WEEK_TUESDAY:
    case WEEK_WEDNESDAY:
    case WEEK_THURSDAY:
    case WEEK_FRIDAY:
    case WEEK_SATURDAY:
    case ISOYEAR:
    case ISOWEEK: {
      int32_t date1;
      int32_t date2;
      int32_t int32_diff;
      SQL_RETURN_IF_ERROR(ExtractFromDatetime(DATE, datetime1, &date1));
      SQL_RETURN_IF_ERROR(ExtractFromDatetime(DATE, datetime2, &date2));
      SQL_RETURN_IF_ERROR(DiffDates(date1, date2, part, &int32_diff));
      *output = int32_diff;
      break;
    }
    case DAY:
      *output = absl::CivilDay(datetime1.ConvertToCivilSecond()) -
                absl::CivilDay(datetime2.ConvertToCivilSecond());
      break;
    case HOUR:
    case MINUTE:
    case SECOND:
    case MILLISECOND:
    case MICROSECOND:
    case NANOSECOND: {
      auto DatetimeDiffOverflowErrorMaker = [&datetime1, &datetime2,
                                             part]() -> absl::Status {
        const std::string error_shared_prefix = absl::StrCat(
            "DATETIME_DIFF at ", DateTimestampPart_Name(part),
            " precision between datetime ", datetime1.DebugString(), " and ",
            datetime2.DebugString());
        if (part == NANOSECOND) {
          return MakeEvalError() << error_shared_prefix << " causes overflow";
        }
        SQL_RET_CHECK_FAIL() << error_shared_prefix
                         << " should never have overflow error";
      };
      return DiffWithPartsSmallerThanDay(
          datetime1.ConvertToCivilSecond(), datetime1.Nanoseconds(),
          datetime2.ConvertToCivilSecond(), datetime2.Nanoseconds(), part,
          DatetimeDiffOverflowErrorMaker, output);
    }
    case DAYOFWEEK:
    case DAYOFYEAR:
    case DATE:
    case DATETIME:
    case TIME:
      return MakeEvalError()
             << "Unsupported DateTimestampPart " << DateTimestampPart_Name(part)
             << " for DATETIME_DIFF";
    default:
      return MakeEvalError()
             << "Unexpected DateTimestampPart " << DateTimestampPart_Name(part)
             << " for DATETIME_DIFF";
  }
  return absl::OkStatus();
}