Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-time/src/offset_by.rs
6939 views
1
use arrow::legacy::time_zone::Tz;
2
use polars_core::prelude::arity::broadcast_try_binary_elementwise;
3
use polars_core::prelude::*;
4
use polars_core::series::IsSorted;
5
6
use crate::Duration;
7
8
fn apply_offsets_to_datetime(
9
datetime: &Logical<DatetimeType, Int64Type>,
10
offsets: &StringChunked,
11
time_zone: Option<&Tz>,
12
) -> PolarsResult<Int64Chunked> {
13
match offsets.len() {
14
1 => match offsets.get(0) {
15
Some(offset) => {
16
let offset = &Duration::parse(offset);
17
if offset.is_constant_duration(datetime.time_zone().as_ref()) {
18
// fastpath!
19
let mut duration = match datetime.time_unit() {
20
TimeUnit::Milliseconds => offset.duration_ms(),
21
TimeUnit::Microseconds => offset.duration_us(),
22
TimeUnit::Nanoseconds => offset.duration_ns(),
23
};
24
if offset.negative() {
25
duration = -duration;
26
}
27
Ok(datetime.phys.clone().wrapping_add_scalar(duration))
28
} else {
29
let offset_fn = match datetime.time_unit() {
30
TimeUnit::Milliseconds => Duration::add_ms,
31
TimeUnit::Microseconds => Duration::add_us,
32
TimeUnit::Nanoseconds => Duration::add_ns,
33
};
34
datetime
35
.phys
36
.try_apply_nonnull_values_generic(|v| offset_fn(offset, v, time_zone))
37
}
38
},
39
_ => Ok(datetime.phys.apply(|_| None)),
40
},
41
_ => {
42
let offset_fn = match datetime.time_unit() {
43
TimeUnit::Milliseconds => Duration::add_ms,
44
TimeUnit::Microseconds => Duration::add_us,
45
TimeUnit::Nanoseconds => Duration::add_ns,
46
};
47
broadcast_try_binary_elementwise(
48
datetime.physical(),
49
offsets,
50
|timestamp_opt, offset_opt| match (timestamp_opt, offset_opt) {
51
(Some(timestamp), Some(offset)) => {
52
offset_fn(&Duration::try_parse(offset)?, timestamp, time_zone).map(Some)
53
},
54
_ => Ok(None),
55
},
56
)
57
},
58
}
59
}
60
61
pub fn impl_offset_by(ts: &Series, offsets: &Series) -> PolarsResult<Series> {
62
let offsets = offsets.str()?;
63
64
polars_ensure!(
65
ts.len() == offsets.len() || offsets.len() == 1 || ts.len() == 1,
66
length_mismatch = "dt.offset_by",
67
ts.len(),
68
offsets.len()
69
);
70
71
let dtype = ts.dtype();
72
73
// Sortedness may not be preserved for non-constant durations,
74
// see https://github.com/pola-rs/polars/issues/19608 for a counterexample.
75
// Constant durations (e.g. 2 hours) always preserve sortedness.
76
let tz = match dtype {
77
DataType::Date => None,
78
DataType::Datetime(_, tz) => tz.clone(),
79
_ => polars_bail!(InvalidOperation: "expected Date or Datetime, got {}", dtype),
80
};
81
let preserve_sortedness = match offsets.len() {
82
1 => match offsets.get(0) {
83
Some(offset) => {
84
let offset = Duration::try_parse(offset)?;
85
offset.is_constant_duration(tz.as_ref())
86
},
87
None => false,
88
},
89
_ => false,
90
};
91
92
let out = match dtype {
93
DataType::Date => {
94
let ts = ts
95
.cast(&DataType::Datetime(TimeUnit::Microseconds, None))
96
.unwrap();
97
let datetime = ts.datetime().unwrap();
98
let out = apply_offsets_to_datetime(datetime, offsets, None)?;
99
out.cast(&DataType::Datetime(TimeUnit::Microseconds, None))
100
.unwrap()
101
.cast(&DataType::Date)
102
},
103
DataType::Datetime(tu, tz) => {
104
let datetime = ts.datetime().unwrap();
105
106
let out = match tz {
107
#[cfg(feature = "timezones")]
108
Some(tz) => {
109
apply_offsets_to_datetime(datetime, offsets, tz.parse::<Tz>().ok().as_ref())?
110
},
111
_ => apply_offsets_to_datetime(datetime, offsets, None)?,
112
};
113
out.cast(&DataType::Datetime(*tu, tz.clone()))
114
},
115
dt => polars_bail!(
116
ComputeError: "cannot use 'offset_by' on Series of datatype {}", dt,
117
),
118
};
119
if preserve_sortedness {
120
out.map(|mut out| {
121
out.set_sorted_flag(ts.is_sorted_flag());
122
out
123
})
124
} else {
125
out.map(|mut out| {
126
out.set_sorted_flag(IsSorted::Not);
127
out
128
})
129
}
130
}
131
132