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
108
109
110
111
|
use log::trace;
use std::time::{Duration, SystemTime};
use time::{
format_description::well_known::{Iso8601, Rfc2822},
format_description::FormatItem,
macros::format_description,
parsing::Parsable,
OffsetDateTime, PrimitiveDateTime,
};
#[derive(Debug, thiserror::Error)]
pub enum TimeError {
#[error("failed to parse date: {0:?}")]
UnknownTimestamp(String),
}
pub fn parse_timestamp(timestamp: &str) -> Result<SystemTime, TimeError> {
trace!("parsing timestamp {:?}", timestamp);
let odt = parse(timestamp)?;
let unix = odt.unix_timestamp();
trace!("unix: {}", unix);
let sys = system_time(unix);
trace!("system time: {:?}", sys);
Ok(system_time(unix))
}
fn system_time(unix: i64) -> SystemTime {
let offset = Duration::from_secs(unix as u64);
SystemTime::UNIX_EPOCH.checked_add(offset).unwrap()
}
fn parse(timestamp: &str) -> Result<OffsetDateTime, TimeError> {
const SIMPLIFIED_ISO9601: &[FormatItem<'static>] =
format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
const SIMPLIFIED_ISO9601_MIN: &[FormatItem<'static>] =
format_description!("[year]-[month]-[day] [hour]:[minute]");
const SIMPLIFIED_ISO9601_TZ: &[FormatItem<'static>] = format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour][offset_minute]"
);
const SIMPLIFIED_ISO9601_MIN_TZ: &[FormatItem<'static>] =
format_description!("[year]-[month]-[day] [hour]:[minute] [offset_hour][offset_minute]");
if let Ok(t) = parse_one_time_format(timestamp, "simplified", SIMPLIFIED_ISO9601) {
Ok(t)
} else if let Ok(t) = parse_one_time_format(timestamp, "simplified-min", SIMPLIFIED_ISO9601_MIN)
{
Ok(t)
} else if let Ok(t) = parse_one_time_format(timestamp, "simplified-tz", SIMPLIFIED_ISO9601_TZ) {
Ok(t)
} else if let Ok(t) =
parse_one_time_format(timestamp, "simplified-tz", SIMPLIFIED_ISO9601_MIN_TZ)
{
Ok(t)
} else if let Ok(t) = parse_one_time_format(timestamp, "ISO8601", &Iso8601::PARSING) {
Ok(t)
} else if let Ok(t) = parse_one_time_format(timestamp, "RFC2822", &Rfc2822) {
Ok(t)
} else {
Err(TimeError::UnknownTimestamp(timestamp.into()))
}
}
fn parse_one_time_format(
timestamp: &str,
what: &str,
fmt: &(impl Parsable + ?Sized),
) -> Result<OffsetDateTime, time::error::Parse> {
trace!("trying to parse using: {}", what);
let r = PrimitiveDateTime::parse(timestamp, fmt);
if let Ok(t) = r {
Ok(t.assume_utc())
} else {
let e = r.err().unwrap();
trace!("error: {}", e);
Err(e)
}
}
#[cfg(test)]
mod test {
use super::parse;
fn unix_time(t: &str) -> i64 {
parse(t).unwrap().unix_timestamp()
}
#[test]
fn simple() {
assert_eq!(unix_time("1970-01-01 00:00:00"), 0);
}
#[test]
fn simple_with_minutes_only() {
assert_eq!(unix_time("1970-01-01 00:00"), 0);
}
#[test]
fn simple_with_tz() {
assert_eq!(unix_time("1970-01-01 00:00:00 +0000"), 0);
}
#[test]
fn iso9601() {
assert_eq!(unix_time("1970-01-01T00:00:00+00:00"), 0);
}
#[test]
fn rfc2822() {
assert_eq!(unix_time("Thu, 01 Jan 1970 00:00:00 +0000"), 0);
}
}
|