-
-
Notifications
You must be signed in to change notification settings - Fork 60
/
Copy pathtimeinterval.rs
120 lines (100 loc) · 3.25 KB
/
timeinterval.rs
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
112
113
114
115
116
117
118
119
120
use std::fmt;
use serde::de::Error as DeserializeError;
use serde::de::Visitor;
use serde::{Deserialize, Deserializer};
use chrono::DateTime;
use chrono::Duration;
use chrono::Utc;
// TODO: Implement serialize
#[derive(Clone, Debug)]
pub struct TimeInterval {
start: DateTime<Utc>,
end: DateTime<Utc>,
}
#[derive(Debug)]
pub enum TimeIntervalError {
ParseError(),
}
impl TimeInterval {
pub fn new(start: DateTime<Utc>, end: DateTime<Utc>) -> TimeInterval {
TimeInterval { start, end }
}
pub fn new_from_string(period: &str) -> Result<TimeInterval, TimeIntervalError> {
let splits = period.split('/').collect::<Vec<&str>>();
if splits.len() != 2 {
return Err(TimeIntervalError::ParseError());
}
let start = match DateTime::parse_from_rfc3339(splits[0]) {
Ok(dt) => dt.with_timezone(&Utc),
Err(_e) => return Err(TimeIntervalError::ParseError()),
};
let end = match DateTime::parse_from_rfc3339(splits[1]) {
Ok(dt) => dt.with_timezone(&Utc),
Err(_e) => return Err(TimeIntervalError::ParseError()),
};
Ok(TimeInterval::new(start, end))
}
pub fn start(&self) -> &DateTime<Utc> {
&self.start
}
pub fn end(&self) -> &DateTime<Utc> {
&self.end
}
pub fn duration(&self) -> Duration {
self.end - self.start
}
}
impl fmt::Display for TimeInterval {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}/{}", self.start.to_rfc3339(), self.end.to_rfc3339())
}
}
struct TimeIntervalVisitor;
use serde::de::Unexpected;
impl<'de> Visitor<'de> for TimeIntervalVisitor {
type Value = TimeInterval;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an string in ISO timeinterval format (such as 2000-01-01T00:00:00+01:00/2001-02-02T01:01:01+01:00)")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: DeserializeError,
{
match TimeInterval::new_from_string(&value) {
Ok(ti) => Ok(ti),
Err(e) => {
warn!("{:?}", e);
Err(DeserializeError::invalid_value(
Unexpected::Str(value),
&self,
))
}
}
}
}
impl<'de> Deserialize<'de> for TimeInterval {
fn deserialize<D>(deserializer: D) -> Result<TimeInterval, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(TimeIntervalVisitor)
}
}
#[test]
fn test_timeinterval() {
use std::str::FromStr;
let start = DateTime::from_str("2000-01-01T00:00:00Z").unwrap();
let end = DateTime::from_str("2000-01-02T00:00:00Z").unwrap();
let period_str = "2000-01-01T00:00:00+00:00/2000-01-02T00:00:00+00:00";
let duration = end - start;
let tp = TimeInterval::new(start, end);
assert_eq!(tp.start(), &start);
assert_eq!(tp.end(), &end);
assert_eq!(tp.duration(), duration);
assert_eq!(tp.to_string(), period_str);
let tp = TimeInterval::new_from_string(period_str).unwrap();
assert_eq!(tp.start(), &start);
assert_eq!(tp.end(), &end);
assert_eq!(tp.duration(), duration);
assert_eq!(tp.to_string(), period_str);
}