ogcapi_types/common/
datetime.rs1use std::str::FromStr;
2use std::{cmp::Ordering, fmt};
3
4use chrono::{DateTime, SecondsFormat, Utc};
5use serde::{Deserialize, Serialize};
6
7#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
8pub enum Datetime {
9 Datetime(DateTime<Utc>),
10 Interval {
11 from: IntervalDatetime,
12 to: IntervalDatetime,
13 },
14}
15
16#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
17pub enum IntervalDatetime {
18 Datetime(DateTime<Utc>),
19 Open,
20}
21
22impl FromStr for IntervalDatetime {
23 type Err = chrono::ParseError;
24
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
26 Ok(match s.trim() {
27 ".." | "" => IntervalDatetime::Open,
28 d => IntervalDatetime::Datetime(DateTime::parse_from_rfc3339(d)?.into()),
29 })
30 }
31}
32
33impl fmt::Display for IntervalDatetime {
34 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35 match self {
36 IntervalDatetime::Datetime(d) => {
37 write!(f, "{}", d.to_rfc3339_opts(SecondsFormat::Secs, true))
38 }
39 IntervalDatetime::Open => write!(f, ".."),
40 }
41 }
42}
43
44impl fmt::Display for Datetime {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 match self {
47 Datetime::Datetime(datetime) => {
48 write!(f, "{}", datetime.to_rfc3339_opts(SecondsFormat::Secs, true))
49 }
50 Datetime::Interval { from, to } => write!(f, "{}/{}", from, to),
51 }
52 }
53}
54
55impl FromStr for Datetime {
56 type Err = chrono::ParseError;
57
58 fn from_str(s: &str) -> Result<Self, Self::Err> {
59 if s.contains('/') && !["../..", "../", "/..", "/"].contains(&s.trim()) {
60 let mut datetimes = s.trim().splitn(2, '/');
61
62 let from = datetimes.next().unwrap_or_default().parse()?;
63 let to = datetimes.next().unwrap_or_default().parse()?;
64
65 Ok(Datetime::Interval { from, to })
66 } else {
67 Ok(Datetime::Datetime(DateTime::parse_from_rfc3339(s)?.into()))
68 }
69 }
70}
71
72impl PartialOrd for IntervalDatetime {
73 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
74 match self {
75 IntervalDatetime::Datetime(d) => match other {
76 IntervalDatetime::Datetime(o) => d.partial_cmp(o),
77 IntervalDatetime::Open => Some(Ordering::Less),
78 },
79 IntervalDatetime::Open => Some(Ordering::Less),
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::Datetime;
87 use std::str::FromStr;
88
89 #[test]
90 fn parse_datetime() {
91 let datetime_str = "2018-02-12T23:20:52Z";
92 let datetime = Datetime::from_str(datetime_str).unwrap();
93 assert_eq!(format!("{:#}", datetime), datetime_str)
94 }
95
96 #[test]
97 fn parse_intervals() {
98 let interval_str = "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z";
99 let datetime = Datetime::from_str(interval_str).unwrap();
100 assert_eq!(format!("{:#}", datetime), interval_str);
101
102 let interval_str = "2018-02-12T00:00:00Z/..";
103 let datetime = Datetime::from_str(interval_str).unwrap();
104 assert_eq!(format!("{:#}", datetime), interval_str);
105
106 let interval_str = "../2018-03-18T12:31:12Z";
107 let datetime = Datetime::from_str(interval_str).unwrap();
108 assert_eq!(format!("{:#}", datetime), interval_str)
109 }
110}