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
6939 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: format!("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
format!(
39
"Could not construct time {}:{}:{}.{}",
40
ts.hour(),
41
ts.minute(),
42
ts.second(),
43
ts.and_utc().timestamp_subsec_nanos()
44
)
45
)
46
})?;
47
let ndt = NaiveDateTime::new(date, time);
48
let t = match tz {
49
#[cfg(feature = "timezones")]
50
Some(tz) => datetime_to_timestamp(
51
try_localize_datetime(ndt, tz, Ambiguous::Raise, NonExistent::Raise)?
52
.expect("we didn't use Ambiguous::Null or NonExistent::Null"),
53
),
54
_ => datetime_to_timestamp(ndt),
55
};
56
Ok(t)
57
}
58
59
pub trait PolarsMonthStart {
60
fn month_start(&self, time_zone: Option<&Tz>) -> PolarsResult<Self>
61
where
62
Self: Sized;
63
}
64
65
impl PolarsMonthStart for DatetimeChunked {
66
fn month_start(&self, tz: Option<&Tz>) -> PolarsResult<Self> {
67
let timestamp_to_datetime: fn(i64) -> NaiveDateTime;
68
let datetime_to_timestamp: fn(NaiveDateTime) -> i64;
69
match self.time_unit() {
70
TimeUnit::Nanoseconds => {
71
timestamp_to_datetime = timestamp_ns_to_datetime;
72
datetime_to_timestamp = datetime_to_timestamp_ns;
73
},
74
TimeUnit::Microseconds => {
75
timestamp_to_datetime = timestamp_us_to_datetime;
76
datetime_to_timestamp = datetime_to_timestamp_us;
77
},
78
TimeUnit::Milliseconds => {
79
timestamp_to_datetime = timestamp_ms_to_datetime;
80
datetime_to_timestamp = datetime_to_timestamp_ms;
81
},
82
};
83
Ok(self
84
.phys
85
.try_apply_nonnull_values_generic(|t| {
86
roll_backward(t, tz, timestamp_to_datetime, datetime_to_timestamp)
87
})?
88
.into_datetime(self.time_unit(), self.time_zone().clone()))
89
}
90
}
91
92
impl PolarsMonthStart for DateChunked {
93
fn month_start(&self, _tz: Option<&Tz>) -> PolarsResult<Self> {
94
const MSECS_IN_DAY: i64 = MILLISECONDS * SECONDS_IN_DAY;
95
let ret = self.phys.try_apply_nonnull_values_generic(|t| {
96
let bwd = roll_backward(
97
MSECS_IN_DAY * t as i64,
98
None,
99
timestamp_ms_to_datetime,
100
datetime_to_timestamp_ms,
101
)?;
102
PolarsResult::Ok((bwd / MSECS_IN_DAY) as i32)
103
})?;
104
Ok(ret.into_date())
105
}
106
}
107
108