1#[cfg(feature = "timezones")]
2use chrono_tz::Tz;
3#[cfg(feature = "timezones")]
4use polars_core::chunked_array::temporal::validate_time_zone;
5#[cfg(feature = "timezones")]
6use polars_time::base_utc_offset as base_utc_offset_fn;
7#[cfg(feature = "timezones")]
8use polars_time::dst_offset as dst_offset_fn;
9#[cfg(feature = "offset_by")]
10use polars_time::impl_offset_by;
11#[cfg(any(feature = "dtype-date", feature = "dtype-datetime"))]
12use polars_time::replace::{replace_date, replace_datetime};
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16use super::*;
17
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19#[derive(Clone, PartialEq, Debug, Eq, Hash)]
20pub enum TemporalFunction {
21 Millennium,
22 Century,
23 Year,
24 IsLeapYear,
25 IsoYear,
26 Quarter,
27 Month,
28 Week,
29 WeekDay,
30 Day,
31 OrdinalDay,
32 Time,
33 Date,
34 Datetime,
35 Duration(TimeUnit),
36 Hour,
37 Minute,
38 Second,
39 Millisecond,
40 Microsecond,
41 Nanosecond,
42 TotalDays,
43 TotalHours,
44 TotalMinutes,
45 TotalSeconds,
46 TotalMilliseconds,
47 TotalMicroseconds,
48 TotalNanoseconds,
49 ToString(String),
50 CastTimeUnit(TimeUnit),
51 WithTimeUnit(TimeUnit),
52 #[cfg(feature = "timezones")]
53 ConvertTimeZone(TimeZone),
54 TimeStamp(TimeUnit),
55 Truncate,
56 #[cfg(feature = "offset_by")]
57 OffsetBy,
58 #[cfg(feature = "month_start")]
59 MonthStart,
60 #[cfg(feature = "month_end")]
61 MonthEnd,
62 #[cfg(feature = "timezones")]
63 BaseUtcOffset,
64 #[cfg(feature = "timezones")]
65 DSTOffset,
66 Round,
67 Replace,
68 #[cfg(feature = "timezones")]
69 ReplaceTimeZone(Option<TimeZone>, NonExistent),
70 Combine(TimeUnit),
71 DatetimeFunction {
72 time_unit: TimeUnit,
73 time_zone: Option<TimeZone>,
74 },
75}
76
77impl TemporalFunction {
78 pub(super) fn get_field(&self, mapper: FieldsMapper) -> PolarsResult<Field> {
79 use TemporalFunction::*;
80 match self {
81 Millennium | Century => mapper.with_dtype(DataType::Int8),
82 Year | IsoYear => mapper.with_dtype(DataType::Int32),
83 OrdinalDay => mapper.with_dtype(DataType::Int16),
84 Month | Quarter | Week | WeekDay | Day | Hour | Minute | Second => {
85 mapper.with_dtype(DataType::Int8)
86 },
87 Millisecond | Microsecond | Nanosecond => mapper.with_dtype(DataType::Int32),
88 TotalDays | TotalHours | TotalMinutes | TotalSeconds | TotalMilliseconds
89 | TotalMicroseconds | TotalNanoseconds => mapper.with_dtype(DataType::Int64),
90 ToString(_) => mapper.with_dtype(DataType::String),
91 WithTimeUnit(_) => mapper.with_same_dtype(),
92 CastTimeUnit(tu) => mapper.try_map_dtype(|dt| match dt {
93 DataType::Duration(_) => Ok(DataType::Duration(*tu)),
94 DataType::Datetime(_, tz) => Ok(DataType::Datetime(*tu, tz.clone())),
95 dtype => polars_bail!(ComputeError: "expected duration or datetime, got {}", dtype),
96 }),
97 #[cfg(feature = "timezones")]
98 ConvertTimeZone(tz) => mapper.try_map_dtype(|dt| match dt {
99 DataType::Datetime(tu, _) => Ok(DataType::Datetime(*tu, Some(tz.clone()))),
100 dtype => polars_bail!(ComputeError: "expected Datetime, got {}", dtype),
101 }),
102 TimeStamp(_) => mapper.with_dtype(DataType::Int64),
103 IsLeapYear => mapper.with_dtype(DataType::Boolean),
104 Time => mapper.with_dtype(DataType::Time),
105 Duration(tu) => mapper.with_dtype(DataType::Duration(*tu)),
106 Date => mapper.with_dtype(DataType::Date),
107 Datetime => mapper.try_map_dtype(|dt| match dt {
108 DataType::Datetime(tu, _) => Ok(DataType::Datetime(*tu, None)),
109 dtype => polars_bail!(ComputeError: "expected Datetime, got {}", dtype),
110 }),
111 Truncate => mapper.with_same_dtype(),
112 #[cfg(feature = "offset_by")]
113 OffsetBy => mapper.with_same_dtype(),
114 #[cfg(feature = "month_start")]
115 MonthStart => mapper.with_same_dtype(),
116 #[cfg(feature = "month_end")]
117 MonthEnd => mapper.with_same_dtype(),
118 #[cfg(feature = "timezones")]
119 BaseUtcOffset => mapper.with_dtype(DataType::Duration(TimeUnit::Milliseconds)),
120 #[cfg(feature = "timezones")]
121 DSTOffset => mapper.with_dtype(DataType::Duration(TimeUnit::Milliseconds)),
122 Round => mapper.with_same_dtype(),
123 Replace => mapper.with_same_dtype(),
124 #[cfg(feature = "timezones")]
125 ReplaceTimeZone(tz, _non_existent) => mapper.map_datetime_dtype_timezone(tz.as_ref()),
126 DatetimeFunction {
127 time_unit,
128 time_zone,
129 } => Ok(Field::new(
130 PlSmallStr::from_static("datetime"),
131 DataType::Datetime(*time_unit, time_zone.clone()),
132 )),
133 Combine(tu) => mapper.try_map_dtype(|dt| match dt {
134 DataType::Datetime(_, tz) => Ok(DataType::Datetime(*tu, tz.clone())),
135 DataType::Date => Ok(DataType::Datetime(*tu, None)),
136 dtype => {
137 polars_bail!(ComputeError: "expected Date or Datetime, got {}", dtype)
138 },
139 }),
140 }
141 }
142
143 pub fn function_options(&self) -> FunctionOptions {
144 use TemporalFunction as T;
145 match self {
146 T::Millennium
147 | T::Century
148 | T::Year
149 | T::IsLeapYear
150 | T::IsoYear
151 | T::Quarter
152 | T::Month
153 | T::Week
154 | T::WeekDay
155 | T::Day
156 | T::OrdinalDay
157 | T::Time
158 | T::Date
159 | T::Datetime
160 | T::Hour
161 | T::Minute
162 | T::Second
163 | T::Millisecond
164 | T::Microsecond
165 | T::Nanosecond
166 | T::TotalDays
167 | T::TotalHours
168 | T::TotalMinutes
169 | T::TotalSeconds
170 | T::TotalMilliseconds
171 | T::TotalMicroseconds
172 | T::TotalNanoseconds
173 | T::ToString(_)
174 | T::TimeStamp(_)
175 | T::CastTimeUnit(_)
176 | T::WithTimeUnit(_) => FunctionOptions::elementwise(),
177 #[cfg(feature = "timezones")]
178 T::ConvertTimeZone(_) => FunctionOptions::elementwise(),
179 #[cfg(feature = "month_start")]
180 T::MonthStart => FunctionOptions::elementwise(),
181 #[cfg(feature = "month_end")]
182 T::MonthEnd => FunctionOptions::elementwise(),
183 #[cfg(feature = "timezones")]
184 T::BaseUtcOffset | T::DSTOffset => FunctionOptions::elementwise(),
185 T::Truncate => FunctionOptions::elementwise(),
186 #[cfg(feature = "offset_by")]
187 T::OffsetBy => FunctionOptions::elementwise(),
188 T::Round => FunctionOptions::elementwise(),
189 T::Replace => FunctionOptions::elementwise(),
190 T::Duration(_) => FunctionOptions::elementwise(),
191 #[cfg(feature = "timezones")]
192 T::ReplaceTimeZone(_, _) => FunctionOptions::elementwise(),
193 T::Combine(_) => FunctionOptions::elementwise(),
194 T::DatetimeFunction { .. } => {
195 FunctionOptions::elementwise().with_flags(|f| f | FunctionFlags::ALLOW_RENAME)
196 },
197 }
198 }
199}
200
201impl Display for TemporalFunction {
202 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
203 use TemporalFunction::*;
204 let s = match self {
205 Millennium => "millennium",
206 Century => "century",
207 Year => "year",
208 IsLeapYear => "is_leap_year",
209 IsoYear => "iso_year",
210 Quarter => "quarter",
211 Month => "month",
212 Week => "week",
213 WeekDay => "weekday",
214 Day => "day",
215 OrdinalDay => "ordinal_day",
216 Time => "time",
217 Date => "date",
218 Datetime => "datetime",
219 Duration(_) => "duration",
220 Hour => "hour",
221 Minute => "minute",
222 Second => "second",
223 Millisecond => "millisecond",
224 Microsecond => "microsecond",
225 Nanosecond => "nanosecond",
226 TotalDays => "total_days",
227 TotalHours => "total_hours",
228 TotalMinutes => "total_minutes",
229 TotalSeconds => "total_seconds",
230 TotalMilliseconds => "total_milliseconds",
231 TotalMicroseconds => "total_microseconds",
232 TotalNanoseconds => "total_nanoseconds",
233 ToString(_) => "to_string",
234 #[cfg(feature = "timezones")]
235 ConvertTimeZone(_) => "convert_time_zone",
236 CastTimeUnit(_) => "cast_time_unit",
237 WithTimeUnit(_) => "with_time_unit",
238 TimeStamp(tu) => return write!(f, "dt.timestamp({tu})"),
239 Truncate => "truncate",
240 #[cfg(feature = "offset_by")]
241 OffsetBy => "offset_by",
242 #[cfg(feature = "month_start")]
243 MonthStart => "month_start",
244 #[cfg(feature = "month_end")]
245 MonthEnd => "month_end",
246 #[cfg(feature = "timezones")]
247 BaseUtcOffset => "base_utc_offset",
248 #[cfg(feature = "timezones")]
249 DSTOffset => "dst_offset",
250 Round => "round",
251 Replace => "replace",
252 #[cfg(feature = "timezones")]
253 ReplaceTimeZone(_, _) => "replace_time_zone",
254 DatetimeFunction { .. } => return write!(f, "dt.datetime"),
255 Combine(_) => "combine",
256 };
257 write!(f, "dt.{s}")
258 }
259}
260
261pub(super) fn millennium(s: &Column) -> PolarsResult<Column> {
262 s.as_materialized_series()
263 .millennium()
264 .map(|ca| ca.into_column())
265}
266pub(super) fn century(s: &Column) -> PolarsResult<Column> {
267 s.as_materialized_series()
268 .century()
269 .map(|ca| ca.into_column())
270}
271pub(super) fn year(s: &Column) -> PolarsResult<Column> {
272 s.as_materialized_series().year().map(|ca| ca.into_column())
273}
274pub(super) fn is_leap_year(s: &Column) -> PolarsResult<Column> {
275 s.as_materialized_series()
276 .is_leap_year()
277 .map(|ca| ca.into_column())
278}
279pub(super) fn iso_year(s: &Column) -> PolarsResult<Column> {
280 s.as_materialized_series()
281 .iso_year()
282 .map(|ca| ca.into_column())
283}
284pub(super) fn month(s: &Column) -> PolarsResult<Column> {
285 s.as_materialized_series()
286 .month()
287 .map(|ca| ca.into_column())
288}
289pub(super) fn quarter(s: &Column) -> PolarsResult<Column> {
290 s.as_materialized_series()
291 .quarter()
292 .map(|ca| ca.into_column())
293}
294pub(super) fn week(s: &Column) -> PolarsResult<Column> {
295 s.as_materialized_series().week().map(|ca| ca.into_column())
296}
297pub(super) fn weekday(s: &Column) -> PolarsResult<Column> {
298 s.as_materialized_series()
299 .weekday()
300 .map(|ca| ca.into_column())
301}
302pub(super) fn day(s: &Column) -> PolarsResult<Column> {
303 s.as_materialized_series().day().map(|ca| ca.into_column())
304}
305pub(super) fn ordinal_day(s: &Column) -> PolarsResult<Column> {
306 s.as_materialized_series()
307 .ordinal_day()
308 .map(|ca| ca.into_column())
309}
310pub(super) fn time(s: &Column) -> PolarsResult<Column> {
311 match s.dtype() {
312 #[cfg(feature = "timezones")]
313 DataType::Datetime(_, Some(_)) => polars_ops::prelude::replace_time_zone(
314 s.datetime().unwrap(),
315 None,
316 &StringChunked::from_iter(std::iter::once("raise")),
317 NonExistent::Raise,
318 )?
319 .cast(&DataType::Time)
320 .map(Column::from),
321 DataType::Datetime(_, _) => s
322 .datetime()
323 .unwrap()
324 .cast(&DataType::Time)
325 .map(Column::from),
326 DataType::Time => Ok(s.clone()),
327 dtype => polars_bail!(ComputeError: "expected Datetime or Time, got {}", dtype),
328 }
329}
330pub(super) fn date(s: &Column) -> PolarsResult<Column> {
331 match s.dtype() {
332 #[cfg(feature = "timezones")]
333 DataType::Datetime(_, Some(_)) => {
334 let mut out = {
335 polars_ops::chunked_array::replace_time_zone(
336 s.datetime().unwrap(),
337 None,
338 &StringChunked::from_iter(std::iter::once("raise")),
339 NonExistent::Raise,
340 )?
341 .cast(&DataType::Date)?
342 };
343 out.set_sorted_flag(s.is_sorted_flag());
349 Ok(out.into())
350 },
351 DataType::Datetime(_, _) => s
352 .datetime()
353 .unwrap()
354 .cast(&DataType::Date)
355 .map(Column::from),
356 DataType::Date => Ok(s.clone()),
357 dtype => polars_bail!(ComputeError: "expected Datetime or Date, got {}", dtype),
358 }
359}
360pub(super) fn datetime(s: &Column) -> PolarsResult<Column> {
361 match s.dtype() {
362 #[cfg(feature = "timezones")]
363 DataType::Datetime(tu, Some(_)) => polars_ops::chunked_array::replace_time_zone(
364 s.datetime().unwrap(),
365 None,
366 &StringChunked::from_iter(std::iter::once("raise")),
367 NonExistent::Raise,
368 )?
369 .cast(&DataType::Datetime(*tu, None))
370 .map(|x| x.into()),
371 DataType::Datetime(tu, _) => s
372 .datetime()
373 .unwrap()
374 .cast(&DataType::Datetime(*tu, None))
375 .map(Column::from),
376 dtype => polars_bail!(ComputeError: "expected Datetime, got {}", dtype),
377 }
378}
379pub(super) fn hour(s: &Column) -> PolarsResult<Column> {
380 s.as_materialized_series().hour().map(|ca| ca.into_column())
381}
382pub(super) fn minute(s: &Column) -> PolarsResult<Column> {
383 s.as_materialized_series()
384 .minute()
385 .map(|ca| ca.into_column())
386}
387pub(super) fn second(s: &Column) -> PolarsResult<Column> {
388 s.as_materialized_series()
389 .second()
390 .map(|ca| ca.into_column())
391}
392pub(super) fn millisecond(s: &Column) -> PolarsResult<Column> {
393 s.as_materialized_series()
394 .nanosecond()
395 .map(|ca| (ca.wrapping_trunc_div_scalar(1_000_000)).into_column())
396}
397pub(super) fn microsecond(s: &Column) -> PolarsResult<Column> {
398 s.as_materialized_series()
399 .nanosecond()
400 .map(|ca| (ca.wrapping_trunc_div_scalar(1_000)).into_column())
401}
402pub(super) fn nanosecond(s: &Column) -> PolarsResult<Column> {
403 s.as_materialized_series()
404 .nanosecond()
405 .map(|ca| ca.into_column())
406}
407#[cfg(feature = "dtype-duration")]
408pub(super) fn total_days(s: &Column) -> PolarsResult<Column> {
409 s.as_materialized_series()
410 .duration()
411 .map(|ca| ca.days().into_column())
412}
413#[cfg(feature = "dtype-duration")]
414pub(super) fn total_hours(s: &Column) -> PolarsResult<Column> {
415 s.as_materialized_series()
416 .duration()
417 .map(|ca| ca.hours().into_column())
418}
419#[cfg(feature = "dtype-duration")]
420pub(super) fn total_minutes(s: &Column) -> PolarsResult<Column> {
421 s.as_materialized_series()
422 .duration()
423 .map(|ca| ca.minutes().into_column())
424}
425#[cfg(feature = "dtype-duration")]
426pub(super) fn total_seconds(s: &Column) -> PolarsResult<Column> {
427 s.as_materialized_series()
428 .duration()
429 .map(|ca| ca.seconds().into_column())
430}
431#[cfg(feature = "dtype-duration")]
432pub(super) fn total_milliseconds(s: &Column) -> PolarsResult<Column> {
433 s.as_materialized_series()
434 .duration()
435 .map(|ca| ca.milliseconds().into_column())
436}
437#[cfg(feature = "dtype-duration")]
438pub(super) fn total_microseconds(s: &Column) -> PolarsResult<Column> {
439 s.as_materialized_series()
440 .duration()
441 .map(|ca| ca.microseconds().into_column())
442}
443#[cfg(feature = "dtype-duration")]
444pub(super) fn total_nanoseconds(s: &Column) -> PolarsResult<Column> {
445 s.as_materialized_series()
446 .duration()
447 .map(|ca| ca.nanoseconds().into_column())
448}
449pub(super) fn timestamp(s: &Column, tu: TimeUnit) -> PolarsResult<Column> {
450 s.as_materialized_series()
451 .timestamp(tu)
452 .map(|ca| ca.into_column())
453}
454pub(super) fn to_string(s: &Column, format: &str) -> PolarsResult<Column> {
455 TemporalMethods::to_string(s.as_materialized_series(), format).map(Column::from)
456}
457
458#[cfg(feature = "timezones")]
459pub(super) fn convert_time_zone(s: &Column, time_zone: &TimeZone) -> PolarsResult<Column> {
460 match s.dtype() {
461 DataType::Datetime(_, _) => {
462 let mut ca = s.datetime()?.clone();
463 validate_time_zone(time_zone)?;
464 ca.set_time_zone(time_zone.clone())?;
465 Ok(ca.into_column())
466 },
467 dtype => polars_bail!(ComputeError: "expected Datetime, got {}", dtype),
468 }
469}
470pub(super) fn with_time_unit(s: &Column, tu: TimeUnit) -> PolarsResult<Column> {
471 match s.dtype() {
472 DataType::Datetime(_, _) => {
473 let mut ca = s.datetime()?.clone();
474 ca.set_time_unit(tu);
475 Ok(ca.into_column())
476 },
477 #[cfg(feature = "dtype-duration")]
478 DataType::Duration(_) => {
479 let mut ca = s.as_materialized_series().duration()?.clone();
480 ca.set_time_unit(tu);
481 Ok(ca.into_column())
482 },
483 dt => polars_bail!(ComputeError: "dtype `{}` has no time unit", dt),
484 }
485}
486pub(super) fn cast_time_unit(s: &Column, tu: TimeUnit) -> PolarsResult<Column> {
487 match s.dtype() {
488 DataType::Datetime(_, _) => {
489 let ca = s.datetime()?;
490 Ok(ca.cast_time_unit(tu).into_column())
491 },
492 #[cfg(feature = "dtype-duration")]
493 DataType::Duration(_) => {
494 let ca = s.as_materialized_series().duration()?;
495 Ok(ca.cast_time_unit(tu).into_column())
496 },
497 dt => polars_bail!(ComputeError: "dtype `{}` has no time unit", dt),
498 }
499}
500
501pub(super) fn truncate(s: &[Column]) -> PolarsResult<Column> {
502 let time_series = &s[0];
503 let every = s[1].str()?;
504
505 let mut out = match time_series.dtype() {
506 DataType::Datetime(_, tz) => match tz {
507 #[cfg(feature = "timezones")]
508 Some(tz) => time_series
509 .datetime()?
510 .truncate(tz.parse::<Tz>().ok().as_ref(), every)?
511 .into_column(),
512 _ => time_series.datetime()?.truncate(None, every)?.into_column(),
513 },
514 DataType::Date => time_series.date()?.truncate(None, every)?.into_column(),
515 dt => polars_bail!(opq = round, got = dt, expected = "date/datetime"),
516 };
517 out.set_sorted_flag(time_series.is_sorted_flag());
518 Ok(out)
519}
520
521#[cfg(feature = "offset_by")]
522pub(super) fn offset_by(s: &[Column]) -> PolarsResult<Column> {
523 impl_offset_by(s[0].as_materialized_series(), s[1].as_materialized_series()).map(Column::from)
524}
525
526#[cfg(feature = "month_start")]
527pub(super) fn month_start(s: &Column) -> PolarsResult<Column> {
528 Ok(match s.dtype() {
529 DataType::Datetime(_, tz) => match tz {
530 #[cfg(feature = "timezones")]
531 Some(tz) => s
532 .datetime()
533 .unwrap()
534 .month_start(tz.parse::<Tz>().ok().as_ref())?
535 .into_column(),
536 _ => s.datetime().unwrap().month_start(None)?.into_column(),
537 },
538 DataType::Date => s.date().unwrap().month_start(None)?.into_column(),
539 dt => polars_bail!(opq = month_start, got = dt, expected = "date/datetime"),
540 })
541}
542
543#[cfg(feature = "month_end")]
544pub(super) fn month_end(s: &Column) -> PolarsResult<Column> {
545 Ok(match s.dtype() {
546 DataType::Datetime(_, tz) => match tz {
547 #[cfg(feature = "timezones")]
548 Some(tz) => s
549 .datetime()
550 .unwrap()
551 .month_end(tz.parse::<Tz>().ok().as_ref())?
552 .into_column(),
553 _ => s.datetime().unwrap().month_end(None)?.into_column(),
554 },
555 DataType::Date => s.date().unwrap().month_end(None)?.into_column(),
556 dt => polars_bail!(opq = month_end, got = dt, expected = "date/datetime"),
557 })
558}
559
560#[cfg(feature = "timezones")]
561pub(super) fn base_utc_offset(s: &Column) -> PolarsResult<Column> {
562 match s.dtype() {
563 DataType::Datetime(time_unit, Some(tz)) => {
564 let tz = tz
565 .parse::<Tz>()
566 .expect("Time zone has already been validated");
567 Ok(base_utc_offset_fn(s.datetime().unwrap(), time_unit, &tz).into_column())
568 },
569 dt => polars_bail!(
570 opq = base_utc_offset,
571 got = dt,
572 expected = "time-zone-aware datetime"
573 ),
574 }
575}
576#[cfg(feature = "timezones")]
577pub(super) fn dst_offset(s: &Column) -> PolarsResult<Column> {
578 match s.dtype() {
579 DataType::Datetime(time_unit, Some(tz)) => {
580 let tz = tz
581 .parse::<Tz>()
582 .expect("Time zone has already been validated");
583 Ok(dst_offset_fn(s.datetime().unwrap(), time_unit, &tz).into_column())
584 },
585 dt => polars_bail!(
586 opq = dst_offset,
587 got = dt,
588 expected = "time-zone-aware datetime"
589 ),
590 }
591}
592
593pub(super) fn round(s: &[Column]) -> PolarsResult<Column> {
594 let time_series = &s[0];
595 let every = s[1].str()?;
596
597 Ok(match time_series.dtype() {
598 DataType::Datetime(_, tz) => match tz {
599 #[cfg(feature = "timezones")]
600 Some(tz) => time_series
601 .datetime()
602 .unwrap()
603 .round(every, tz.parse::<Tz>().ok().as_ref())?
604 .into_column(),
605 _ => time_series
606 .datetime()
607 .unwrap()
608 .round(every, None)?
609 .into_column(),
610 },
611 DataType::Date => time_series
612 .date()
613 .unwrap()
614 .round(every, None)?
615 .into_column(),
616 dt => polars_bail!(opq = round, got = dt, expected = "date/datetime"),
617 })
618}
619
620pub(super) fn replace(s: &[Column]) -> PolarsResult<Column> {
621 let time_series = &s[0];
622 let s_year = &s[1].strict_cast(&DataType::Int32)?;
623 let s_month = &s[2].strict_cast(&DataType::Int8)?;
624 let s_day = &s[3].strict_cast(&DataType::Int8)?;
625 let year = s_year.i32()?;
626 let month = s_month.i8()?;
627 let day = s_day.i8()?;
628
629 match time_series.dtype() {
630 DataType::Datetime(_, _) => {
631 let s_hour = &s[4].strict_cast(&DataType::Int8)?;
632 let s_minute = &s[5].strict_cast(&DataType::Int8)?;
633 let s_second = &s[6].strict_cast(&DataType::Int8)?;
634 let s_microsecond = &s[7].strict_cast(&DataType::Int32)?;
635 let hour = s_hour.i8()?;
636 let minute = s_minute.i8()?;
637 let second = s_second.i8()?;
638 let nanosecond = &(s_microsecond.i32()? * 1_000);
639 let s_ambiguous = &s[8].strict_cast(&DataType::String)?;
640 let ambiguous = s_ambiguous.str()?;
641
642 let out = replace_datetime(
643 time_series.datetime().unwrap(),
644 year,
645 month,
646 day,
647 hour,
648 minute,
649 second,
650 nanosecond,
651 ambiguous,
652 );
653 out.map(|s| s.into_column())
654 },
655 DataType::Date => {
656 let out = replace_date(time_series.date().unwrap(), year, month, day);
657 out.map(|s| s.into_column())
658 },
659 dt => polars_bail!(opq = round, got = dt, expected = "date/datetime"),
660 }
661}