polars_core/chunked_array/logical/
datetime.rs

1use super::*;
2use crate::datatypes::time_unit::TimeUnit;
3use crate::prelude::*;
4
5pub type DatetimeChunked = Logical<DatetimeType, Int64Type>;
6
7impl Int64Chunked {
8    pub fn into_datetime(self, timeunit: TimeUnit, tz: Option<TimeZone>) -> DatetimeChunked {
9        // SAFETY: no invalid states.
10        unsafe { DatetimeChunked::new_logical(self, DataType::Datetime(timeunit, tz)) }
11    }
12}
13
14impl LogicalType for DatetimeChunked {
15    fn dtype(&self) -> &DataType {
16        &self.dtype
17    }
18
19    fn get_any_value(&self, i: usize) -> PolarsResult<AnyValue<'_>> {
20        self.phys
21            .get_any_value(i)
22            .map(|av| av.as_datetime(self.time_unit(), self.time_zone().as_ref()))
23    }
24
25    unsafe fn get_any_value_unchecked(&self, i: usize) -> AnyValue<'_> {
26        self.phys
27            .get_any_value_unchecked(i)
28            .as_datetime(self.time_unit(), self.time_zone().as_ref())
29    }
30
31    fn cast_with_options(
32        &self,
33        dtype: &DataType,
34        cast_options: CastOptions,
35    ) -> PolarsResult<Series> {
36        use DataType::*;
37
38        use crate::datatypes::time_unit::TimeUnit::*;
39
40        let out = match dtype {
41            Datetime(to_unit, tz) => {
42                let from_unit = self.time_unit();
43                let (multiplier, divisor) = match (from_unit, to_unit) {
44                    // scaling from lower precision to higher precision
45                    (Milliseconds, Nanoseconds) => (Some(1_000_000i64), None),
46                    (Milliseconds, Microseconds) => (Some(1_000i64), None),
47                    (Microseconds, Nanoseconds) => (Some(1_000i64), None),
48                    // scaling from higher precision to lower precision
49                    (Nanoseconds, Milliseconds) => (None, Some(1_000_000i64)),
50                    (Nanoseconds, Microseconds) => (None, Some(1_000i64)),
51                    (Microseconds, Milliseconds) => (None, Some(1_000i64)),
52                    _ => return self.phys.cast_with_options(dtype, cast_options),
53                };
54                match multiplier {
55                    // scale to higher precision (eg: ms → us, ms → ns, us → ns)
56                    Some(m) => Ok((self.phys.as_ref().checked_mul_scalar(m))
57                        .into_datetime(*to_unit, tz.clone())
58                        .into_series()),
59                    // scale to lower precision (eg: ns → us, ns → ms, us → ms)
60                    None => match divisor {
61                        Some(d) => Ok(self
62                            .phys
63                            .apply_values(|v| v.div_euclid(d))
64                            .into_datetime(*to_unit, tz.clone())
65                            .into_series()),
66                        None => unreachable!("must always have a time unit divisor here"),
67                    },
68                }
69            },
70            #[cfg(feature = "dtype-date")]
71            Date => {
72                let cast_to_date = |tu_in_day: i64| {
73                    let mut dt = self
74                        .phys
75                        .apply_values(|v| v.div_euclid(tu_in_day))
76                        .cast_with_options(&Int32, cast_options)
77                        .unwrap()
78                        .into_date()
79                        .into_series();
80                    dt.set_sorted_flag(self.physical().is_sorted_flag());
81                    Ok(dt)
82                };
83                match self.time_unit() {
84                    Nanoseconds => cast_to_date(NS_IN_DAY),
85                    Microseconds => cast_to_date(US_IN_DAY),
86                    Milliseconds => cast_to_date(MS_IN_DAY),
87                }
88            },
89            #[cfg(feature = "dtype-time")]
90            Time => {
91                let (scaled_mod, multiplier) = match self.time_unit() {
92                    Nanoseconds => (NS_IN_DAY, 1i64),
93                    Microseconds => (US_IN_DAY, 1_000i64),
94                    Milliseconds => (MS_IN_DAY, 1_000_000i64),
95                };
96                return Ok(self
97                    .phys
98                    .apply(|v| {
99                        let t = (v? % scaled_mod).checked_mul(multiplier)?;
100                        t.checked_add(NS_IN_DAY * (t < 0) as i64)
101                    })
102                    .into_time()
103                    .into_series());
104            },
105            dt if dt.is_primitive_numeric() => {
106                return self.phys.cast_with_options(dtype, cast_options);
107            },
108            dt => {
109                polars_bail!(
110                    InvalidOperation:
111                    "casting from {:?} to {:?} not supported",
112                    self.dtype(), dt
113                )
114            },
115        };
116        out.map(|mut s| {
117            // TODO!; implement the divisions/multipliers above
118            // in a checked manner so that we raise on overflow
119            s.set_sorted_flag(self.physical().is_sorted_flag());
120            s
121        })
122    }
123}