use chrono::{Datelike, Timelike};
use polars_error::PolarsResult;
use super::arity::unary;
use crate::array::*;
use crate::datatypes::*;
use crate::temporal_conversions::*;
use crate::types::NativeType;
trait Int8Weekday: Datelike {
fn i8_weekday(&self) -> i8 {
self.weekday().number_from_monday().try_into().unwrap()
}
}
impl Int8Weekday for chrono::NaiveDateTime {}
impl<T: chrono::TimeZone> Int8Weekday for chrono::DateTime<T> {}
trait Int8IsoWeek: Datelike {
fn i8_iso_week(&self) -> i8 {
self.iso_week().week().try_into().unwrap()
}
}
impl Int8IsoWeek for chrono::NaiveDateTime {}
impl<T: chrono::TimeZone> Int8IsoWeek for chrono::DateTime<T> {}
macro_rules! date_like {
($extract:ident, $array:ident, $dtype:path) => {
match $array.dtype().to_logical_type() {
ArrowDataType::Date32 | ArrowDataType::Date64 | ArrowDataType::Timestamp(_, None) => {
date_variants($array, $dtype, |x| x.$extract().try_into().unwrap())
},
ArrowDataType::Timestamp(time_unit, Some(timezone_str)) => {
let array = $array.as_any().downcast_ref().unwrap();
if let Ok(timezone) = parse_offset(timezone_str.as_str()) {
Ok(extract_impl(array, *time_unit, timezone, |x| {
x.$extract().try_into().unwrap()
}))
} else {
chrono_tz(array, *time_unit, timezone_str.as_str(), |x| {
x.$extract().try_into().unwrap()
})
}
},
_ => unimplemented!(),
}
};
}
pub fn year(array: &dyn Array) -> PolarsResult<PrimitiveArray<i32>> {
date_like!(year, array, ArrowDataType::Int32)
}
pub fn month(array: &dyn Array) -> PolarsResult<PrimitiveArray<i8>> {
date_like!(month, array, ArrowDataType::Int8)
}
pub fn day(array: &dyn Array) -> PolarsResult<PrimitiveArray<i8>> {
date_like!(day, array, ArrowDataType::Int8)
}
pub fn weekday(array: &dyn Array) -> PolarsResult<PrimitiveArray<i8>> {
date_like!(i8_weekday, array, ArrowDataType::Int8)
}
pub fn iso_week(array: &dyn Array) -> PolarsResult<PrimitiveArray<i8>> {
date_like!(i8_iso_week, array, ArrowDataType::Int8)
}
macro_rules! time_like {
($extract:ident, $array:ident, $dtype:path) => {
match $array.dtype().to_logical_type() {
ArrowDataType::Date32 | ArrowDataType::Date64 | ArrowDataType::Timestamp(_, None) => {
date_variants($array, $dtype, |x| x.$extract().try_into().unwrap())
},
ArrowDataType::Time32(_) | ArrowDataType::Time64(_) => {
time_variants($array, ArrowDataType::UInt32, |x| {
x.$extract().try_into().unwrap()
})
},
ArrowDataType::Timestamp(time_unit, Some(timezone_str)) => {
let array = $array.as_any().downcast_ref().unwrap();
if let Ok(timezone) = parse_offset(timezone_str.as_str()) {
Ok(extract_impl(array, *time_unit, timezone, |x| {
x.$extract().try_into().unwrap()
}))
} else {
chrono_tz(array, *time_unit, timezone_str.as_str(), |x| {
x.$extract().try_into().unwrap()
})
}
},
_ => unimplemented!(),
}
};
}
pub fn hour(array: &dyn Array) -> PolarsResult<PrimitiveArray<i8>> {
time_like!(hour, array, ArrowDataType::Int8)
}
pub fn minute(array: &dyn Array) -> PolarsResult<PrimitiveArray<i8>> {
time_like!(minute, array, ArrowDataType::Int8)
}
pub fn second(array: &dyn Array) -> PolarsResult<PrimitiveArray<i8>> {
time_like!(second, array, ArrowDataType::Int8)
}
pub fn nanosecond(array: &dyn Array) -> PolarsResult<PrimitiveArray<i32>> {
time_like!(nanosecond, array, ArrowDataType::Int32)
}
fn date_variants<F, O>(
array: &dyn Array,
dtype: ArrowDataType,
op: F,
) -> PolarsResult<PrimitiveArray<O>>
where
O: NativeType,
F: Fn(chrono::NaiveDateTime) -> O,
{
match array.dtype().to_logical_type() {
ArrowDataType::Date32 => {
let array = array
.as_any()
.downcast_ref::<PrimitiveArray<i32>>()
.unwrap();
Ok(unary(array, |x| op(date32_to_datetime(x)), dtype))
},
ArrowDataType::Date64 => {
let array = array
.as_any()
.downcast_ref::<PrimitiveArray<i64>>()
.unwrap();
Ok(unary(array, |x| op(date64_to_datetime(x)), dtype))
},
ArrowDataType::Timestamp(time_unit, None) => {
let array = array
.as_any()
.downcast_ref::<PrimitiveArray<i64>>()
.unwrap();
let func = match time_unit {
TimeUnit::Second => timestamp_s_to_datetime,
TimeUnit::Millisecond => timestamp_ms_to_datetime,
TimeUnit::Microsecond => timestamp_us_to_datetime,
TimeUnit::Nanosecond => timestamp_ns_to_datetime,
};
Ok(PrimitiveArray::<O>::from_trusted_len_iter(
array.iter().map(|v| v.map(|x| op(func(*x)))),
))
},
_ => unreachable!(),
}
}
fn time_variants<F, O>(
array: &dyn Array,
dtype: ArrowDataType,
op: F,
) -> PolarsResult<PrimitiveArray<O>>
where
O: NativeType,
F: Fn(chrono::NaiveTime) -> O,
{
match array.dtype().to_logical_type() {
ArrowDataType::Time32(TimeUnit::Second) => {
let array = array
.as_any()
.downcast_ref::<PrimitiveArray<i32>>()
.unwrap();
Ok(unary(array, |x| op(time32s_to_time(x)), dtype))
},
ArrowDataType::Time32(TimeUnit::Millisecond) => {
let array = array
.as_any()
.downcast_ref::<PrimitiveArray<i32>>()
.unwrap();
Ok(unary(array, |x| op(time32ms_to_time(x)), dtype))
},
ArrowDataType::Time64(TimeUnit::Microsecond) => {
let array = array
.as_any()
.downcast_ref::<PrimitiveArray<i64>>()
.unwrap();
Ok(unary(array, |x| op(time64us_to_time(x)), dtype))
},
ArrowDataType::Time64(TimeUnit::Nanosecond) => {
let array = array
.as_any()
.downcast_ref::<PrimitiveArray<i64>>()
.unwrap();
Ok(unary(array, |x| op(time64ns_to_time(x)), dtype))
},
_ => unreachable!(),
}
}
#[cfg(feature = "chrono-tz")]
fn chrono_tz<F, O>(
array: &PrimitiveArray<i64>,
time_unit: TimeUnit,
timezone_str: &str,
op: F,
) -> PolarsResult<PrimitiveArray<O>>
where
O: NativeType,
F: Fn(chrono::DateTime<chrono_tz::Tz>) -> O,
{
let timezone = parse_offset_tz(timezone_str)?;
Ok(extract_impl(array, time_unit, timezone, op))
}
#[cfg(not(feature = "chrono-tz"))]
fn chrono_tz<F, O>(
_: &PrimitiveArray<i64>,
_: TimeUnit,
timezone_str: &str,
_: F,
) -> PolarsResult<PrimitiveArray<O>>
where
O: NativeType,
F: Fn(chrono::DateTime<chrono::FixedOffset>) -> O,
{
panic!(
"timezone \"{}\" cannot be parsed (feature chrono-tz is not active)",
timezone_str
)
}
fn extract_impl<T, A, F>(
array: &PrimitiveArray<i64>,
time_unit: TimeUnit,
timezone: T,
extract: F,
) -> PrimitiveArray<A>
where
T: chrono::TimeZone,
A: NativeType,
F: Fn(chrono::DateTime<T>) -> A,
{
match time_unit {
TimeUnit::Second => {
let op = |x| {
let datetime = timestamp_s_to_datetime(x);
let offset = timezone.offset_from_utc_datetime(&datetime);
extract(chrono::DateTime::<T>::from_naive_utc_and_offset(
datetime, offset,
))
};
unary(array, op, A::PRIMITIVE.into())
},
TimeUnit::Millisecond => {
let op = |x| {
let datetime = timestamp_ms_to_datetime(x);
let offset = timezone.offset_from_utc_datetime(&datetime);
extract(chrono::DateTime::<T>::from_naive_utc_and_offset(
datetime, offset,
))
};
unary(array, op, A::PRIMITIVE.into())
},
TimeUnit::Microsecond => {
let op = |x| {
let datetime = timestamp_us_to_datetime(x);
let offset = timezone.offset_from_utc_datetime(&datetime);
extract(chrono::DateTime::<T>::from_naive_utc_and_offset(
datetime, offset,
))
};
unary(array, op, A::PRIMITIVE.into())
},
TimeUnit::Nanosecond => {
let op = |x| {
let datetime = timestamp_ns_to_datetime(x);
let offset = timezone.offset_from_utc_datetime(&datetime);
extract(chrono::DateTime::<T>::from_naive_utc_and_offset(
datetime, offset,
))
};
unary(array, op, A::PRIMITIVE.into())
},
}
}