Path: blob/main/crates/polars-python/src/conversion/datetime.rs
7889 views
//! Utilities for converting dates, times, datetimes, and so on.12use std::str::FromStr;34use chrono::{DateTime, Datelike, FixedOffset, NaiveDateTime, NaiveTime, TimeDelta, TimeZone as _};5use chrono_tz::Tz;6use polars::datatypes::TimeUnit;7use polars_core::datatypes::TimeZone;8use pyo3::types::PyAnyMethods;9use pyo3::{Bound, IntoPyObjectExt, PyAny, PyResult, Python, intern};1011use crate::error::PyPolarsErr;12use crate::py_modules::pl_utils;1314pub fn elapsed_offset_to_timedelta(elapsed: i64, time_unit: TimeUnit) -> TimeDelta {15let (in_second, nano_multiplier) = match time_unit {16TimeUnit::Nanoseconds => (1_000_000_000, 1),17TimeUnit::Microseconds => (1_000_000, 1_000),18TimeUnit::Milliseconds => (1_000, 1_000_000),19};20let mut elapsed_sec = elapsed / in_second;21let mut elapsed_nanos = nano_multiplier * (elapsed % in_second);22if elapsed_nanos < 0 {23// TimeDelta expects nanos to always be positive.24elapsed_sec -= 1;25elapsed_nanos += 1_000_000_000;26}27TimeDelta::new(elapsed_sec, elapsed_nanos as u32).unwrap()28}2930/// Convert time-units-since-epoch to a more structured object.31pub fn timestamp_to_naive_datetime(since_epoch: i64, time_unit: TimeUnit) -> NaiveDateTime {32DateTime::UNIX_EPOCH.naive_utc() + elapsed_offset_to_timedelta(since_epoch, time_unit)33}3435/// Convert nanoseconds-since-midnight to a more structured object.36pub fn nanos_since_midnight_to_naivetime(nanos_since_midnight: i64) -> NaiveTime {37NaiveTime::from_hms_opt(0, 0, 0).unwrap()38+ elapsed_offset_to_timedelta(nanos_since_midnight, TimeUnit::Nanoseconds)39}4041pub fn datetime_to_py_object<'py>(42py: Python<'py>,43v: i64,44tu: TimeUnit,45tz: Option<&TimeZone>,46) -> PyResult<Bound<'py, PyAny>> {47if let Some(time_zone) = tz {48if let Ok(tz) = Tz::from_str(time_zone) {49let utc_datetime = DateTime::UNIX_EPOCH + elapsed_offset_to_timedelta(v, tu);50if utc_datetime.year() >= 2100 {51// chrono-tz does not support dates after 210052// https://github.com/chronotope/chrono-tz/issues/13553pl_utils(py)54.bind(py)55.getattr(intern!(py, "to_py_datetime"))?56.call1((v, tu.to_ascii(), time_zone.as_str()))57} else {58let datetime = utc_datetime.with_timezone(&tz);59datetime.into_bound_py_any(py)60}61} else if let Ok(tz) = FixedOffset::from_str(time_zone) {62let naive_datetime = timestamp_to_naive_datetime(v, tu);63let datetime = tz.from_utc_datetime(&naive_datetime);64datetime.into_bound_py_any(py)65} else {66Err(PyPolarsErr::Other(format!("Could not parse timezone: {time_zone}")).into())67}68} else {69timestamp_to_naive_datetime(v, tu).into_bound_py_any(py)70}71}727374