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