Path: blob/main/crates/polars-arrow/src/legacy/kernels/time.rs
6939 views
use std::str::FromStr;12#[cfg(feature = "timezones")]3use chrono::{LocalResult, NaiveDateTime, TimeZone};4#[cfg(feature = "timezones")]5use chrono_tz::Tz;6#[cfg(feature = "timezones")]7use polars_error::PolarsResult;8use polars_error::{PolarsError, polars_bail};9#[cfg(feature = "serde")]10use serde::{Deserialize, Serialize};11use strum_macros::IntoStaticStr;1213pub enum Ambiguous {14Earliest,15Latest,16Null,17Raise,18}19impl FromStr for Ambiguous {20type Err = PolarsError;2122fn from_str(s: &str) -> Result<Self, Self::Err> {23match s {24"earliest" => Ok(Ambiguous::Earliest),25"latest" => Ok(Ambiguous::Latest),26"raise" => Ok(Ambiguous::Raise),27"null" => Ok(Ambiguous::Null),28s => polars_bail!(InvalidOperation:29"Invalid argument {}, expected one of: \"earliest\", \"latest\", \"null\", \"raise\"", s30),31}32}33}3435#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, IntoStaticStr)]36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]37#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]38#[strum(serialize_all = "snake_case")]39pub enum NonExistent {40Null,41Raise,42}4344#[cfg(feature = "timezones")]45pub fn convert_to_naive_local(46from_tz: &Tz,47to_tz: &Tz,48ndt: NaiveDateTime,49ambiguous: Ambiguous,50non_existent: NonExistent,51) -> PolarsResult<Option<NaiveDateTime>> {52let ndt = from_tz.from_utc_datetime(&ndt).naive_local();53match to_tz.from_local_datetime(&ndt) {54LocalResult::Single(dt) => Ok(Some(dt.naive_utc())),55LocalResult::Ambiguous(dt_earliest, dt_latest) => match ambiguous {56Ambiguous::Earliest => Ok(Some(dt_earliest.naive_utc())),57Ambiguous::Latest => Ok(Some(dt_latest.naive_utc())),58Ambiguous::Null => Ok(None),59Ambiguous::Raise => {60polars_bail!(ComputeError: "datetime '{}' is ambiguous in time zone '{}'. Please use `ambiguous` to tell how it should be localized.", ndt, to_tz)61},62},63LocalResult::None => match non_existent {64NonExistent::Raise => polars_bail!(ComputeError:65"datetime '{}' is non-existent in time zone '{}'. You may be able to use `non_existent='null'` to return `null` in this case.",66ndt, to_tz67),68NonExistent::Null => Ok(None),69},70}71}7273/// Same as convert_to_naive_local, but return `None` instead74/// raising - in some cases this can be used to save a string allocation.75#[cfg(feature = "timezones")]76pub fn convert_to_naive_local_opt(77from_tz: &Tz,78to_tz: &Tz,79ndt: NaiveDateTime,80ambiguous: Ambiguous,81) -> Option<Option<NaiveDateTime>> {82let ndt = from_tz.from_utc_datetime(&ndt).naive_local();83match to_tz.from_local_datetime(&ndt) {84LocalResult::Single(dt) => Some(Some(dt.naive_utc())),85LocalResult::Ambiguous(dt_earliest, dt_latest) => match ambiguous {86Ambiguous::Earliest => Some(Some(dt_earliest.naive_utc())),87Ambiguous::Latest => Some(Some(dt_latest.naive_utc())),88Ambiguous::Null => Some(None),89Ambiguous::Raise => None,90},91LocalResult::None => None,92}93}949596