absl::Status DiffDates()

in sql_utils/public/functions/date_time_util.cc [3104:3166]


absl::Status DiffDates(int32_t date1, int32_t date2, DateTimestampPart part,
                       int32_t* output) {
  if (!IsValidDate(date1)) {
    return MakeEvalError() << "Invalid date value: " << date1;
  }
  if (!IsValidDate(date2)) {
    return MakeEvalError() << "Invalid date value: " << date2;
  }

  switch (part) {
    case DAY:
      *output = date1 - date2;
      break;
    case WEEK:
    case WEEK_MONDAY:
    case WEEK_TUESDAY:
    case WEEK_WEDNESDAY:
    case WEEK_THURSDAY:
    case WEEK_FRIDAY:
    case WEEK_SATURDAY:
    case ISOWEEK:
      SQL_RETURN_IF_ERROR(
          TruncateDateImpl(date1, part, /*enforce_range=*/false, &date1));
      SQL_RETURN_IF_ERROR(
          TruncateDateImpl(date2, part, /*enforce_range=*/false, &date2));
      *output = (date1 - date2) / 7;
      break;
    case YEAR:
    case ISOYEAR:
    case QUARTER:
    case MONTH: {
      absl::CivilDay civil_day1 = EpochDaysToCivilDay(date1);
      absl::civil_year_t y1 = civil_day1.year();
      int32_t m1 = civil_day1.month();
      absl::CivilDay civil_day2 = EpochDaysToCivilDay(date2);
      absl::civil_year_t y2 = civil_day2.year();
      int32_t m2 = civil_day2.month();
      switch (part) {
        case YEAR:
          *output = y1 - y2;
          break;
        case ISOYEAR:
          // cast is safe because dates are severely restricted by IsValidDate.
          *output = static_cast<int32_t>(GetIsoYear(civil_day1) -
                                         GetIsoYear(civil_day2));
          break;
        case MONTH:
          *output = (y1 - y2) * 12 + (m1 - m2);
          break;
        case QUARTER:
          *output = (y1 * 12 + m1 - 1) / 3 - (y2 * 12 + m2 - 1) / 3;
          break;
        default:
          break;
      }
      break;
    }
    default:
      return MakeEvalError() << "Unsupported DateTimestampPart "
                             << DateTimestampPart_Name(part);
  }
  return absl::OkStatus();
}