use std::marker::PhantomData;
use arrow::array::{Array, ArrayRef};
use arrow::datatypes::DataType;
use crate::datatype::{
ColumnError, Datatype, InfallibleBuild, PrimitiveDatatype, RefDatatype, downcast_array,
};
pub struct Timestamp<U, Z = NoTimezone> {
_marker: PhantomData<fn() -> (U, Z)>,
}
pub trait TimeUnitSpec {
type TimestampType: arrow::datatypes::ArrowTimestampType;
type DurationType: arrow::datatypes::ArrowPrimitiveType<Native = i64>;
}
pub struct Second;
pub struct Millisecond;
pub struct Microsecond;
pub struct Nanosecond;
impl TimeUnitSpec for Second {
type TimestampType = arrow::datatypes::TimestampSecondType;
type DurationType = arrow::datatypes::DurationSecondType;
}
impl TimeUnitSpec for Millisecond {
type TimestampType = arrow::datatypes::TimestampMillisecondType;
type DurationType = arrow::datatypes::DurationMillisecondType;
}
impl TimeUnitSpec for Microsecond {
type TimestampType = arrow::datatypes::TimestampMicrosecondType;
type DurationType = arrow::datatypes::DurationMicrosecondType;
}
impl TimeUnitSpec for Nanosecond {
type TimestampType = arrow::datatypes::TimestampNanosecondType;
type DurationType = arrow::datatypes::DurationNanosecondType;
}
pub trait TimezoneSpec {
fn timezone() -> Option<std::sync::Arc<str>>;
}
pub struct NoTimezone;
impl TimezoneSpec for NoTimezone {
fn timezone() -> Option<std::sync::Arc<str>> {
None
}
}
pub struct Utc;
impl TimezoneSpec for Utc {
fn timezone() -> Option<std::sync::Arc<str>> {
Some("UTC".into())
}
}
impl<U: TimeUnitSpec + 'static, Z: TimezoneSpec + 'static> Datatype for Timestamp<U, Z> {
type Typed = arrow::array::PrimitiveArray<U::TimestampType>;
type Value<'a>
= i64
where
Self: 'a;
type Owned = i64;
fn datatype() -> DataType {
DataType::Timestamp(
<U::TimestampType as arrow::datatypes::ArrowTimestampType>::UNIT,
Z::timezone(),
)
}
fn downcast(array: &dyn Array) -> Result<Self::Typed, ColumnError> {
downcast_array::<Self::Typed>(array)
}
fn is_null(typed: &Self::Typed, index: usize) -> bool {
typed.is_null(index)
}
fn value(typed: &Self::Typed, index: usize) -> Self::Value<'_> {
typed.value(index)
}
fn build(values: impl Iterator<Item = Option<Self::Owned>>) -> Result<ArrayRef, ColumnError> {
let array: arrow::array::PrimitiveArray<U::TimestampType> = values.collect();
Ok(std::sync::Arc::new(array.with_timezone_opt(Z::timezone())))
}
fn to_owned_value(value: Self::Value<'_>) -> Self::Owned {
value
}
}
pub type TimestampSecond<Z = NoTimezone> = Timestamp<Second, Z>;
pub type TimestampMillisecond<Z = NoTimezone> = Timestamp<Millisecond, Z>;
pub type TimestampMicrosecond<Z = NoTimezone> = Timestamp<Microsecond, Z>;
pub type TimestampNanosecond<Z = NoTimezone> = Timestamp<Nanosecond, Z>;
impl<U: TimeUnitSpec + 'static, Z: TimezoneSpec + 'static> InfallibleBuild for Timestamp<U, Z> {}
impl<U: TimeUnitSpec + 'static, Z: TimezoneSpec + 'static> PrimitiveDatatype for Timestamp<U, Z> {
type Native = i64;
fn values(typed: &Self::Typed) -> &[i64] {
typed.values()
}
}
impl<U: TimeUnitSpec + 'static, Z: TimezoneSpec + 'static> RefDatatype for Timestamp<U, Z> {
type Ref = i64;
fn value_ref(typed: &Self::Typed, index: usize) -> &i64 {
&typed.values()[index]
}
}