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