Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-time/src/date_range.rs
6939 views
1
use arrow::legacy::time_zone::Tz;
2
use chrono::{Datelike, NaiveDateTime, NaiveTime};
3
use polars_core::chunked_array::temporal::time_to_time64ns;
4
use polars_core::prelude::*;
5
use polars_core::series::IsSorted;
6
7
use crate::prelude::*;
8
9
pub fn in_nanoseconds_window(ndt: &NaiveDateTime) -> bool {
10
// ~584 year around 1970
11
!(ndt.year() > 2554 || ndt.year() < 1386)
12
}
13
14
/// Create a [`DatetimeChunked`] from a given `start` and `end` date and a given `interval`.
15
pub fn date_range(
16
name: PlSmallStr,
17
start: NaiveDateTime,
18
end: NaiveDateTime,
19
interval: Duration,
20
closed: ClosedWindow,
21
tu: TimeUnit,
22
tz: Option<&Tz>,
23
) -> PolarsResult<DatetimeChunked> {
24
let (start, end) = match tu {
25
TimeUnit::Nanoseconds => (
26
start.and_utc().timestamp_nanos_opt().unwrap(),
27
end.and_utc().timestamp_nanos_opt().unwrap(),
28
),
29
TimeUnit::Microseconds => (
30
start.and_utc().timestamp_micros(),
31
end.and_utc().timestamp_micros(),
32
),
33
TimeUnit::Milliseconds => (
34
start.and_utc().timestamp_millis(),
35
end.and_utc().timestamp_millis(),
36
),
37
};
38
datetime_range_impl(name, start, end, interval, closed, tu, tz)
39
}
40
41
#[doc(hidden)]
42
pub fn datetime_range_impl(
43
name: PlSmallStr,
44
start: i64,
45
end: i64,
46
interval: Duration,
47
closed: ClosedWindow,
48
tu: TimeUnit,
49
tz: Option<&Tz>,
50
) -> PolarsResult<DatetimeChunked> {
51
let out = Int64Chunked::new_vec(
52
name,
53
datetime_range_i64(start, end, interval, closed, tu, tz)?,
54
);
55
let mut out = match tz {
56
#[cfg(feature = "timezones")]
57
Some(tz) => out.into_datetime(tu, Some(TimeZone::from_chrono(tz))),
58
_ => out.into_datetime(tu, None),
59
};
60
61
out.physical_mut().set_sorted_flag(IsSorted::Ascending);
62
Ok(out)
63
}
64
65
/// Create a [`TimeChunked`] from a given `start` and `end` date and a given `interval`.
66
pub fn time_range(
67
name: PlSmallStr,
68
start: NaiveTime,
69
end: NaiveTime,
70
interval: Duration,
71
closed: ClosedWindow,
72
) -> PolarsResult<TimeChunked> {
73
let start = time_to_time64ns(&start);
74
let end = time_to_time64ns(&end);
75
time_range_impl(name, start, end, interval, closed)
76
}
77
78
#[doc(hidden)]
79
pub fn time_range_impl(
80
name: PlSmallStr,
81
start: i64,
82
end: i64,
83
interval: Duration,
84
closed: ClosedWindow,
85
) -> PolarsResult<TimeChunked> {
86
let mut out = Int64Chunked::new_vec(
87
name,
88
datetime_range_i64(start, end, interval, closed, TimeUnit::Nanoseconds, None)?,
89
)
90
.into_time();
91
92
out.physical_mut().set_sorted_flag(IsSorted::Ascending);
93
Ok(out)
94
}
95
96
/// vector of i64 representing temporal values
97
pub(crate) fn datetime_range_i64(
98
start: i64,
99
end: i64,
100
interval: Duration,
101
closed: ClosedWindow,
102
time_unit: TimeUnit,
103
time_zone: Option<&Tz>,
104
) -> PolarsResult<Vec<i64>> {
105
if start > end {
106
return Ok(Vec::new());
107
}
108
polars_ensure!(
109
!interval.negative && !interval.is_zero(),
110
ComputeError: "`interval` must be positive"
111
);
112
113
let duration = match time_unit {
114
TimeUnit::Nanoseconds => interval.duration_ns(),
115
TimeUnit::Microseconds => interval.duration_us(),
116
TimeUnit::Milliseconds => interval.duration_ms(),
117
};
118
let time_zone_opt: Option<TimeZone> = match time_zone {
119
#[cfg(feature = "timezones")]
120
Some(tz) => Some(TimeZone::from_chrono(tz)),
121
_ => None,
122
};
123
124
if interval.is_constant_duration(time_zone_opt.as_ref()) {
125
// Fast path!
126
let step: usize = duration.try_into().map_err(
127
|_err| polars_err!(ComputeError: "Could not convert {:?} to usize", duration),
128
)?;
129
polars_ensure!(
130
step != 0,
131
InvalidOperation: "interval {} is too small for time unit {} and got rounded down to zero",
132
interval,
133
time_unit,
134
);
135
return match closed {
136
ClosedWindow::Both => Ok((start..=end).step_by(step).collect::<Vec<i64>>()),
137
ClosedWindow::None => Ok((start + duration..end).step_by(step).collect::<Vec<i64>>()),
138
ClosedWindow::Left => Ok((start..end).step_by(step).collect::<Vec<i64>>()),
139
ClosedWindow::Right => Ok((start + duration..=end).step_by(step).collect::<Vec<i64>>()),
140
};
141
}
142
143
let size = ((end - start) / duration + 1) as usize;
144
let offset_fn = match time_unit {
145
TimeUnit::Nanoseconds => Duration::add_ns,
146
TimeUnit::Microseconds => Duration::add_us,
147
TimeUnit::Milliseconds => Duration::add_ms,
148
};
149
let mut ts = Vec::with_capacity(size);
150
let mut i = match closed {
151
ClosedWindow::Both | ClosedWindow::Left => 0,
152
ClosedWindow::Right | ClosedWindow::None => 1,
153
};
154
let mut t = offset_fn(&(interval * i), start, time_zone)?;
155
i += 1;
156
match closed {
157
ClosedWindow::Both | ClosedWindow::Right => {
158
while t <= end {
159
ts.push(t);
160
t = offset_fn(&(interval * i), start, time_zone)?;
161
i += 1;
162
}
163
},
164
ClosedWindow::Left | ClosedWindow::None => {
165
while t < end {
166
ts.push(t);
167
t = offset_fn(&(interval * i), start, time_zone)?;
168
i += 1;
169
}
170
},
171
}
172
debug_assert!(size >= ts.len());
173
Ok(ts)
174
}
175
176