polars_python/conversion/
datetime.rs1use std::str::FromStr;
4
5use chrono::{DateTime, Datelike, FixedOffset, NaiveDateTime, NaiveTime, TimeDelta, TimeZone as _};
6use chrono_tz::Tz;
7use polars::datatypes::TimeUnit;
8use polars_core::datatypes::TimeZone;
9use pyo3::types::PyAnyMethods;
10use pyo3::{Bound, IntoPyObjectExt, PyAny, PyResult, Python, intern};
11
12use crate::error::PyPolarsErr;
13use crate::py_modules::pl_utils;
14
15pub fn elapsed_offset_to_timedelta(elapsed: i64, time_unit: TimeUnit) -> TimeDelta {
16 let (in_second, nano_multiplier) = match time_unit {
17 TimeUnit::Nanoseconds => (1_000_000_000, 1),
18 TimeUnit::Microseconds => (1_000_000, 1_000),
19 TimeUnit::Milliseconds => (1_000, 1_000_000),
20 };
21 let mut elapsed_sec = elapsed / in_second;
22 let mut elapsed_nanos = nano_multiplier * (elapsed % in_second);
23 if elapsed_nanos < 0 {
24 elapsed_sec -= 1;
26 elapsed_nanos += 1_000_000_000;
27 }
28 TimeDelta::new(elapsed_sec, elapsed_nanos as u32).unwrap()
29}
30
31pub fn timestamp_to_naive_datetime(since_epoch: i64, time_unit: TimeUnit) -> NaiveDateTime {
33 DateTime::UNIX_EPOCH.naive_utc() + elapsed_offset_to_timedelta(since_epoch, time_unit)
34}
35
36pub fn nanos_since_midnight_to_naivetime(nanos_since_midnight: i64) -> NaiveTime {
38 NaiveTime::from_hms_opt(0, 0, 0).unwrap()
39 + elapsed_offset_to_timedelta(nanos_since_midnight, TimeUnit::Nanoseconds)
40}
41
42pub fn datetime_to_py_object<'py>(
43 py: Python<'py>,
44 v: i64,
45 tu: TimeUnit,
46 tz: Option<&TimeZone>,
47) -> PyResult<Bound<'py, PyAny>> {
48 if let Some(time_zone) = tz {
49 if let Ok(tz) = Tz::from_str(time_zone) {
50 let utc_datetime = DateTime::UNIX_EPOCH + elapsed_offset_to_timedelta(v, tu);
51 if utc_datetime.year() >= 2100 {
52 pl_utils(py)
55 .bind(py)
56 .getattr(intern!(py, "to_py_datetime"))?
57 .call1((v, tu.to_ascii(), time_zone.as_str()))
58 } else {
59 let datetime = utc_datetime.with_timezone(&tz);
60 datetime.into_bound_py_any(py)
61 }
62 } else if let Ok(tz) = FixedOffset::from_str(time_zone) {
63 let naive_datetime = timestamp_to_naive_datetime(v, tu);
64 let datetime = tz.from_utc_datetime(&naive_datetime);
65 datetime.into_bound_py_any(py)
66 } else {
67 Err(PyPolarsErr::Other(format!("Could not parse timezone: {time_zone}")).into())
68 }
69 } else {
70 timestamp_to_naive_datetime(v, tu).into_bound_py_any(py)
71 }
72}