polars_core/chunked_array/logical/
time.rs

1use polars_compute::cast::CastOptionsImpl;
2
3use super::*;
4use crate::prelude::*;
5
6pub type TimeChunked = Logical<TimeType, Int64Type>;
7
8impl Int64Chunked {
9    pub fn into_time(mut self) -> TimeChunked {
10        let mut null_count = 0;
11
12        // Invalid time values are replaced with `null` during the arrow cast. We utilize the
13        // validity coming from there to create the new TimeChunked.
14        let chunks = std::mem::take(&mut self.chunks)
15            .into_iter()
16            .map(|chunk| {
17                // We need to retain the PhysicalType underneath, but we should properly update the
18                // validity as that might change because Time is not valid for all values of Int64.
19                let casted = polars_compute::cast::cast(
20                    chunk.as_ref(),
21                    &ArrowDataType::Time64(ArrowTimeUnit::Nanosecond),
22                    CastOptionsImpl::default(),
23                )
24                .unwrap();
25                let validity = casted.validity();
26
27                match validity {
28                    None => chunk,
29                    Some(validity) => {
30                        null_count += validity.unset_bits();
31                        chunk.with_validity(Some(validity.clone()))
32                    },
33                }
34            })
35            .collect::<Vec<Box<dyn Array>>>();
36
37        debug_assert!(null_count >= self.null_count);
38
39        // @TODO: We throw away metadata here. That is mostly not needed.
40        // SAFETY: We calculated the null_count again. And we are taking the rest from the previous
41        // Int64Chunked.
42        let int64chunked =
43            unsafe { Self::new_with_dims(self.field.clone(), chunks, self.length, null_count) };
44
45        // SAFETY: no invalid states.
46        unsafe { TimeChunked::new_logical(int64chunked, DataType::Time) }
47    }
48}
49
50impl LogicalType for TimeChunked {
51    fn dtype(&self) -> &'static DataType {
52        &DataType::Time
53    }
54
55    #[cfg(feature = "dtype-time")]
56    fn get_any_value(&self, i: usize) -> PolarsResult<AnyValue<'_>> {
57        self.phys.get_any_value(i).map(|av| av.as_time())
58    }
59    unsafe fn get_any_value_unchecked(&self, i: usize) -> AnyValue<'_> {
60        self.phys.get_any_value_unchecked(i).as_time()
61    }
62
63    fn cast_with_options(
64        &self,
65        dtype: &DataType,
66        cast_options: CastOptions,
67    ) -> PolarsResult<Series> {
68        use DataType::*;
69        match dtype {
70            Time => Ok(self.clone().into_series()),
71            #[cfg(feature = "dtype-duration")]
72            Duration(tu) => {
73                let out = self
74                    .phys
75                    .cast_with_options(&DataType::Duration(TimeUnit::Nanoseconds), cast_options);
76                if !matches!(tu, TimeUnit::Nanoseconds) {
77                    out?.cast_with_options(dtype, cast_options)
78                } else {
79                    out
80                }
81            },
82            #[cfg(feature = "dtype-datetime")]
83            Datetime(_, _) => {
84                polars_bail!(
85                    InvalidOperation:
86                    "casting from {:?} to {:?} not supported; consider using `dt.combine`",
87                    self.dtype(), dtype
88                )
89            },
90            dt if dt.is_primitive_numeric() => self.phys.cast_with_options(dtype, cast_options),
91            _ => {
92                polars_bail!(
93                    InvalidOperation:
94                    "casting from {:?} to {:?} not supported",
95                    self.dtype(), dtype
96                )
97            },
98        }
99    }
100}