timespan/
date_time_span.rs1use crate::Error;
20use crate::Formatable;
21use crate::NaiveDateTimeSpan;
22use crate::Parsable;
23use crate::Span;
24use crate::Spanable;
25use chrono::format::{DelayedFormat, StrftimeItems};
26use chrono::offset::{FixedOffset, Local, Utc};
27use chrono::{DateTime as ChronoDateTime, Duration, TimeZone};
28use std;
29
30impl<T: TimeZone + std::marker::Copy> Spanable for ChronoDateTime<T>
31where
32    <T as TimeZone>::Offset: std::marker::Copy,
33{
34    #[inline]
35    fn signed_duration_since(self, other: Self) -> Duration {
36        ChronoDateTime::signed_duration_since(self, other)
37    }
38}
39
40impl<T: TimeZone> Formatable for ChronoDateTime<T>
41where
42    <T as TimeZone>::Offset: std::fmt::Display,
43{
44    #[inline]
45    fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
46        ChronoDateTime::format(self, fmt)
47    }
48}
49
50impl Parsable for ChronoDateTime<Local> {
51    #[inline]
52    fn parse_from_str(s: &str, fmt: &str) -> Result<ChronoDateTime<Local>, Error> {
53        Local
54            .datetime_from_str(s, fmt)
55            .map_err(|e| Error::Parsing(e))
56    }
57}
58
59impl Parsable for ChronoDateTime<Utc> {
60    #[inline]
61    fn parse_from_str(s: &str, fmt: &str) -> Result<ChronoDateTime<Utc>, Error> {
62        Utc.datetime_from_str(s, fmt).map_err(|e| Error::Parsing(e))
63    }
64}
65
66impl Parsable for ChronoDateTime<FixedOffset> {
67    #[inline]
68    fn parse_from_str(s: &str, fmt: &str) -> Result<ChronoDateTime<FixedOffset>, Error> {
69        ChronoDateTime::parse_from_str(s, fmt).map_err(|e| Error::Parsing(e))
70    }
71}
72
73pub type DateTimeSpan<T> = Span<ChronoDateTime<T>>;
100
101impl<T: TimeZone> DateTimeSpan<T> {
102    pub fn from_local_datetimespan(span: &NaiveDateTimeSpan, tz: &T) -> Result<Self, Error> {
110        Ok(DateTimeSpan {
111            start: tz
112                .from_local_datetime(&span.start)
113                .single()
114                .ok_or(Error::LocalAmbigious)?,
115            end: tz
116                .from_local_datetime(&span.end)
117                .single()
118                .ok_or(Error::LocalAmbigious)?,
119        })
120    }
121
122    pub fn from_utc_datetimespan(span: &NaiveDateTimeSpan, tz: &T) -> Self {
140        DateTimeSpan {
141            start: tz.from_utc_datetime(&span.start),
142            end: tz.from_utc_datetime(&span.end),
143        }
144    }
145}
146
147#[cfg(feature = "with-chrono-tz")]
148pub use self::with_chrono_tz::DateTime;
149
150#[cfg(feature = "with-chrono-tz")]
151mod with_chrono_tz {
152    use super::DateTimeSpan;
153    use super::Error;
154    use super::Parsable;
155    use chrono::{DateTime as ChronoDateTime, ParseError, TimeZone};
156    use chrono_tz::Tz;
157    use regex::Regex;
158    use std::convert::From;
159    use std::str::FromStr;
160
161    pub struct DateTime<T: TimeZone>(pub ChronoDateTime<T>);
162
163    impl<T: TimeZone> From<ChronoDateTime<T>> for DateTime<T> {
164        fn from(dt: ChronoDateTime<T>) -> DateTime<T> {
165            DateTime(dt)
166        }
167    }
168
169    impl FromStr for DateTime<Tz> {
170        type Err = ParseError;
171
172        #[inline]
173        fn from_str(s: &str) -> Result<Self, ParseError> {
174            let re = Regex::new(r"(.*)\s+(\w+)$").unwrap();
178            let caps = re.captures(s).unwrap();
179
180            let c1 = caps.get(1).map(|m| m.as_str()).unwrap();
181            let c2 = caps.get(2).map(|m| m.as_str()).unwrap();
182
183            let tz = c2.parse::<Tz>().unwrap();
184            Tz::datetime_from_str(&tz, &c1, "%F %T").map(|dt| DateTime(dt))
185        }
186    }
187
188    impl Parsable for DateTime<Tz> {
189        #[inline]
190        fn parse_from_str(s: &str, fmt: &str) -> Result<DateTime<Tz>, Error> {
191            let re = Regex::new(r"(.*)\s+(\w+)$").unwrap();
192            let caps = re.captures(s).ok_or(Error::BadFormat)?;
193
194            let c1 = caps.get(1).map(|m| m.as_str()).ok_or(Error::BadFormat)?;
195            let c2 = caps.get(2).map(|m| m.as_str()).ok_or(Error::BadFormat)?;
196
197            let tz = c2.parse::<Tz>().map_err(|_| Error::BadFormat)?;
198            Tz::datetime_from_str(&tz, &c1, fmt)
199                .map(|dt| DateTime(dt))
200                .map_err(|e| Error::Parsing(e))
201        }
202    }
203
204    impl FromStr for DateTimeSpan<Tz> {
206        type Err = Error;
207
208        fn from_str(s: &str) -> Result<Self, Self::Err> {
209            let re = Regex::new(r"(.*)\s+-\s+(.*)").unwrap();
210            let caps = re.captures(s).ok_or(Error::Empty)?;
211
212            let c1 = caps.get(1).ok_or(Error::NoStart)?;
213            let c2 = caps.get(2).ok_or(Error::NoEnd)?;
214
215            DateTimeSpan::new(
216                DateTime::from_str(c1.as_str()).map(|dt| dt.0)?,
217                DateTime::from_str(c2.as_str()).map(|dt| dt.0)?,
218            )
219        }
220    }
221}