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!() }
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}