absl::Status ValidateDateTimeFormatElements()

in sql_utils/public/functions/cast_date_time.cc [1231:1314]


absl::Status ValidateDateTimeFormatElements(
    const std::vector<DateTimeFormatElement>& format_elements,
    const std::vector<FormatElementCategory>& invalid_categories,
    absl::string_view output_type_name) {
  CategoryToElementsMap category_to_elements_map;
  TypeToElementMap type_to_element_map;

  for (const DateTimeFormatElement& format_element : format_elements) {
    if (!IsSupportedForParsing(format_element)) {
      return MakeEvalError() << "Format element " << format_element.ToString()
                             << " is not supported for parsing";
    }

    // We store at most 2 elements inside this map, since this is enough to
    // print in error message when duplicate checks fail for a category.
    if (category_to_elements_map[format_element.category].size() < 2) {
      category_to_elements_map[format_element.category].push_back(
          &format_element);
    }

    if (type_to_element_map.contains(format_element.type)) {
      // We do not allow that more than one non-literal format element of the
      // same type exist at the same time. For example, the format string
      // "MiYYmI" is invalid since two format elements of "kMI" type
      // (appearing as "Mi" and "MI") exist in it.
      if (format_element.category != FormatElementCategory::kLiteral) {
        return MakeEvalError() << absl::Substitute(
                   "Format element $0 appears more than once in the "
                   "format string",
                   format_element.ToString());
      }
    } else {
      type_to_element_map[format_element.type] = &format_element;
    }
  }

  // Checks invalid format element categories for the output type.
  for (const FormatElementCategory& invalid_category : invalid_categories) {
    SQL_RETURN_IF_ERROR(CheckCategoryNotExist(
        invalid_category, category_to_elements_map, output_type_name));
  }

  // Checks categories which do not allow duplications.
  const std::vector<FormatElementCategory> categories_to_check_duplicate = {
      FormatElementCategory::kMeridianIndicator,
      FormatElementCategory::kYear,
      FormatElementCategory::kMonth,
      FormatElementCategory::kDay,
      FormatElementCategory::kHour,
      FormatElementCategory::kMinute};

  for (FormatElementCategory category : categories_to_check_duplicate) {
    SQL_RETURN_IF_ERROR(CheckForDuplicateElementsInCategory(
        category, category_to_elements_map));
  }

  // Checks mutually exclusive format elements/types.
  // The Check between "kHH24" type and "kHH"/"kHH12" types is included in
  // duplicate check for "kHour" category.
  SQL_RETURN_IF_ERROR(CheckForMutuallyExclusiveElements(
      FormatElementType::kHH24, FormatElementCategory::kMeridianIndicator,
      type_to_element_map, category_to_elements_map));
  // A Format element in "kMeridianIndicator" category must exist when a format
  // element of "kHH" or "kHH12" is present. Also, if we have a format element
  // in "kMeridianIndicator" category, a format element of "kHH" or "kHH12" type
  // must exist.
  SQL_RETURN_IF_ERROR(
      CheckForCoexistance({FormatElementType::kHH, FormatElementType::kHH12},
                          FormatElementCategory::kMeridianIndicator,
                          type_to_element_map, category_to_elements_map));

  // Format elements of "kSSSSS" type contain Hour, Minute and Second info,
  // therefore elements in "kHour" (along with "kMeridianIndicator") and
  // "kMinute" categories and elements of "kSS" type are disallowed.
  SQL_RETURN_IF_ERROR(CheckForMutuallyExclusiveElements(
      FormatElementType::kSSSSS, FormatElementCategory::kHour,
      type_to_element_map, category_to_elements_map));
  SQL_RETURN_IF_ERROR(CheckForMutuallyExclusiveElements(
      FormatElementType::kSSSSS, FormatElementCategory::kMinute,
      type_to_element_map, category_to_elements_map));
  SQL_RETURN_IF_ERROR(CheckForMutuallyExclusiveElements(
      FormatElementType::kSSSSS, FormatElementType::kSS, type_to_element_map));
  return absl::OkStatus();
}