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