Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-time/src/month_start.rs
8430 views
1
use arrow::legacy::time_zone::Tz;
2
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
3
use polars_core::prelude::*;
4
use polars_core::utils::arrow::temporal_conversions::{
5
MILLISECONDS, SECONDS_IN_DAY, timestamp_ms_to_datetime, timestamp_ns_to_datetime,
6
timestamp_us_to_datetime,
7
};
8
9
#[cfg(feature = "timezones")]
10
use crate::utils::{try_localize_datetime, unlocalize_datetime};
11
12
// roll backward to the first day of the month
13
pub(crate) fn roll_backward(
14
t: i64,
15
tz: Option<&Tz>,
16
timestamp_to_datetime: fn(i64) -> NaiveDateTime,
17
datetime_to_timestamp: fn(NaiveDateTime) -> i64,
18
) -> PolarsResult<i64> {
19
let ts = match tz {
20
#[cfg(feature = "timezones")]
21
Some(tz) => unlocalize_datetime(timestamp_to_datetime(t), tz),
22
_ => timestamp_to_datetime(t),
23
};
24
let date = NaiveDate::from_ymd_opt(ts.year(), ts.month(), 1).ok_or_else(|| {
25
polars_err!(
26
ComputeError: "Could not construct date {}-{}-1", ts.year(), ts.month()
27
)
28
})?;
29
let time = NaiveTime::from_hms_nano_opt(
30
ts.hour(),
31
ts.minute(),
32
ts.second(),
33
ts.and_utc().timestamp_subsec_nanos(),
34
)
35
.ok_or_else(|| {
36
polars_err!(
37
ComputeError:
38
"Could not construct time {}:{}:{}.{}",
39
ts.hour(),
40
ts.minute(),
41
ts.second(),
42
ts.and_utc().timestamp_subsec_nanos()
43
)
44
})?;
45
let ndt = NaiveDateTime::new(date, time);
46
let t = match tz {
47
#[cfg(feature = "timezones")]
48
Some(tz) => datetime_to_timestamp(
49
try_localize_datetime(ndt, tz, Ambiguous::Raise, NonExistent::Raise)?
50
.expect("we didn't use Ambiguous::Null or NonExistent::Null"),
51
),
52
_ => datetime_to_timestamp(ndt),
53
};
54
Ok(t)
55
}
56
57
pub trait PolarsMonthStart {
58
fn month_start(&self, time_zone: Option<&Tz>) -> PolarsResult<Self>
59
where
60
Self: Sized;
61
}
62
63
impl PolarsMonthStart for DatetimeChunked {
64
fn month_start(&self, tz: Option<&Tz>) -> PolarsResult<Self> {
65
let timestamp_to_datetime: fn(i64) -> NaiveDateTime;
66
let datetime_to_timestamp: fn(NaiveDateTime) -> i64;
67
match self.time_unit() {
68
TimeUnit::Nanoseconds => {
69
timestamp_to_datetime = timestamp_ns_to_datetime;
70
datetime_to_timestamp = datetime_to_timestamp_ns;
71
},
72
TimeUnit::Microseconds => {
73
timestamp_to_datetime = timestamp_us_to_datetime;
74
datetime_to_timestamp = datetime_to_timestamp_us;
75
},
76
TimeUnit::Milliseconds => {
77
timestamp_to_datetime = timestamp_ms_to_datetime;
78
datetime_to_timestamp = datetime_to_timestamp_ms;
79
},
80
};
81
Ok(self
82
.phys
83
.try_apply_nonnull_values_generic(|t| {
84
roll_backward(t, tz, timestamp_to_datetime, datetime_to_timestamp)
85
})?
86
.into_datetime(self.time_unit(), self.time_zone().clone()))
87
}
88
}
89
90
impl PolarsMonthStart for DateChunked {
91
fn month_start(&self, _tz: Option<&Tz>) -> PolarsResult<Self> {
92
const MSECS_IN_DAY: i64 = MILLISECONDS * SECONDS_IN_DAY;
93
let ret = self.phys.try_apply_nonnull_values_generic(|t| {
94
let bwd = roll_backward(
95
MSECS_IN_DAY * t as i64,
96
None,
97
timestamp_ms_to_datetime,
98
datetime_to_timestamp_ms,
99
)?;
100
PolarsResult::Ok((bwd / MSECS_IN_DAY) as i32)
101
})?;
102
Ok(ret.into_date())
103
}
104
}
105
106