Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-expr/src/dispatch/datetime.rs
7884 views
1
#[cfg(feature = "timezones")]
2
use arrow::legacy::time_zone::Tz;
3
use polars_core::error::{PolarsResult, polars_bail};
4
use polars_core::prelude::{
5
ArithmeticChunked, Column, DataType, IntoColumn, LogicalType, TimeUnit,
6
};
7
#[cfg(feature = "timezones")]
8
use polars_core::prelude::{NonExistent, StringChunked, TimeZone};
9
use polars_time::prelude::*;
10
use polars_time::replace_datetime;
11
use polars_time::series::TemporalMethods;
12
13
pub(super) fn millennium(s: &Column) -> PolarsResult<Column> {
14
s.as_materialized_series()
15
.millennium()
16
.map(|ca| ca.into_column())
17
}
18
pub(super) fn century(s: &Column) -> PolarsResult<Column> {
19
s.as_materialized_series()
20
.century()
21
.map(|ca| ca.into_column())
22
}
23
pub(super) fn year(s: &Column) -> PolarsResult<Column> {
24
s.as_materialized_series().year().map(|ca| ca.into_column())
25
}
26
pub(super) fn is_leap_year(s: &Column) -> PolarsResult<Column> {
27
s.as_materialized_series()
28
.is_leap_year()
29
.map(|ca| ca.into_column())
30
}
31
pub(super) fn iso_year(s: &Column) -> PolarsResult<Column> {
32
s.as_materialized_series()
33
.iso_year()
34
.map(|ca| ca.into_column())
35
}
36
pub(super) fn month(s: &Column) -> PolarsResult<Column> {
37
s.as_materialized_series()
38
.month()
39
.map(|ca| ca.into_column())
40
}
41
pub(super) fn days_in_month(s: &Column) -> PolarsResult<Column> {
42
s.as_materialized_series()
43
.days_in_month()
44
.map(|ca| ca.into_column())
45
}
46
pub(super) fn quarter(s: &Column) -> PolarsResult<Column> {
47
s.as_materialized_series()
48
.quarter()
49
.map(|ca| ca.into_column())
50
}
51
pub(super) fn week(s: &Column) -> PolarsResult<Column> {
52
s.as_materialized_series().week().map(|ca| ca.into_column())
53
}
54
pub(super) fn weekday(s: &Column) -> PolarsResult<Column> {
55
s.as_materialized_series()
56
.weekday()
57
.map(|ca| ca.into_column())
58
}
59
pub(super) fn day(s: &Column) -> PolarsResult<Column> {
60
s.as_materialized_series().day().map(|ca| ca.into_column())
61
}
62
pub(super) fn ordinal_day(s: &Column) -> PolarsResult<Column> {
63
s.as_materialized_series()
64
.ordinal_day()
65
.map(|ca| ca.into_column())
66
}
67
pub(super) fn time(s: &Column) -> PolarsResult<Column> {
68
match s.dtype() {
69
#[cfg(feature = "timezones")]
70
DataType::Datetime(_, Some(_)) => polars_ops::prelude::replace_time_zone(
71
s.datetime().unwrap(),
72
None,
73
&StringChunked::from_iter(std::iter::once("raise")),
74
NonExistent::Raise,
75
)?
76
.cast(&DataType::Time)
77
.map(Column::from),
78
DataType::Datetime(_, _) => s
79
.datetime()
80
.unwrap()
81
.cast(&DataType::Time)
82
.map(Column::from),
83
DataType::Time => Ok(s.clone()),
84
dtype => polars_bail!(ComputeError: "expected Datetime or Time, got {}", dtype),
85
}
86
}
87
pub(super) fn date(s: &Column) -> PolarsResult<Column> {
88
match s.dtype() {
89
#[cfg(feature = "timezones")]
90
DataType::Datetime(_, Some(_)) => {
91
let mut out = {
92
polars_ops::chunked_array::replace_time_zone(
93
s.datetime().unwrap(),
94
None,
95
&StringChunked::from_iter(std::iter::once("raise")),
96
NonExistent::Raise,
97
)?
98
.cast(&DataType::Date)?
99
};
100
// `replace_time_zone` may unset sorted flag. But, we're only taking the date
101
// part of the result, so we can safely preserve the sorted flag here. We may
102
// need to make an exception if a time zone introduces a change which involves
103
// "going back in time" and repeating a day, but we're not aware of that ever
104
// having happened.
105
out.set_sorted_flag(s.is_sorted_flag());
106
Ok(out.into())
107
},
108
DataType::Datetime(_, _) => s
109
.datetime()
110
.unwrap()
111
.cast(&DataType::Date)
112
.map(Column::from),
113
DataType::Date => Ok(s.clone()),
114
dtype => polars_bail!(ComputeError: "expected Datetime or Date, got {}", dtype),
115
}
116
}
117
pub(super) fn datetime(s: &Column) -> PolarsResult<Column> {
118
match s.dtype() {
119
#[cfg(feature = "timezones")]
120
DataType::Datetime(tu, Some(_)) => polars_ops::chunked_array::replace_time_zone(
121
s.datetime().unwrap(),
122
None,
123
&StringChunked::from_iter(std::iter::once("raise")),
124
NonExistent::Raise,
125
)?
126
.cast(&DataType::Datetime(*tu, None))
127
.map(|x| x.into()),
128
DataType::Datetime(tu, _) => s
129
.datetime()
130
.unwrap()
131
.cast(&DataType::Datetime(*tu, None))
132
.map(Column::from),
133
dtype => polars_bail!(ComputeError: "expected Datetime, got {}", dtype),
134
}
135
}
136
pub(super) fn hour(s: &Column) -> PolarsResult<Column> {
137
s.as_materialized_series().hour().map(|ca| ca.into_column())
138
}
139
pub(super) fn minute(s: &Column) -> PolarsResult<Column> {
140
s.as_materialized_series()
141
.minute()
142
.map(|ca| ca.into_column())
143
}
144
pub(super) fn second(s: &Column) -> PolarsResult<Column> {
145
s.as_materialized_series()
146
.second()
147
.map(|ca| ca.into_column())
148
}
149
pub(super) fn millisecond(s: &Column) -> PolarsResult<Column> {
150
s.as_materialized_series()
151
.nanosecond()
152
.map(|ca| (ca.wrapping_trunc_div_scalar(1_000_000)).into_column())
153
}
154
pub(super) fn microsecond(s: &Column) -> PolarsResult<Column> {
155
s.as_materialized_series()
156
.nanosecond()
157
.map(|ca| (ca.wrapping_trunc_div_scalar(1_000)).into_column())
158
}
159
pub(super) fn nanosecond(s: &Column) -> PolarsResult<Column> {
160
s.as_materialized_series()
161
.nanosecond()
162
.map(|ca| ca.into_column())
163
}
164
#[cfg(feature = "dtype-duration")]
165
pub(super) fn total_days(s: &Column) -> PolarsResult<Column> {
166
use polars_time::prelude::DurationMethods;
167
168
s.as_materialized_series()
169
.duration()
170
.map(|ca| ca.days().into_column())
171
}
172
#[cfg(feature = "dtype-duration")]
173
pub(super) fn total_days_fractional(s: &Column) -> PolarsResult<Column> {
174
use polars_time::prelude::DurationMethods;
175
176
s.as_materialized_series()
177
.duration()
178
.map(|ca| ca.days_fractional().into_column())
179
}
180
#[cfg(feature = "dtype-duration")]
181
pub(super) fn total_hours(s: &Column) -> PolarsResult<Column> {
182
use polars_time::prelude::DurationMethods;
183
184
s.as_materialized_series()
185
.duration()
186
.map(|ca| ca.hours().into_column())
187
}
188
#[cfg(feature = "dtype-duration")]
189
pub(super) fn total_hours_fractional(s: &Column) -> PolarsResult<Column> {
190
use polars_time::prelude::DurationMethods;
191
192
s.as_materialized_series()
193
.duration()
194
.map(|ca| ca.hours_fractional().into_column())
195
}
196
#[cfg(feature = "dtype-duration")]
197
pub(super) fn total_minutes(s: &Column) -> PolarsResult<Column> {
198
use polars_time::prelude::DurationMethods;
199
200
s.as_materialized_series()
201
.duration()
202
.map(|ca| ca.minutes().into_column())
203
}
204
#[cfg(feature = "dtype-duration")]
205
pub(super) fn total_minutes_fractional(s: &Column) -> PolarsResult<Column> {
206
use polars_time::prelude::DurationMethods;
207
208
s.as_materialized_series()
209
.duration()
210
.map(|ca| ca.minutes_fractional().into_column())
211
}
212
#[cfg(feature = "dtype-duration")]
213
pub(super) fn total_seconds(s: &Column) -> PolarsResult<Column> {
214
use polars_time::prelude::DurationMethods;
215
216
s.as_materialized_series()
217
.duration()
218
.map(|ca| ca.seconds().into_column())
219
}
220
#[cfg(feature = "dtype-duration")]
221
pub(super) fn total_seconds_fractional(s: &Column) -> PolarsResult<Column> {
222
s.as_materialized_series()
223
.duration()
224
.map(|ca| ca.seconds_fractional().into_column())
225
}
226
#[cfg(feature = "dtype-duration")]
227
pub(super) fn total_milliseconds(s: &Column) -> PolarsResult<Column> {
228
s.as_materialized_series()
229
.duration()
230
.map(|ca| ca.milliseconds().into_column())
231
}
232
#[cfg(feature = "dtype-duration")]
233
pub(super) fn total_milliseconds_fractional(s: &Column) -> PolarsResult<Column> {
234
s.as_materialized_series()
235
.duration()
236
.map(|ca| ca.milliseconds_fractional().into_column())
237
}
238
#[cfg(feature = "dtype-duration")]
239
pub(super) fn total_microseconds(s: &Column) -> PolarsResult<Column> {
240
s.as_materialized_series()
241
.duration()
242
.map(|ca| ca.microseconds().into_column())
243
}
244
#[cfg(feature = "dtype-duration")]
245
pub(super) fn total_microseconds_fractional(s: &Column) -> PolarsResult<Column> {
246
s.as_materialized_series()
247
.duration()
248
.map(|ca| ca.microseconds_fractional().into_column())
249
}
250
#[cfg(feature = "dtype-duration")]
251
pub(super) fn total_nanoseconds(s: &Column) -> PolarsResult<Column> {
252
s.as_materialized_series()
253
.duration()
254
.map(|ca| ca.nanoseconds().into_column())
255
}
256
#[cfg(feature = "dtype-duration")]
257
pub(super) fn total_nanoseconds_fractional(s: &Column) -> PolarsResult<Column> {
258
s.as_materialized_series()
259
.duration()
260
.map(|ca| ca.nanoseconds_fractional().into_column())
261
}
262
pub(super) fn timestamp(s: &Column, tu: TimeUnit) -> PolarsResult<Column> {
263
s.as_materialized_series()
264
.timestamp(tu)
265
.map(|ca| ca.into_column())
266
}
267
pub(super) fn to_string(s: &Column, format: &str) -> PolarsResult<Column> {
268
TemporalMethods::to_string(s.as_materialized_series(), format).map(Column::from)
269
}
270
271
#[cfg(feature = "timezones")]
272
pub(super) fn convert_time_zone(s: &Column, time_zone: &TimeZone) -> PolarsResult<Column> {
273
match s.dtype() {
274
DataType::Datetime(_, _) => {
275
let mut ca = s.datetime()?.clone();
276
ca.set_time_zone(time_zone.clone())?;
277
Ok(ca.into_column())
278
},
279
dtype => polars_bail!(ComputeError: "expected Datetime, got {}", dtype),
280
}
281
}
282
pub(super) fn with_time_unit(s: &Column, tu: TimeUnit) -> PolarsResult<Column> {
283
match s.dtype() {
284
DataType::Datetime(_, _) => {
285
let mut ca = s.datetime()?.clone();
286
ca.set_time_unit(tu);
287
Ok(ca.into_column())
288
},
289
#[cfg(feature = "dtype-duration")]
290
DataType::Duration(_) => {
291
let mut ca = s.as_materialized_series().duration()?.clone();
292
ca.set_time_unit(tu);
293
Ok(ca.into_column())
294
},
295
dt => polars_bail!(ComputeError: "dtype `{}` has no time unit", dt),
296
}
297
}
298
pub(super) fn cast_time_unit(s: &Column, tu: TimeUnit) -> PolarsResult<Column> {
299
match s.dtype() {
300
DataType::Datetime(_, _) => {
301
let ca = s.datetime()?;
302
Ok(ca.cast_time_unit(tu).into_column())
303
},
304
#[cfg(feature = "dtype-duration")]
305
DataType::Duration(_) => {
306
let ca = s.as_materialized_series().duration()?;
307
Ok(ca.cast_time_unit(tu).into_column())
308
},
309
dt => polars_bail!(ComputeError: "dtype `{}` has no time unit", dt),
310
}
311
}
312
313
pub(super) fn truncate(s: &[Column]) -> PolarsResult<Column> {
314
let time_series = &s[0];
315
let every = s[1].str()?;
316
317
let mut out = match time_series.dtype() {
318
DataType::Datetime(_, tz) => match tz {
319
#[cfg(feature = "timezones")]
320
Some(tz) => time_series
321
.datetime()?
322
.truncate(tz.parse::<Tz>().ok().as_ref(), every)?
323
.into_column(),
324
_ => time_series.datetime()?.truncate(None, every)?.into_column(),
325
},
326
DataType::Date => time_series.date()?.truncate(None, every)?.into_column(),
327
dt => polars_bail!(opq = round, got = dt, expected = "date/datetime"),
328
};
329
out.set_sorted_flag(time_series.is_sorted_flag());
330
Ok(out)
331
}
332
333
#[cfg(feature = "offset_by")]
334
pub(super) fn offset_by(s: &[Column]) -> PolarsResult<Column> {
335
use polars_time::impl_offset_by;
336
337
impl_offset_by(s[0].as_materialized_series(), s[1].as_materialized_series()).map(Column::from)
338
}
339
340
#[cfg(feature = "month_start")]
341
pub(super) fn month_start(s: &Column) -> PolarsResult<Column> {
342
Ok(match s.dtype() {
343
DataType::Datetime(_, tz) => match tz {
344
#[cfg(feature = "timezones")]
345
Some(tz) => s
346
.datetime()
347
.unwrap()
348
.month_start(tz.parse::<Tz>().ok().as_ref())?
349
.into_column(),
350
_ => s.datetime().unwrap().month_start(None)?.into_column(),
351
},
352
DataType::Date => s.date().unwrap().month_start(None)?.into_column(),
353
dt => polars_bail!(opq = month_start, got = dt, expected = "date/datetime"),
354
})
355
}
356
357
#[cfg(feature = "month_end")]
358
pub(super) fn month_end(s: &Column) -> PolarsResult<Column> {
359
Ok(match s.dtype() {
360
DataType::Datetime(_, tz) => match tz {
361
#[cfg(feature = "timezones")]
362
Some(tz) => s
363
.datetime()
364
.unwrap()
365
.month_end(tz.parse::<Tz>().ok().as_ref())?
366
.into_column(),
367
_ => s.datetime().unwrap().month_end(None)?.into_column(),
368
},
369
DataType::Date => s.date().unwrap().month_end(None)?.into_column(),
370
dt => polars_bail!(opq = month_end, got = dt, expected = "date/datetime"),
371
})
372
}
373
374
#[cfg(feature = "timezones")]
375
pub(super) fn base_utc_offset(s: &Column) -> PolarsResult<Column> {
376
match s.dtype() {
377
DataType::Datetime(time_unit, Some(tz)) => {
378
let tz = tz
379
.parse::<Tz>()
380
.expect("Time zone has already been validated");
381
Ok(polars_time::base_utc_offset(s.datetime().unwrap(), time_unit, &tz).into_column())
382
},
383
dt => polars_bail!(
384
opq = base_utc_offset,
385
got = dt,
386
expected = "time-zone-aware datetime"
387
),
388
}
389
}
390
#[cfg(feature = "timezones")]
391
pub(super) fn dst_offset(s: &Column) -> PolarsResult<Column> {
392
match s.dtype() {
393
DataType::Datetime(time_unit, Some(tz)) => {
394
let tz = tz
395
.parse::<Tz>()
396
.expect("Time zone has already been validated");
397
Ok(polars_time::dst_offset(s.datetime().unwrap(), time_unit, &tz).into_column())
398
},
399
dt => polars_bail!(
400
opq = dst_offset,
401
got = dt,
402
expected = "time-zone-aware datetime"
403
),
404
}
405
}
406
407
pub(super) fn round(s: &[Column]) -> PolarsResult<Column> {
408
let time_series = &s[0];
409
let every = s[1].str()?;
410
411
Ok(match time_series.dtype() {
412
DataType::Datetime(_, tz) => match tz {
413
#[cfg(feature = "timezones")]
414
Some(tz) => time_series
415
.datetime()
416
.unwrap()
417
.round(every, tz.parse::<Tz>().ok().as_ref())?
418
.into_column(),
419
_ => time_series
420
.datetime()
421
.unwrap()
422
.round(every, None)?
423
.into_column(),
424
},
425
DataType::Date => time_series
426
.date()
427
.unwrap()
428
.round(every, None)?
429
.into_column(),
430
dt => polars_bail!(opq = round, got = dt, expected = "date/datetime"),
431
})
432
}
433
434
pub(super) fn replace(s: &[Column]) -> PolarsResult<Column> {
435
let time_series = &s[0];
436
let s_year = &s[1].strict_cast(&DataType::Int32)?;
437
let s_month = &s[2].strict_cast(&DataType::Int8)?;
438
let s_day = &s[3].strict_cast(&DataType::Int8)?;
439
let year = s_year.i32()?;
440
let month = s_month.i8()?;
441
let day = s_day.i8()?;
442
443
match time_series.dtype() {
444
DataType::Datetime(_, _) => {
445
let s_hour = &s[4].strict_cast(&DataType::Int8)?;
446
let s_minute = &s[5].strict_cast(&DataType::Int8)?;
447
let s_second = &s[6].strict_cast(&DataType::Int8)?;
448
let s_microsecond = &s[7].strict_cast(&DataType::Int32)?;
449
let hour = s_hour.i8()?;
450
let minute = s_minute.i8()?;
451
let second = s_second.i8()?;
452
let nanosecond = &(s_microsecond.i32()? * 1_000);
453
let s_ambiguous = &s[8].strict_cast(&DataType::String)?;
454
let ambiguous = s_ambiguous.str()?;
455
456
let out = replace_datetime(
457
time_series.datetime().unwrap(),
458
year,
459
month,
460
day,
461
hour,
462
minute,
463
second,
464
nanosecond,
465
ambiguous,
466
);
467
out.map(|s| s.into_column())
468
},
469
DataType::Date => {
470
let out = polars_time::replace_date(time_series.date().unwrap(), year, month, day);
471
out.map(|s| s.into_column())
472
},
473
dt => polars_bail!(opq = round, got = dt, expected = "date/datetime"),
474
}
475
}
476
477