polars_core/chunked_array/logical/
datetime.rs

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