1use std::ops::Div;
2
3use polars_arrow::temporal_conversions::{
4 MICROSECONDS_IN_DAY, MILLISECONDS_IN_DAY, NANOSECONDS_IN_DAY,
5};
6use polars_core::prelude::arity::unary_elementwise_values;
7use polars_core::prelude::*;
8
9use crate::chunkedarray::*;
10
11pub trait AsSeries {
12 fn as_series(&self) -> &Series;
13}
14
15impl AsSeries for Series {
16 fn as_series(&self) -> &Series {
17 self
18 }
19}
20
21pub trait TemporalMethods: AsSeries {
22 fn hour(&self) -> PolarsResult<Int8Chunked> {
25 let s = self.as_series();
26 match s.dtype() {
27 #[cfg(feature = "dtype-datetime")]
28 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.hour()),
29 #[cfg(feature = "dtype-time")]
30 DataType::Time => s.time().map(|ca| ca.hour()),
31 dt => polars_bail!(opq = hour, dt),
32 }
33 }
34
35 fn minute(&self) -> PolarsResult<Int8Chunked> {
38 let s = self.as_series();
39 match s.dtype() {
40 #[cfg(feature = "dtype-datetime")]
41 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.minute()),
42 #[cfg(feature = "dtype-time")]
43 DataType::Time => s.time().map(|ca| ca.minute()),
44 dt => polars_bail!(opq = minute, dt),
45 }
46 }
47
48 fn second(&self) -> PolarsResult<Int8Chunked> {
51 let s = self.as_series();
52 match s.dtype() {
53 #[cfg(feature = "dtype-datetime")]
54 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.second()),
55 #[cfg(feature = "dtype-time")]
56 DataType::Time => s.time().map(|ca| ca.second()),
57 dt => polars_bail!(opq = second, dt),
58 }
59 }
60
61 fn nanosecond(&self) -> PolarsResult<Int32Chunked> {
64 let s = self.as_series();
65 match s.dtype() {
66 #[cfg(feature = "dtype-datetime")]
67 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.nanosecond()),
68 #[cfg(feature = "dtype-time")]
69 DataType::Time => s.time().map(|ca| ca.nanosecond()),
70 dt => polars_bail!(opq = nanosecond, dt),
71 }
72 }
73
74 fn day(&self) -> PolarsResult<Int8Chunked> {
79 let s = self.as_series();
80 match s.dtype() {
81 #[cfg(feature = "dtype-date")]
82 DataType::Date => s.date().map(|ca| ca.day()),
83 #[cfg(feature = "dtype-datetime")]
84 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.day()),
85 dt => polars_bail!(opq = day, dt),
86 }
87 }
88 fn weekday(&self) -> PolarsResult<Int8Chunked> {
90 let s = self.as_series();
91 match s.dtype() {
92 #[cfg(feature = "dtype-date")]
93 DataType::Date => s.date().map(|ca| {
94 unary_elementwise_values(ca.physical(), |t| (((t - 4) % 7 + 7) % 7 + 1) as i8)
98 }),
99 #[cfg(feature = "dtype-datetime")]
100 DataType::Datetime(time_unit, time_zone) => s.datetime().map(|ca| {
101 match time_zone.as_deref().map(|x| x.as_str()) {
102 Some("UTC") | None => {
103 let divisor = match time_unit {
107 TimeUnit::Milliseconds => MILLISECONDS_IN_DAY,
108 TimeUnit::Microseconds => MICROSECONDS_IN_DAY,
109 TimeUnit::Nanoseconds => NANOSECONDS_IN_DAY,
110 };
111 unary_elementwise_values(ca.physical(), |t| {
112 let t = t / divisor - ((t < 0 && t % divisor != 0) as i64);
113 (((t - 4) % 7 + 7) % 7 + 1) as i8
114 })
115 },
116 _ => ca.weekday(),
117 }
118 }),
119 dt => polars_bail!(opq = weekday, dt),
120 }
121 }
122
123 fn week(&self) -> PolarsResult<Int8Chunked> {
126 let s = self.as_series();
127 match s.dtype() {
128 #[cfg(feature = "dtype-date")]
129 DataType::Date => s.date().map(|ca| ca.week()),
130 #[cfg(feature = "dtype-datetime")]
131 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.week()),
132 dt => polars_bail!(opq = week, dt),
133 }
134 }
135
136 fn ordinal_day(&self) -> PolarsResult<Int16Chunked> {
140 let s = self.as_series();
141 match s.dtype() {
142 #[cfg(feature = "dtype-date")]
143 DataType::Date => s.date().map(|ca| ca.ordinal()),
144 #[cfg(feature = "dtype-datetime")]
145 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.ordinal()),
146 dt => polars_bail!(opq = ordinal_day, dt),
147 }
148 }
149
150 fn millennium(&self) -> PolarsResult<Int32Chunked> {
152 let s = self.as_series();
153 match s.dtype() {
154 #[cfg(feature = "dtype-date")]
157 DataType::Date => s.date().map(|ca| (ca.year() - 1i32).div(1000f64) + 1),
158 #[cfg(feature = "dtype-datetime")]
159 DataType::Datetime(_, _) => s.datetime().map(|ca| (ca.year() - 1i32).div(1000f64) + 1),
160 dt => polars_bail!(opq = century, dt),
161 }
162 }
163
164 fn century(&self) -> PolarsResult<Int32Chunked> {
166 let s = self.as_series();
167 match s.dtype() {
168 #[cfg(feature = "dtype-date")]
171 DataType::Date => s.date().map(|ca| (ca.year() - 1i32).div(100f64) + 1),
172 #[cfg(feature = "dtype-datetime")]
173 DataType::Datetime(_, _) => s.datetime().map(|ca| (ca.year() - 1i32).div(100f64) + 1),
174 dt => polars_bail!(opq = century, dt),
175 }
176 }
177
178 fn year(&self) -> PolarsResult<Int32Chunked> {
181 let s = self.as_series();
182 match s.dtype() {
183 #[cfg(feature = "dtype-date")]
184 DataType::Date => s.date().map(|ca| ca.year()),
185 #[cfg(feature = "dtype-datetime")]
186 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.year()),
187 dt => polars_bail!(opq = year, dt),
188 }
189 }
190
191 fn iso_year(&self) -> PolarsResult<Int32Chunked> {
192 let s = self.as_series();
193 match s.dtype() {
194 #[cfg(feature = "dtype-date")]
195 DataType::Date => s.date().map(|ca| ca.iso_year()),
196 #[cfg(feature = "dtype-datetime")]
197 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.iso_year()),
198 dt => polars_bail!(opq = iso_year, dt),
199 }
200 }
201
202 fn ordinal_year(&self) -> PolarsResult<Int32Chunked> {
205 let s = self.as_series();
206 match s.dtype() {
207 #[cfg(feature = "dtype-date")]
208 DataType::Date => s.date().map(|ca| ca.year()),
209 #[cfg(feature = "dtype-datetime")]
210 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.year()),
211 dt => polars_bail!(opq = ordinal_year, dt),
212 }
213 }
214
215 fn is_leap_year(&self) -> PolarsResult<BooleanChunked> {
218 let s = self.as_series();
219 match s.dtype() {
220 #[cfg(feature = "dtype-date")]
221 DataType::Date => s.date().map(|ca| ca.is_leap_year()),
222 #[cfg(feature = "dtype-datetime")]
223 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.is_leap_year()),
224 dt => polars_bail!(opq = is_leap_year, dt),
225 }
226 }
227
228 fn quarter(&self) -> PolarsResult<Int8Chunked> {
231 let s = self.as_series();
232 match s.dtype() {
233 #[cfg(feature = "dtype-date")]
234 DataType::Date => s.date().map(|ca| ca.quarter()),
235 #[cfg(feature = "dtype-datetime")]
236 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.quarter()),
237 dt => polars_bail!(opq = quarter, dt),
238 }
239 }
240
241 fn month(&self) -> PolarsResult<Int8Chunked> {
246 let s = self.as_series();
247 match s.dtype() {
248 #[cfg(feature = "dtype-date")]
249 DataType::Date => s.date().map(|ca| ca.month()),
250 #[cfg(feature = "dtype-datetime")]
251 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.month()),
252 dt => polars_bail!(opq = month, dt),
253 }
254 }
255
256 fn days_in_month(&self) -> PolarsResult<Int8Chunked> {
259 let s = self.as_series();
260 match s.dtype() {
261 #[cfg(feature = "dtype-date")]
262 DataType::Date => s.date().map(|ca| ca.days_in_month()),
263 #[cfg(feature = "dtype-datetime")]
264 DataType::Datetime(_, _) => s.datetime().map(|ca| ca.days_in_month()),
265 dt => polars_bail!(opq = days_in_month, dt),
266 }
267 }
268
269 fn to_string(&self, format: &str) -> PolarsResult<Series> {
272 let s = self.as_series();
273 match s.dtype() {
274 #[cfg(feature = "dtype-datetime")]
275 DataType::Datetime(_, _) => {
276 let format = get_strftime_format(format, s.dtype())?;
277 s.datetime()
278 .map(|ca| Ok(ca.to_string(format.as_str())?.into_series()))?
279 },
280 #[cfg(feature = "dtype-date")]
281 DataType::Date => {
282 let format = get_strftime_format(format, s.dtype())?;
283 s.date()
284 .map(|ca| Ok(ca.to_string(format.as_str())?.into_series()))?
285 },
286 #[cfg(feature = "dtype-time")]
287 DataType::Time => {
288 let format = get_strftime_format(format, s.dtype())?;
289 s.time()
290 .map(|ca| ca.to_string(format.as_str()).into_series())
291 },
292 #[cfg(feature = "dtype-duration")]
293 DataType::Duration(_) => s
294 .duration()
295 .map(|ca| Ok(ca.to_string(format)?.into_series()))?,
296 dt => polars_bail!(opq = to_string, dt),
297 }
298 }
299
300 fn strftime(&self, format: &str) -> PolarsResult<Series> {
305 self.to_string(format)
306 }
307
308 #[cfg(feature = "temporal")]
309 fn timestamp(&self, tu: TimeUnit) -> PolarsResult<Int64Chunked> {
311 let s = self.as_series();
312 if matches!(s.dtype(), DataType::Time | DataType::Duration(_)) {
313 polars_bail!(opq = timestamp, s.dtype());
314 } else {
315 s.cast(&DataType::Datetime(tu, None))
316 .map(|s| s.datetime().unwrap().physical().clone())
317 }
318 }
319}
320
321impl<T: ?Sized + AsSeries> TemporalMethods for T {}