datetime/cal/
parse.rs

1use std::error::Error as ErrorTrait;
2use std::fmt;
3use std::str::FromStr;
4
5use iso8601;
6
7use cal::datetime::{LocalDate, LocalTime, LocalDateTime, Month, Weekday, Error as DateTimeError};
8use cal::offset::{Offset, OffsetDateTime, Error as OffsetError};
9
10
11impl FromStr for LocalDate {
12    type Err = Error<DateTimeError>;
13
14    fn from_str(input: &str) -> Result<Self, Self::Err> {
15        match iso8601::date(input) {
16            Ok(fields)  => fields_to_date(fields).map_err(Error::Date),
17            Err(e)      => Err(Error::Parse(e)),
18        }
19    }
20}
21
22impl FromStr for LocalTime {
23    type Err = Error<DateTimeError>;
24
25    fn from_str(input: &str) -> Result<Self, Self::Err> {
26        match iso8601::time(input) {
27            Ok(fields)  => fields_to_time(fields).map_err(Error::Date),
28            Err(e)      => Err(Error::Parse(e)),
29        }
30    }
31}
32
33impl FromStr for LocalDateTime {
34    type Err = Error<DateTimeError>;
35
36    fn from_str(input: &str) -> Result<Self, Self::Err> {
37        let fields = match iso8601::datetime(input) {
38            Ok(fields)  => fields,
39            Err(e)      => return Err(Error::Parse(e)),
40        };
41
42        let date = fields_to_date(fields.date).map_err(Error::Date)?;
43        let time = fields_to_time(fields.time).map_err(Error::Date)?;
44        Ok(Self::new(date, time))
45    }
46}
47
48impl FromStr for OffsetDateTime {
49    type Err = Error<OffsetError>;
50
51    fn from_str(input: &str) -> Result<Self, Self::Err> {
52        let fields = match iso8601::datetime(input) {
53            Ok(fields)  => fields,
54            Err(e)      => return Err(Error::Parse(e)),
55        };
56
57        let date   = fields_to_date(fields.date).map_err(|e| Error::Date(OffsetError::Date(e)))?;
58        let time   = fields_to_time(fields.time).map_err(|e| Error::Date(OffsetError::Date(e)))?;
59        let offset = Offset::of_hours_and_minutes(fields.time.tz_offset_hours as i8, fields.time.tz_offset_minutes as i8).map_err(Error::Date)?;
60        Ok(offset.transform_date(LocalDateTime::new(date, time)))
61    }
62}
63
64
65fn fields_to_date(fields: iso8601::Date) -> Result<LocalDate, DateTimeError> {
66    if let iso8601::Date::YMD { year, month, day } = fields {
67        let month_variant = Month::from_one(month as i8)?;
68        LocalDate::ymd(year as i64, month_variant, day as i8)
69    }
70    else if let iso8601::Date::Week { year, ww, d } = fields {
71        let weekday_variant = Weekday::from_one(d as i8)?;
72        LocalDate::ywd(year as i64, ww as i64, weekday_variant)
73    }
74    else if let iso8601::Date::Ordinal { year, ddd } = fields {
75        LocalDate::yd(year as i64, ddd as i64)
76    }
77    else {
78        unreachable!()  // should be unnecessary??
79    }
80}
81
82fn fields_to_time(fields: iso8601::Time) -> Result<LocalTime, DateTimeError> {
83    let h  = fields.hour as i8;
84    let m  = fields.minute as i8;
85    let s  = fields.second as i8;
86    let ms = fields.millisecond as i16;
87
88    LocalTime::hms_ms(h, m, s, ms)
89}
90
91
92#[derive(PartialEq, Debug, Clone)]
93pub enum Error<E: ErrorTrait> {
94    Date(E),
95    Parse(String),
96}
97
98impl<E: ErrorTrait> fmt::Display for Error<E> {
99    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100        match *self {
101            Error::Date(ref error)    => write!(f, "parsing resulted in an invalid date: {}", error),
102            Error::Parse(ref string)  => write!(f, "parse error: {}", string),
103        }
104    }
105}
106
107impl<E: ErrorTrait> ErrorTrait for Error<E> {
108    fn cause(&self) -> Option<&dyn ErrorTrait> {
109        match *self {
110            Error::Date(ref error)  => Some(error),
111            Error::Parse(_)         => None,
112        }
113    }
114}