blob: 05aafb961ba882222d686b332665fe9810d45c81 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "iceberg/util/temporal_util.h"
#include <chrono>
#include <cstdint>
#include <utility>
#include "iceberg/expression/literal.h"
namespace iceberg {
namespace {
using namespace std::chrono; // NOLINT
constexpr auto kEpochYmd = year{1970} / January / 1;
constexpr auto kEpochDays = sys_days(kEpochYmd);
inline constexpr year_month_day DateToYmd(int32_t days_since_epoch) {
return {kEpochDays + days{days_since_epoch}};
}
inline constexpr year_month_day TimestampToYmd(int64_t micros_since_epoch) {
return {floor<days>(sys_time<microseconds>(microseconds{micros_since_epoch}))};
}
template <typename Duration>
requires std::is_same_v<Duration, days> || std::is_same_v<Duration, hours>
inline constexpr int32_t TimestampToDuration(int64_t micros_since_epoch) {
return static_cast<int32_t>(
floor<Duration>(
sys_time<microseconds>(microseconds{micros_since_epoch}).time_since_epoch())
.count());
}
inline constexpr int32_t MonthsSinceEpoch(const year_month_day& ymd) {
auto delta = ymd.year() - kEpochYmd.year();
// Calculate the month as months from 1970-01
// Note: January is month 1, so we subtract 1 to get zero-based month count.
return static_cast<int32_t>(delta.count() * 12 + static_cast<unsigned>(ymd.month()) -
1);
}
template <TypeId type_id>
Result<Literal> ExtractYearImpl(const Literal& literal) {
std::unreachable();
}
template <>
Result<Literal> ExtractYearImpl<TypeId::kDate>(const Literal& literal) {
auto value = std::get<int32_t>(literal.value());
auto ymd = DateToYmd(value);
return Literal::Int((ymd.year() - kEpochYmd.year()).count());
}
template <>
Result<Literal> ExtractYearImpl<TypeId::kTimestamp>(const Literal& literal) {
auto value = std::get<int64_t>(literal.value());
auto ymd = TimestampToYmd(value);
return Literal::Int((ymd.year() - kEpochYmd.year()).count());
}
template <>
Result<Literal> ExtractYearImpl<TypeId::kTimestampTz>(const Literal& literal) {
return ExtractYearImpl<TypeId::kTimestamp>(literal);
}
template <TypeId type_id>
Result<Literal> ExtractMonthImpl(const Literal& literal) {
std::unreachable();
}
template <>
Result<Literal> ExtractMonthImpl<TypeId::kDate>(const Literal& literal) {
auto value = std::get<int32_t>(literal.value());
auto ymd = DateToYmd(value);
return Literal::Int(MonthsSinceEpoch(ymd));
}
template <>
Result<Literal> ExtractMonthImpl<TypeId::kTimestamp>(const Literal& literal) {
auto value = std::get<int64_t>(literal.value());
auto ymd = TimestampToYmd(value);
return Literal::Int(MonthsSinceEpoch(ymd));
}
template <>
Result<Literal> ExtractMonthImpl<TypeId::kTimestampTz>(const Literal& literal) {
return ExtractMonthImpl<TypeId::kTimestamp>(literal);
}
template <TypeId type_id>
Result<Literal> ExtractDayImpl(const Literal& literal) {
std::unreachable();
}
template <>
Result<Literal> ExtractDayImpl<TypeId::kDate>(const Literal& literal) {
return Literal::Int(std::get<int32_t>(literal.value()));
}
template <>
Result<Literal> ExtractDayImpl<TypeId::kTimestamp>(const Literal& literal) {
auto value = std::get<int64_t>(literal.value());
return Literal::Int(TimestampToDuration<days>(value));
}
template <>
Result<Literal> ExtractDayImpl<TypeId::kTimestampTz>(const Literal& literal) {
return ExtractDayImpl<TypeId::kTimestamp>(literal);
}
template <TypeId type_id>
Result<Literal> ExtractHourImpl(const Literal& literal) {
std::unreachable();
}
template <>
Result<Literal> ExtractHourImpl<TypeId::kTimestamp>(const Literal& literal) {
auto value = std::get<int64_t>(literal.value());
return Literal::Int(TimestampToDuration<hours>(value));
}
template <>
Result<Literal> ExtractHourImpl<TypeId::kTimestampTz>(const Literal& literal) {
return ExtractHourImpl<TypeId::kTimestamp>(literal);
}
} // namespace
#define DISPATCH_EXTRACT_YEAR(type_id) \
case type_id: \
return ExtractYearImpl<type_id>(literal);
Result<Literal> TemporalUtils::ExtractYear(const Literal& literal) {
if (literal.IsNull()) [[unlikely]] {
return Literal::Null(int32());
}
if (literal.IsAboveMax() || literal.IsBelowMin()) [[unlikely]] {
return NotSupported("Cannot extract year from {}", literal.ToString());
}
switch (literal.type()->type_id()) {
DISPATCH_EXTRACT_YEAR(TypeId::kDate)
DISPATCH_EXTRACT_YEAR(TypeId::kTimestamp)
DISPATCH_EXTRACT_YEAR(TypeId::kTimestampTz)
default:
return NotSupported("Extract year from type {} is not supported",
literal.type()->ToString());
}
}
#define DISPATCH_EXTRACT_MONTH(type_id) \
case type_id: \
return ExtractMonthImpl<type_id>(literal);
Result<Literal> TemporalUtils::ExtractMonth(const Literal& literal) {
if (literal.IsNull()) [[unlikely]] {
return Literal::Null(int32());
}
if (literal.IsAboveMax() || literal.IsBelowMin()) [[unlikely]] {
return NotSupported("Cannot extract month from {}", literal.ToString());
}
switch (literal.type()->type_id()) {
DISPATCH_EXTRACT_MONTH(TypeId::kDate)
DISPATCH_EXTRACT_MONTH(TypeId::kTimestamp)
DISPATCH_EXTRACT_MONTH(TypeId::kTimestampTz)
default:
return NotSupported("Extract month from type {} is not supported",
literal.type()->ToString());
}
}
#define DISPATCH_EXTRACT_DAY(type_id) \
case type_id: \
return ExtractDayImpl<type_id>(literal);
Result<Literal> TemporalUtils::ExtractDay(const Literal& literal) {
if (literal.IsNull()) [[unlikely]] {
return Literal::Null(int32());
}
if (literal.IsAboveMax() || literal.IsBelowMin()) [[unlikely]] {
return NotSupported("Cannot extract day from {}", literal.ToString());
}
switch (literal.type()->type_id()) {
DISPATCH_EXTRACT_DAY(TypeId::kDate)
DISPATCH_EXTRACT_DAY(TypeId::kTimestamp)
DISPATCH_EXTRACT_DAY(TypeId::kTimestampTz)
default:
return NotSupported("Extract day from type {} is not supported",
literal.type()->ToString());
}
}
#define DISPATCH_EXTRACT_HOUR(type_id) \
case type_id: \
return ExtractHourImpl<type_id>(literal);
Result<Literal> TemporalUtils::ExtractHour(const Literal& literal) {
if (literal.IsNull()) [[unlikely]] {
return Literal::Null(int32());
}
if (literal.IsAboveMax() || literal.IsBelowMin()) [[unlikely]] {
return NotSupported("Cannot extract hour from {}", literal.ToString());
}
switch (literal.type()->type_id()) {
DISPATCH_EXTRACT_HOUR(TypeId::kTimestamp)
DISPATCH_EXTRACT_HOUR(TypeId::kTimestampTz)
default:
return NotSupported("Extract hour from type {} is not supported",
literal.type()->ToString());
}
}
} // namespace iceberg