Skip to main content

rustbasic_core/
chrono_tz.rs

1use std::str::FromStr;
2use crate::chrono::{FixedOffset, TimeZone, Offset};
3
4#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
5pub enum Tz {
6    Utc,
7    Fixed(FixedOffset),
8}
9
10pub const UTC: Tz = Tz::Utc;
11
12impl FromStr for Tz {
13    type Err = &'static str;
14
15    fn from_str(s: &str) -> Result<Self, Self::Err> {
16        let s = s.trim();
17        if s.eq_ignore_ascii_case("utc") || s.eq_ignore_ascii_case("gmt") || s.is_empty() {
18            return Ok(Tz::Utc);
19        }
20        if let Some(offset) = parse_offset_string(s) {
21            return Ok(Tz::Fixed(offset));
22        }
23        match s {
24            "Asia/Jakarta" | "WIB" => Ok(Tz::Fixed(FixedOffset::east_opt(7 * 3600).unwrap())),
25            "Asia/Singapore" | "Asia/Kuala_Lumpur" | "Asia/Makassar" | "WITA" => {
26                Ok(Tz::Fixed(FixedOffset::east_opt(8 * 3600).unwrap()))
27            }
28            "Asia/Jayapura" | "WIT" => Ok(Tz::Fixed(FixedOffset::east_opt(9 * 3600).unwrap())),
29            "Asia/Bangkok" => Ok(Tz::Fixed(FixedOffset::east_opt(7 * 3600).unwrap())),
30            "Asia/Tokyo" | "Asia/Seoul" => Ok(Tz::Fixed(FixedOffset::east_opt(9 * 3600).unwrap())),
31            "America/New_York" | "EST" => Ok(Tz::Fixed(FixedOffset::west_opt(5 * 3600).unwrap())),
32            "America/Chicago" | "CST" => Ok(Tz::Fixed(FixedOffset::west_opt(6 * 3600).unwrap())),
33            "America/Denver" | "MST" => Ok(Tz::Fixed(FixedOffset::west_opt(7 * 3600).unwrap())),
34            "America/Los_Angeles" | "PST" => Ok(Tz::Fixed(FixedOffset::west_opt(8 * 3600).unwrap())),
35            "Europe/London" | "GMT" => Ok(Tz::Fixed(FixedOffset::east_opt(0).unwrap())),
36            _ => {
37                Ok(Tz::Fixed(FixedOffset::east_opt(7 * 3600).unwrap())) // Default to Jakarta/WIB
38            }
39        }
40    }
41}
42
43fn parse_offset_string(s: &str) -> Option<FixedOffset> {
44    let s = s.trim();
45    if s.is_empty() { return None; }
46    let sign = match s.chars().next()? {
47        '+' => 1,
48        '-' => -1,
49        _ => return None,
50    };
51    let rest = &s[1..];
52    let parts: Vec<&str> = rest.split(':').collect();
53    if parts.len() == 2 {
54        let hours: i32 = parts[0].parse().ok()?;
55        let minutes: i32 = parts[1].parse().ok()?;
56        FixedOffset::east_opt(sign * (hours * 3600 + minutes * 60))
57    } else if parts.len() == 1 {
58        if rest.len() == 4 {
59            let hours: i32 = rest[0..2].parse().ok()?;
60            let minutes: i32 = rest[2..4].parse().ok()?;
61            FixedOffset::east_opt(sign * (hours * 3600 + minutes * 60))
62        } else if rest.len() == 2 || rest.len() == 1 {
63            let hours: i32 = rest.parse().ok()?;
64            FixedOffset::east_opt(sign * hours * 3600)
65        } else {
66            None
67        }
68    } else {
69        None
70    }
71}
72
73impl TimeZone for Tz {
74    type Offset = TzOffset;
75
76    fn from_offset(offset: &Self::Offset) -> Self {
77        match offset {
78            TzOffset::Utc => Tz::Utc,
79            TzOffset::Fixed(fo) => Tz::Fixed(*fo),
80        }
81    }
82
83    fn offset_from_local_date(&self, _local: &crate::chrono::NaiveDate) -> crate::chrono::LocalResult<Self::Offset> {
84        match self {
85            Tz::Utc => crate::chrono::LocalResult::Single(TzOffset::Utc),
86            Tz::Fixed(fo) => crate::chrono::LocalResult::Single(TzOffset::Fixed(*fo)),
87        }
88    }
89
90    fn offset_from_local_datetime(&self, _local: &crate::chrono::NaiveDateTime) -> crate::chrono::LocalResult<Self::Offset> {
91        match self {
92            Tz::Utc => crate::chrono::LocalResult::Single(TzOffset::Utc),
93            Tz::Fixed(fo) => crate::chrono::LocalResult::Single(TzOffset::Fixed(*fo)),
94        }
95    }
96
97    fn offset_from_utc_date(&self, _utc: &crate::chrono::NaiveDate) -> Self::Offset {
98        match self {
99            Tz::Utc => TzOffset::Utc,
100            Tz::Fixed(fo) => TzOffset::Fixed(*fo),
101        }
102    }
103
104    fn offset_from_utc_datetime(&self, _utc: &crate::chrono::NaiveDateTime) -> Self::Offset {
105        match self {
106            Tz::Utc => TzOffset::Utc,
107            Tz::Fixed(fo) => TzOffset::Fixed(*fo),
108        }
109    }
110}
111
112#[derive(Copy, Clone, Debug, PartialEq, Eq)]
113pub enum TzOffset {
114    Utc,
115    Fixed(FixedOffset),
116}
117
118impl Offset for TzOffset {
119    fn fix(&self) -> FixedOffset {
120        match self {
121            TzOffset::Utc => FixedOffset::east_opt(0).unwrap(),
122            TzOffset::Fixed(fo) => *fo,
123        }
124    }
125}
126
127impl std::fmt::Display for TzOffset {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        write!(f, "{}", self.fix())
130    }
131}