use std::{ptr, sync::Arc};
use crate::{
core::{
column::{Column, ColumnView},
error::{self, Error, ErrorCode, FACT_TYPE_ID, Result},
types::TypeId,
},
runtime::execution_domain::ExecutionDomain,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum DatetimeComponent {
Year = 0,
Month = 1,
Day = 2,
Hour = 3,
Minute = 4,
Second = 5,
Millisecond = 6,
Microsecond = 7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum DatetimeRoundingFrequency {
Day = 0,
Hour = 1,
Minute = 2,
Second = 3,
Millisecond = 4,
Microsecond = 5,
Nanosecond = 6,
}
impl DatetimeRoundingFrequency {
pub(crate) const fn name(self) -> &'static str {
match self {
DatetimeRoundingFrequency::Day => "day",
DatetimeRoundingFrequency::Hour => "hour",
DatetimeRoundingFrequency::Minute => "minute",
DatetimeRoundingFrequency::Second => "second",
DatetimeRoundingFrequency::Millisecond => "millisecond",
DatetimeRoundingFrequency::Microsecond => "microsecond",
DatetimeRoundingFrequency::Nanosecond => "nanosecond",
}
}
pub(crate) fn to_raw(self) -> cudf_sys::cudf_datetime_rounding_frequency_t {
match self {
DatetimeRoundingFrequency::Day => cudf_sys::CUDF_DATETIME_ROUNDING_DAY,
DatetimeRoundingFrequency::Hour => cudf_sys::CUDF_DATETIME_ROUNDING_HOUR,
DatetimeRoundingFrequency::Minute => cudf_sys::CUDF_DATETIME_ROUNDING_MINUTE,
DatetimeRoundingFrequency::Second => cudf_sys::CUDF_DATETIME_ROUNDING_SECOND,
DatetimeRoundingFrequency::Millisecond => cudf_sys::CUDF_DATETIME_ROUNDING_MILLISECOND,
DatetimeRoundingFrequency::Microsecond => cudf_sys::CUDF_DATETIME_ROUNDING_MICROSECOND,
DatetimeRoundingFrequency::Nanosecond => cudf_sys::CUDF_DATETIME_ROUNDING_NANOSECOND,
}
}
}
impl DatetimeComponent {
pub(crate) const fn name(self) -> &'static str {
match self {
DatetimeComponent::Year => "year",
DatetimeComponent::Month => "month",
DatetimeComponent::Day => "day",
DatetimeComponent::Hour => "hour",
DatetimeComponent::Minute => "minute",
DatetimeComponent::Second => "second",
DatetimeComponent::Millisecond => "millisecond",
DatetimeComponent::Microsecond => "microsecond",
}
}
pub(crate) fn to_raw(self) -> cudf_sys::cudf_datetime_component_t {
match self {
DatetimeComponent::Year => cudf_sys::CUDF_DATETIME_COMPONENT_YEAR,
DatetimeComponent::Month => cudf_sys::CUDF_DATETIME_COMPONENT_MONTH,
DatetimeComponent::Day => cudf_sys::CUDF_DATETIME_COMPONENT_DAY,
DatetimeComponent::Hour => cudf_sys::CUDF_DATETIME_COMPONENT_HOUR,
DatetimeComponent::Minute => cudf_sys::CUDF_DATETIME_COMPONENT_MINUTE,
DatetimeComponent::Second => cudf_sys::CUDF_DATETIME_COMPONENT_SECOND,
DatetimeComponent::Millisecond => cudf_sys::CUDF_DATETIME_COMPONENT_MILLISECOND,
DatetimeComponent::Microsecond => cudf_sys::CUDF_DATETIME_COMPONENT_MICROSECOND,
}
}
}
fn require_timestamp_input(
input: &ColumnView<'_>,
component: DatetimeComponent,
operation: &'static str,
) -> Result<TypeId> {
let type_id = input.data_type().map_err(|e| e.reanchor(operation))?.id();
if type_id.is_timestamp() {
Ok(type_id)
} else {
Err(Error::new_with(
ErrorCode::TypeMismatch,
operation,
"datetime component extraction requires timestamp input",
)
.with_fact(FACT_TYPE_ID, type_id)
.with_detail("expected_family", "timestamp")
.with_detail("component", component.name()))
}
}
fn require_timestamp_offset_input(input: &ColumnView<'_>, operation: &'static str) -> Result<TypeId> {
let type_id = input.data_type().map_err(|e| e.reanchor(operation))?.id();
if type_id.is_timestamp() {
Ok(type_id)
} else {
Err(Error::new_with(ErrorCode::TypeMismatch, operation, "datetime offset requires timestamp input")
.with_fact(FACT_TYPE_ID, type_id)
.with_detail("expected_family", "timestamp"))
}
}
pub(crate) fn floor_datetimes_impl(
input: &ColumnView<'_>,
frequency: DatetimeRoundingFrequency,
output_domain: &Arc<ExecutionDomain>,
exec_options: &cudf_sys::cudf_exec_options_t,
operation: &'static str,
) -> Result<Column> {
let input_type_id = require_timestamp_offset_input(input, operation)?;
let mut out = ptr::null_mut();
let frequency_ffi = frequency.to_raw();
let status = unsafe { cudf_sys::cudf_datetime_floor_ex(input.raw_handle(), frequency_ffi, &mut out, exec_options) };
error::check_status_in_with_details(
status,
operation,
[
("frequency", frequency.name().to_string()),
("input_type_id", input_type_id.to_string()),
("execution_authority", "retained_domain".to_string()),
],
)?;
unsafe { Column::from_raw_with_domain(out, Arc::clone(output_domain)) }
}
pub(crate) fn extract_component_impl(
input: &ColumnView<'_>,
component: DatetimeComponent,
output_domain: &Arc<ExecutionDomain>,
exec_options: &cudf_sys::cudf_exec_options_t,
operation: &'static str,
) -> Result<Column> {
let input_type_id = require_timestamp_input(input, component, operation)?;
let mut out = ptr::null_mut();
let component_ffi = component.to_raw();
let status = unsafe {
cudf_sys::cudf_datetime_extract_component_ex(input.raw_handle(), component_ffi, &mut out, exec_options)
};
error::check_status_in_with_details(
status,
operation,
[
("component", component.name().to_string()),
("input_type_id", input_type_id.to_string()),
("output_type_id", TypeId::Int16.to_string()),
("execution_authority", "retained_domain".to_string()),
],
)?;
unsafe { Column::from_raw_with_domain(out, Arc::clone(output_domain)) }
}
pub(crate) fn add_calendrical_months_impl(
input: &ColumnView<'_>,
months: i32,
output_domain: &Arc<ExecutionDomain>,
exec_options: &cudf_sys::cudf_exec_options_t,
operation: &'static str,
) -> Result<Column> {
let input_type_id = require_timestamp_offset_input(input, operation)?;
let mut out = ptr::null_mut();
let status = unsafe {
cudf_sys::cudf_datetime_add_calendrical_months_scalar_ex(input.raw_handle(), months, &mut out, exec_options)
};
error::check_status_in_with_details(
status,
operation,
[
("months", months.to_string()),
("input_type_id", input_type_id.to_string()),
("execution_authority", "retained_domain".to_string()),
],
)?;
unsafe { Column::from_raw_with_domain(out, Arc::clone(output_domain)) }
}