static absl::Status ComputeYearMonthDayFromNonISOParts()

in sql_utils/public/functions/parse_date_time.cc [860:933]


static absl::Status ComputeYearMonthDayFromNonISOParts(
    int64_t* year, int* month, int* mday, DateParseContext* date_parse_context) {
  // Compute the non-ISO year/month/day from the canonicalized DateParseContext.
  std::optional<ParseElementInfo> week_info;
  std::optional<ParseElementInfo> weekday_info;
  std::optional<ParseElementInfo> dayofyear_info;

  for (const auto& element : date_parse_context->elements) {
    switch (element.fmt) {
      case 'A':  // Full weekday name
      case 'a':  // Abbreviated weekday name
      case 'u':  // weekday number 1-7, starting Monday
      case 'w':  // weekday number 0-6, starting Sunday
        weekday_info = element;
        break;
      case 'j':  // Non-ISO day of year
        dayofyear_info = element;
        break;
      case 'U':  // Non-ISO week number of the year (starting Sunday) 00-53
      case 'W':  // Non-ISO week number of the year (starting Monday) 00-53
        week_info = element;
        break;
      default:
        SQL_RET_CHECK_FAIL() << "Unexpected format element: '" << element.fmt
                         << "'";
    }
  }

  absl::CivilDay civil_day(*year, *month, *mday);

  // Non-ISO case.  The <year> is used as input to compute the final date.
  //
  // Valid combinations are:
  // 1) week - interpreted as the first day of the week.
  // 2) week and day of week
  // 3) <nothing additional> - interpreted as the first day of the year
  // 4) day of year
  int new_element_position = -1;
  if (week_info.has_value()) {
    SQL_RET_CHECK(!dayofyear_info.has_value());
    SQL_RETURN_IF_ERROR(ComputeDateFromYearWeekAndWeekday(
        *year, week_info.value(), weekday_info, &civil_day));
    new_element_position = week_info.value().position;
  } else {
    SQL_RET_CHECK(!weekday_info.has_value());
    // Compute date from year and day of year (optional).
    SQL_RETURN_IF_ERROR(
        ComputeDateFromYearAndDayOfYear(*year, dayofyear_info, &civil_day));
    new_element_position = dayofyear_info.value().position;
  }

  // Only update the month or day if the new part is after all related
  // original parts.
  if (date_parse_context->last_year_element_position < new_element_position) {
    *year = civil_day.year();
  }
  if (date_parse_context->last_month_element_position < new_element_position) {
    *month = civil_day.month();
  }
  if (date_parse_context->last_mday_element_position < new_element_position) {
    *mday = civil_day.day();
  }
  // Verify that the result date is valid.  This is needed for a case like
  // PARSE_DATE("%Y %W %d", "1999-09-29"), where we have updated the month
  // from the week date part element (in this case February), but the
  // originally specified day (whose element %d appears after %W) is out of
  // range for that updated month.
  if (!IsValidDay(*year, *month, *mday)) {
    return MakeEvalError()
           << "Out-of-range datetime field in parsing function; year: " << *year
           << ", month: " << *month << ", day: " << *mday;
  }
  return absl::OkStatus();
}