absl::StatusOr TimestampBucketizer::Create()

in sql_utils/public/functions/date_time_util.cc [4187:4244]


absl::StatusOr<TimestampBucketizer> TimestampBucketizer::Create(
    bigquery_ml_utils::IntervalValue bucket_width, absl::Time origin,
    absl::TimeZone timezone, TimestampScale scale) {
  SQL_RET_CHECK(scale == kMicroseconds || scale == kNanoseconds)
      << "Only kMicroseconds and kNanoseconds are acceptable values for scale";
  if (scale == kMicroseconds && bucket_width.get_nano_fractions() != 0) {
    return MakeEvalError() << "TIMESTAMP_BUCKET doesn't support bucket width "
                              "INTERVAL with nanoseconds precision";
  }
  if (bucket_width.get_months() != 0) {
    return MakeEvalError() << "TIMESTAMP_BUCKET doesn't support bucket width "
                              "INTERVAL with non-zero MONTH part";
  }
  // Nano fractions can't be negative, so only checking days and micros here.
  if (bucket_width.get_days() < 0 || bucket_width.get_micros() < 0) {
    return MakeEvalError() << "TIMESTAMP_BUCKET doesn't support negative "
                              "bucket width INTERVAL";
  }
  if (bucket_width.get_days() != 0) {
    if (scale == kMicroseconds) {
      if (bucket_width.get_micros() != 0) {
        return MakeEvalError()
               << "TIMESTAMP_BUCKET doesn't support bucket width "
                  "INTERVAL with mixed DAY and MICROSECOND parts";
      }
    } else {
      if (bucket_width.get_micros() != 0 ||
          bucket_width.get_nano_fractions() != 0) {
        return MakeEvalError()
               << "TIMESTAMP_BUCKET doesn't support bucket width "
                  "INTERVAL with mixed DAY and NANOSECOND parts";
      }
    }
  }
  if (bucket_width.get_days() == 0 && bucket_width.get_micros() == 0 &&
      bucket_width.get_nano_fractions() == 0) {
    return MakeEvalError() << "TIMESTAMP_BUCKET doesn't support zero "
                              "bucket width INTERVAL";
  }

  absl::Duration bucket_size;
  if (bucket_width.get_days() > 0) {
    // Only 23 bits are used by days in IntervalValue type, so it's safe to
    // multiple by 24 without the risk of overflowing int64_t.
    bucket_size =
        absl::Hours(bucket_width.get_days() * static_cast<int64_t>(24));
  } else {
    bucket_size = absl::Microseconds(bucket_width.get_micros());
    if (bucket_width.get_nano_fractions() > 0) {
      // Note that we can't construct Duration directly from nanoseconds, since
      // it would overflow int64_t.
      bucket_size += absl::Nanoseconds(bucket_width.get_nano_fractions());
    }
  }

  return TimestampBucketizer(std::move(bucket_size), std::move(origin),
                             std::move(timezone));
}