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