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