polars_core/chunked_array/temporal/
mod.rs

1//! Traits and utilities for temporal data.
2pub mod conversion;
3#[cfg(feature = "dtype-date")]
4mod date;
5#[cfg(feature = "dtype-datetime")]
6mod datetime;
7#[cfg(feature = "dtype-duration")]
8mod duration;
9#[cfg(feature = "dtype-time")]
10mod time;
11#[cfg(feature = "timezones")]
12use std::sync::LazyLock;
13
14#[cfg(feature = "dtype-date")]
15use chrono::NaiveDate;
16use chrono::NaiveDateTime;
17#[cfg(any(feature = "dtype-time", feature = "dtype-date"))]
18use chrono::NaiveTime;
19#[cfg(feature = "timezones")]
20use chrono_tz::Tz;
21#[cfg(feature = "timezones")]
22use polars_utils::pl_str::PlSmallStr;
23#[cfg(all(feature = "regex", feature = "timezones"))]
24use regex::Regex;
25#[cfg(feature = "dtype-time")]
26pub use time::time_to_time64ns;
27
28pub use self::conversion::*;
29#[cfg(feature = "timezones")]
30use crate::prelude::{PolarsResult, polars_bail};
31
32#[cfg(feature = "timezones")]
33static FIXED_OFFSET_PATTERN: &str = r#"(?x)
34    ^
35    (?P<sign>[-+])?            # optional sign
36    (?P<hour>0[0-9]|1[0-4])    # hour (between 0 and 14)
37    :?                         # optional separator
38    00                         # minute
39    $
40    "#;
41#[cfg(feature = "timezones")]
42static FIXED_OFFSET_RE: LazyLock<Regex> =
43    LazyLock::new(|| Regex::new(FIXED_OFFSET_PATTERN).unwrap());
44
45#[cfg(feature = "timezones")]
46pub fn validate_time_zone(tz: &str) -> PolarsResult<()> {
47    match tz.parse::<Tz>() {
48        Ok(_) => Ok(()),
49        Err(_) => {
50            polars_bail!(ComputeError: "unable to parse time zone: '{}'. Please check the Time Zone Database for a list of available time zones", tz)
51        },
52    }
53}
54
55#[cfg(feature = "timezones")]
56pub fn parse_time_zone(tz: &str) -> PolarsResult<Tz> {
57    match tz.parse::<Tz>() {
58        Ok(tz) => Ok(tz),
59        Err(_) => {
60            polars_bail!(ComputeError: "unable to parse time zone: '{}'. Please check the Time Zone Database for a list of available time zones", tz)
61        },
62    }
63}
64
65/// Convert fixed offset to Etc/GMT one from time zone database
66///
67/// E.g. +01:00 -> Etc/GMT-1
68///
69/// Note: the sign appears reversed, but is correct, see <https://en.wikipedia.org/wiki/Tz_database#Area>:
70/// > In order to conform with the POSIX style, those zone names beginning with
71/// > "Etc/GMT" have their sign reversed from the standard ISO 8601 convention.
72/// > In the "Etc" area, zones west of GMT have a positive sign and those east
73/// > have a negative sign in their name (e.g "Etc/GMT-14" is 14 hours ahead of GMT).
74#[cfg(feature = "timezones")]
75pub fn parse_fixed_offset(tz: &str) -> PolarsResult<PlSmallStr> {
76    use polars_utils::format_pl_smallstr;
77
78    if let Some(caps) = FIXED_OFFSET_RE.captures(tz) {
79        let sign = match caps.name("sign").map(|s| s.as_str()) {
80            Some("-") => "+",
81            _ => "-",
82        };
83        let hour = caps.name("hour").unwrap().as_str().parse::<i32>().unwrap();
84        let etc_tz = format_pl_smallstr!("Etc/GMT{}{}", sign, hour);
85        if etc_tz.parse::<Tz>().is_ok() {
86            return Ok(etc_tz);
87        }
88    }
89    polars_bail!(ComputeError: "unable to parse time zone: '{}'. Please check the Time Zone Database for a list of available time zones", tz)
90}