polars_plan/dsl/functions/
temporal.rs1use chrono::{Datelike, Timelike};
2
3use super::*;
4
5macro_rules! impl_unit_setter {
6 ($fn_name:ident($field:ident)) => {
7 #[doc = concat!("Set the ", stringify!($field))]
8 pub fn $fn_name(mut self, n: Expr) -> Self {
9 self.$field = n.into();
10 self
11 }
12 };
13}
14
15#[derive(Debug, Clone)]
33pub struct DatetimeArgs {
34 pub year: Expr,
35 pub month: Expr,
36 pub day: Expr,
37 pub hour: Expr,
38 pub minute: Expr,
39 pub second: Expr,
40 pub microsecond: Expr,
41 pub time_unit: TimeUnit,
42 pub time_zone: Option<TimeZone>,
43 pub ambiguous: Expr,
44}
45
46impl Default for DatetimeArgs {
47 fn default() -> Self {
48 Self {
49 year: lit(1970),
50 month: lit(1),
51 day: lit(1),
52 hour: lit(0),
53 minute: lit(0),
54 second: lit(0),
55 microsecond: lit(0),
56 time_unit: TimeUnit::Microseconds,
57 time_zone: None,
58 ambiguous: lit(String::from("raise")),
59 }
60 }
61}
62
63impl DatetimeArgs {
64 pub fn new(year: Expr, month: Expr, day: Expr) -> Self {
68 Self {
69 year,
70 month,
71 day,
72 ..Default::default()
73 }
74 }
75
76 pub fn with_hms(self, hour: Expr, minute: Expr, second: Expr) -> Self {
85 Self {
86 hour,
87 minute,
88 second,
89 ..self
90 }
91 }
92
93 impl_unit_setter!(with_year(year));
94 impl_unit_setter!(with_month(month));
95 impl_unit_setter!(with_day(day));
96 impl_unit_setter!(with_hour(hour));
97 impl_unit_setter!(with_minute(minute));
98 impl_unit_setter!(with_second(second));
99 impl_unit_setter!(with_microsecond(microsecond));
100
101 pub fn with_time_unit(self, time_unit: TimeUnit) -> Self {
102 Self { time_unit, ..self }
103 }
104 #[cfg(feature = "timezones")]
105 pub fn with_time_zone(self, time_zone: Option<TimeZone>) -> Self {
106 Self { time_zone, ..self }
107 }
108 #[cfg(feature = "timezones")]
109 pub fn with_ambiguous(self, ambiguous: Expr) -> Self {
110 Self { ambiguous, ..self }
111 }
112
113 fn all_literal(&self) -> bool {
114 use Expr::*;
115 [
116 &self.year,
117 &self.month,
118 &self.day,
119 &self.hour,
120 &self.minute,
121 &self.second,
122 &self.microsecond,
123 ]
124 .iter()
125 .all(|e| matches!(e, Literal(_)))
126 }
127
128 fn as_literal(&self) -> Option<Expr> {
129 if self.time_zone.is_some() || !self.all_literal() {
130 return None;
131 };
132 let Expr::Literal(lv) = &self.year else {
133 unreachable!()
134 };
135 let year = lv.to_any_value()?.extract()?;
136 let Expr::Literal(lv) = &self.month else {
137 unreachable!()
138 };
139 let month = lv.to_any_value()?.extract()?;
140 let Expr::Literal(lv) = &self.day else {
141 unreachable!()
142 };
143 let day = lv.to_any_value()?.extract()?;
144 let Expr::Literal(lv) = &self.hour else {
145 unreachable!()
146 };
147 let hour = lv.to_any_value()?.extract()?;
148 let Expr::Literal(lv) = &self.minute else {
149 unreachable!()
150 };
151 let minute = lv.to_any_value()?.extract()?;
152 let Expr::Literal(lv) = &self.second else {
153 unreachable!()
154 };
155 let second = lv.to_any_value()?.extract()?;
156 let Expr::Literal(lv) = &self.microsecond else {
157 unreachable!()
158 };
159 let ms: u32 = lv.to_any_value()?.extract()?;
160
161 let dt = chrono::NaiveDateTime::default()
162 .with_year(year)?
163 .with_month(month)?
164 .with_day(day)?
165 .with_hour(hour)?
166 .with_minute(minute)?
167 .with_second(second)?
168 .with_nanosecond(ms * 1000)?;
169
170 let ts = match self.time_unit {
171 TimeUnit::Milliseconds => dt.and_utc().timestamp_millis(),
172 TimeUnit::Microseconds => dt.and_utc().timestamp_micros(),
173 TimeUnit::Nanoseconds => dt.and_utc().timestamp_nanos_opt()?,
174 };
175
176 Some(
177 Expr::Literal(LiteralValue::Scalar(Scalar::new(
178 DataType::Datetime(self.time_unit, None),
179 AnyValue::Datetime(ts, self.time_unit, None),
180 )))
181 .alias(PlSmallStr::from_static("datetime")),
182 )
183 }
184}
185
186pub fn datetime(args: DatetimeArgs) -> Expr {
188 if let Some(e) = args.as_literal() {
189 return e;
190 }
191
192 let year = args.year;
193 let month = args.month;
194 let day = args.day;
195 let hour = args.hour;
196 let minute = args.minute;
197 let second = args.second;
198 let microsecond = args.microsecond;
199 let time_unit = args.time_unit;
200 let time_zone = args.time_zone;
201 let ambiguous = args.ambiguous;
202
203 let input = vec![
204 year,
205 month,
206 day,
207 hour,
208 minute,
209 second,
210 microsecond,
211 ambiguous,
212 ];
213
214 Expr::Alias(
215 Arc::new(Expr::Function {
216 input,
217 function: FunctionExpr::TemporalExpr(TemporalFunction::DatetimeFunction {
218 time_unit,
219 time_zone,
220 }),
221 options: FunctionOptions::elementwise()
222 .with_flags(|f| f | FunctionFlags::ALLOW_RENAME)
223 .with_fmt_str("datetime"),
224 }),
225 PlSmallStr::from_static("datetime"),
227 )
228}
229
230#[derive(Debug, Clone)]
250pub struct DurationArgs {
251 pub weeks: Expr,
252 pub days: Expr,
253 pub hours: Expr,
254 pub minutes: Expr,
255 pub seconds: Expr,
256 pub milliseconds: Expr,
257 pub microseconds: Expr,
258 pub nanoseconds: Expr,
259 pub time_unit: TimeUnit,
260}
261
262impl Default for DurationArgs {
263 fn default() -> Self {
264 Self {
265 weeks: lit(0),
266 days: lit(0),
267 hours: lit(0),
268 minutes: lit(0),
269 seconds: lit(0),
270 milliseconds: lit(0),
271 microseconds: lit(0),
272 nanoseconds: lit(0),
273 time_unit: TimeUnit::Microseconds,
274 }
275 }
276}
277
278impl DurationArgs {
279 pub fn new() -> Self {
281 Self::default()
282 }
283
284 pub fn with_hms(self, hours: Expr, minutes: Expr, seconds: Expr) -> Self {
294 Self {
295 hours,
296 minutes,
297 seconds,
298 ..self
299 }
300 }
301
302 pub fn with_fractional_seconds(
311 self,
312 milliseconds: Expr,
313 microseconds: Expr,
314 nanoseconds: Expr,
315 ) -> Self {
316 Self {
317 milliseconds,
318 microseconds,
319 nanoseconds,
320 ..self
321 }
322 }
323
324 impl_unit_setter!(with_weeks(weeks));
325 impl_unit_setter!(with_days(days));
326 impl_unit_setter!(with_hours(hours));
327 impl_unit_setter!(with_minutes(minutes));
328 impl_unit_setter!(with_seconds(seconds));
329 impl_unit_setter!(with_milliseconds(milliseconds));
330 impl_unit_setter!(with_microseconds(microseconds));
331 impl_unit_setter!(with_nanoseconds(nanoseconds));
332
333 fn all_literal(&self) -> bool {
334 use Expr::*;
335 [
336 &self.weeks,
337 &self.days,
338 &self.hours,
339 &self.seconds,
340 &self.minutes,
341 &self.milliseconds,
342 &self.microseconds,
343 &self.nanoseconds,
344 ]
345 .iter()
346 .all(|e| matches!(e, Literal(_)))
347 }
348
349 fn as_literal(&self) -> Option<Expr> {
350 if !self.all_literal() {
351 return None;
352 };
353 let Expr::Literal(lv) = &self.weeks else {
354 unreachable!()
355 };
356 let weeks = lv.to_any_value()?.extract()?;
357 let Expr::Literal(lv) = &self.days else {
358 unreachable!()
359 };
360 let days = lv.to_any_value()?.extract()?;
361 let Expr::Literal(lv) = &self.hours else {
362 unreachable!()
363 };
364 let hours = lv.to_any_value()?.extract()?;
365 let Expr::Literal(lv) = &self.seconds else {
366 unreachable!()
367 };
368 let seconds = lv.to_any_value()?.extract()?;
369 let Expr::Literal(lv) = &self.minutes else {
370 unreachable!()
371 };
372 let minutes = lv.to_any_value()?.extract()?;
373 let Expr::Literal(lv) = &self.milliseconds else {
374 unreachable!()
375 };
376 let milliseconds = lv.to_any_value()?.extract()?;
377 let Expr::Literal(lv) = &self.microseconds else {
378 unreachable!()
379 };
380 let microseconds = lv.to_any_value()?.extract()?;
381 let Expr::Literal(lv) = &self.nanoseconds else {
382 unreachable!()
383 };
384 let nanoseconds = lv.to_any_value()?.extract()?;
385
386 type D = chrono::Duration;
387 let delta = D::weeks(weeks)
388 + D::days(days)
389 + D::hours(hours)
390 + D::seconds(seconds)
391 + D::minutes(minutes)
392 + D::milliseconds(milliseconds)
393 + D::microseconds(microseconds)
394 + D::nanoseconds(nanoseconds);
395
396 let d = match self.time_unit {
397 TimeUnit::Milliseconds => delta.num_milliseconds(),
398 TimeUnit::Microseconds => delta.num_microseconds()?,
399 TimeUnit::Nanoseconds => delta.num_nanoseconds()?,
400 };
401
402 Some(
403 Expr::Literal(LiteralValue::Scalar(Scalar::new(
404 DataType::Duration(self.time_unit),
405 AnyValue::Duration(d, self.time_unit),
406 )))
407 .alias(PlSmallStr::from_static("duration")),
408 )
409 }
410}
411
412pub fn duration(args: DurationArgs) -> Expr {
414 if let Some(e) = args.as_literal() {
415 return e;
416 }
417 Expr::Function {
418 input: vec![
419 args.weeks,
420 args.days,
421 args.hours,
422 args.minutes,
423 args.seconds,
424 args.milliseconds,
425 args.microseconds,
426 args.nanoseconds,
427 ],
428 function: FunctionExpr::TemporalExpr(TemporalFunction::Duration(args.time_unit)),
429 options: FunctionOptions::elementwise(),
430 }
431}