use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, TimeDelta, Timelike, Utc};
use crate::{
NonNullable, Nullable,
array::{ArrayType, UnionType},
buffer::BufferType,
offset::Offset,
};
use super::{LogicalArray, LogicalArrayType};
impl ArrayType<DateTime<Utc>> for DateTime<Utc> {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<Self, NonNullable, Buffer, OffsetItem, UnionLayout>;
}
impl ArrayType<DateTime<Utc>> for Option<DateTime<Utc>> {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<DateTime<Utc>, Nullable, Buffer, OffsetItem, UnionLayout>;
}
impl LogicalArrayType<DateTime<Utc>> for DateTime<Utc> {
type ArrayType = i64;
fn from_array_type(item: Self::ArrayType) -> Self {
DateTime::from_timestamp_nanos(item)
}
fn into_array_type(self) -> Self::ArrayType {
self.timestamp_nanos_opt().expect("out of range")
}
}
#[cfg(feature = "arrow-rs")]
impl crate::arrow::LogicalArrayType<DateTime<Utc>> for DateTime<Utc> {
type ExtensionType = crate::arrow::NoExtensionType;
}
pub type DateTimeArray<Nullable = NonNullable, Buffer = crate::buffer::VecBuffer> =
LogicalArray<DateTime<Utc>, Nullable, Buffer>;
impl ArrayType<NaiveDateTime> for NaiveDateTime {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<Self, NonNullable, Buffer, OffsetItem, UnionLayout>;
}
impl ArrayType<NaiveDateTime> for Option<NaiveDateTime> {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<NaiveDateTime, Nullable, Buffer, OffsetItem, UnionLayout>;
}
impl LogicalArrayType<NaiveDateTime> for NaiveDateTime {
type ArrayType = i64;
fn from_array_type(item: Self::ArrayType) -> Self {
DateTime::from_timestamp_nanos(item).naive_utc()
}
fn into_array_type(self) -> Self::ArrayType {
self.and_utc().timestamp_nanos_opt().expect("out of range")
}
}
#[cfg(feature = "arrow-rs")]
impl crate::arrow::LogicalArrayType<NaiveDateTime> for NaiveDateTime {
type ExtensionType = crate::arrow::NoExtensionType;
}
pub type NaiveDateTimeArray<Nullable = NonNullable, Buffer = crate::buffer::VecBuffer> =
LogicalArray<NaiveDateTime, Nullable, Buffer>;
impl ArrayType<NaiveDate> for NaiveDate {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<Self, NonNullable, Buffer, OffsetItem, UnionLayout>;
}
impl ArrayType<NaiveDate> for Option<NaiveDate> {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<NaiveDate, Nullable, Buffer, OffsetItem, UnionLayout>;
}
impl LogicalArrayType<NaiveDate> for NaiveDate {
type ArrayType = i32;
fn from_array_type(item: Self::ArrayType) -> Self {
NaiveDate::from_num_days_from_ce_opt(item).expect("out of range")
}
fn into_array_type(self) -> Self::ArrayType {
self.num_days_from_ce()
}
}
#[cfg(feature = "arrow-rs")]
impl crate::arrow::LogicalArrayType<NaiveDate> for NaiveDate {
type ExtensionType = crate::arrow::NoExtensionType;
}
pub type NaiveDateArray<Nullable = NonNullable, Buffer = crate::buffer::VecBuffer> =
LogicalArray<NaiveDate, Nullable, Buffer>;
impl ArrayType<NaiveTime> for NaiveTime {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<Self, NonNullable, Buffer, OffsetItem, UnionLayout>;
}
impl ArrayType<NaiveTime> for Option<NaiveTime> {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<NaiveTime, Nullable, Buffer, OffsetItem, UnionLayout>;
}
const NANO_SECONDS: i64 = 1_000_000_000;
impl LogicalArrayType<NaiveTime> for NaiveTime {
type ArrayType = i64;
fn from_array_type(item: Self::ArrayType) -> Self {
let (secs, nano) = (item.div_euclid(NANO_SECONDS), item.rem_euclid(NANO_SECONDS));
Self::from_num_seconds_from_midnight_opt(
u32::try_from(secs).expect("out of range"),
u32::try_from(nano).expect("out of range"),
)
.expect("out of range")
}
fn into_array_type(self) -> Self::ArrayType {
i64::from(self.num_seconds_from_midnight()) * NANO_SECONDS + i64::from(self.nanosecond())
}
}
#[cfg(feature = "arrow-rs")]
impl crate::arrow::LogicalArrayType<NaiveTime> for NaiveTime {
type ExtensionType = crate::arrow::NoExtensionType;
}
pub type NaiveTimeArray<Nullable = NonNullable, Buffer = crate::buffer::VecBuffer> =
LogicalArray<NaiveTime, Nullable, Buffer>;
impl ArrayType<TimeDelta> for TimeDelta {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<Self, NonNullable, Buffer, OffsetItem, UnionLayout>;
}
impl ArrayType<TimeDelta> for Option<TimeDelta> {
type Array<Buffer: BufferType, OffsetItem: Offset, UnionLayout: UnionType> =
LogicalArray<TimeDelta, Nullable, Buffer, OffsetItem, UnionLayout>;
}
impl LogicalArrayType<TimeDelta> for TimeDelta {
type ArrayType = i64;
fn from_array_type(item: Self::ArrayType) -> Self {
Self::nanoseconds(item)
}
fn into_array_type(self) -> Self::ArrayType {
self.num_nanoseconds().expect("out of range")
}
}
#[cfg(feature = "arrow-rs")]
impl crate::arrow::LogicalArrayType<TimeDelta> for TimeDelta {
type ExtensionType = crate::arrow::NoExtensionType;
}
pub type TimeDeltaArray<Nullable = NonNullable, Buffer = crate::buffer::VecBuffer> =
LogicalArray<TimeDelta, Nullable, Buffer>;
#[cfg(test)]
mod tests {
use super::*;
use crate::{Length, Nullable};
#[test]
fn round_trip_naivedate() {
for value in [
NaiveDate::from_yo_opt(2024, 7)
.expect("out of range")
.num_days_from_ce(),
NaiveDate::from_yo_opt(2020, 6)
.expect("out of range")
.num_days_from_ce(),
] {
assert_eq!(NaiveDate::from_array_type(value).into_array_type(), value);
}
}
#[test]
fn round_trip_naivetime() {
for value in [
0,
1234,
1234 * NANO_SECONDS,
86_398 * NANO_SECONDS + 1_999_999_999,
] {
assert_eq!(NaiveTime::from_array_type(value).into_array_type(), value);
}
}
#[test]
fn round_trip_timedelta() {
for value in [
0,
1234,
1234 * NANO_SECONDS,
86_398 * NANO_SECONDS + 1_999_999_999,
] {
assert_eq!(TimeDelta::nanoseconds(value).into_array_type(), value);
}
}
#[test]
fn from_iter() {
let array = [DateTime::<Utc>::UNIX_EPOCH, DateTime::<Utc>::UNIX_EPOCH]
.into_iter()
.collect::<DateTimeArray>();
assert_eq!(array.len(), 2);
assert_eq!(array.0.len(), 2);
let array_nullable = [
Some(DateTime::<Utc>::UNIX_EPOCH),
None,
Some(DateTime::<Utc>::UNIX_EPOCH),
]
.into_iter()
.collect::<DateTimeArray<Nullable>>();
assert_eq!(array_nullable.len(), 3);
assert_eq!(array_nullable.0.len(), 3);
}
#[test]
fn into_iter() {
let input = [DateTime::<Utc>::UNIX_EPOCH, DateTime::<Utc>::UNIX_EPOCH];
let array = input.into_iter().collect::<DateTimeArray>();
let output = array.into_iter().collect::<Vec<_>>();
assert_eq!(input, output.as_slice());
let input_nullable = [
Some(DateTime::<Utc>::UNIX_EPOCH),
None,
Some(DateTime::<Utc>::UNIX_EPOCH),
];
let array_nullable = input_nullable
.into_iter()
.collect::<DateTimeArray<Nullable>>();
let output_nullable = array_nullable.into_iter().collect::<Vec<_>>();
assert_eq!(input_nullable, output_nullable.as_slice());
}
}