1use crate::prelude::*;
6
7use parking_lot::{const_rwlock, RwLock};
8
9#[derive(SerializeDisplay)]
10#[derive(DeserializeFromStr)]
11#[derive(Clone,Debug)]
12pub struct Timezone(Arc<TzInfo>);
13
14#[derive(Clone,Debug,Default)]
15struct TzInfo {
16 name: String,
17 ctz: Option<chrono_tz::Tz>,
18}
19
20impl Timezone {
21 pub fn name(&self) -> &str {
22 &self.0.name
23 }
24
25 #[throws(as Option)]
28 fn format_tz<'r, TZ: chrono::TimeZone>(
29 tz: &TZ, ts: Timestamp, fmt: &'r str
30 ) -> chrono::format::DelayedFormat<chrono::format::StrftimeItems<'r>>
31 where <TZ as chrono::TimeZone>::Offset: Display
32 {
33 use chrono::DateTime;
34 let dt = tz.timestamp_opt(ts.0.try_into().ok()?, 0).single()?;
35 DateTime::format(&dt, fmt)
36 }
37
38 pub fn format(&self, ts: Timestamp) -> String {
39 match (||{
40 let fmt = "%Y-%m-%d %H:%M:%S %z";
41 let df = match &self.0.ctz {
42 Some(ctz) => Timezone::format_tz(ctz, ts, fmt),
43 None => Timezone::format_tz(&chrono::offset::Utc, ts, fmt),
44 }
45 .ok_or_else(
46 || format!("timestamp {} out of range!", &ts.0)
47 )?;
48 Ok(format!("{}", df))
49 })() {
50 Ok(s) => s,
51 Err(s) => s,
52 }
53 }
54
55 pub fn default_todo() -> Self {
56 Timezone::from_str("").unwrap()
57 }
58}
59
60impl Display for Timezone {
61 #[throws(fmt::Error)]
62 fn fmt(&self, f: &mut Formatter) {
63 write!(f, "{}", &self.0.name)?;
64 }
65}
66
67type MemoTable = Option<HashMap<String, Timezone>>;
68static MEMO: RwLock<MemoTable> = const_rwlock(None);
69
70impl FromStr for Timezone {
71 type Err = Void;
72 #[throws(Void)]
73 fn from_str(name: &str) -> Self {
74 if name.is_empty() { return default() }
75
76 let get = |memor: &MemoTable| memor.as_ref()?.get(name).map(Clone::clone);
77 if let Some(got) = get(&MEMO.read()) { return got }
78
79 let mut memow = MEMO.write();
81 if let Some(got) = get(&memow) { return got }
82
83 let out = {
85 let name = name.to_string();
86 match chrono_tz::Tz::from_str(&name) {
87 Ok(ctz) => {
88 Arc::new(TzInfo { name, ctz: Some(ctz) })
89 },
90 Err(emsg) => {
91 error!("Error loading timezone {:?}: {}, using UTC", name, emsg);
92 Arc::new(TzInfo { name, ctz: None })
93 }
94 }
95 };
96 let out = Timezone(out);
97 memow.get_or_insert_with(default)
98 .insert(name.to_string(), out.clone());
99 out
100 }
101}
102
103impl Default for Timezone {
104 fn default() -> Self {
105 Timezone(Arc::new(TzInfo { ctz: None, name: default() }))
106 }
107}