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
121
use std::error::Error as ErrorTrait;
use std::fmt;
use std::str::FromStr;

use iso8601;

use cal::datetime::{LocalDate, LocalTime, LocalDateTime, Month, Weekday, Error as DateTimeError};
use cal::offset::{Offset, OffsetDateTime, Error as OffsetError};


impl FromStr for LocalDate {
    type Err = Error<DateTimeError>;

    fn from_str(input: &str) -> Result<Self, Self::Err> {
        match iso8601::date(input) {
            Ok(fields)  => fields_to_date(fields).map_err(Error::Date),
            Err(e)      => Err(Error::Parse(e)),
        }
    }
}

impl FromStr for LocalTime {
    type Err = Error<DateTimeError>;

    fn from_str(input: &str) -> Result<LocalTime, Self::Err> {
        match iso8601::time(input) {
            Ok(fields)  => fields_to_time(fields).map_err(Error::Date),
            Err(e)      => Err(Error::Parse(e)),
        }
    }
}

impl FromStr for LocalDateTime {
    type Err = Error<DateTimeError>;

    fn from_str(input: &str) -> Result<LocalDateTime, Self::Err> {
        let fields = match iso8601::datetime(input) {
            Ok(fields)  => fields,
            Err(e)      => return Err(Error::Parse(e)),
        };

        let date = try!(fields_to_date(fields.date).map_err(Error::Date));
        let time = try!(fields_to_time(fields.time).map_err(Error::Date));
        Ok(LocalDateTime::new(date, time))
    }
}

impl FromStr for OffsetDateTime {
    type Err = Error<OffsetError>;

    fn from_str(input: &str) -> Result<OffsetDateTime, Self::Err> {
        let fields = match iso8601::datetime(input) {
            Ok(fields)  => fields,
            Err(e)      => return Err(Error::Parse(e)),
        };

        let date   = try!(fields_to_date(fields.date).map_err(|e| Error::Date(OffsetError::Date(e))));
        let time   = try!(fields_to_time(fields.time).map_err(|e| Error::Date(OffsetError::Date(e))));
        let offset = try!(Offset::of_hours_and_minutes(fields.time.tz_offset_hours as i8, fields.time.tz_offset_minutes as i8).map_err(Error::Date));
        Ok(offset.transform_date(LocalDateTime::new(date, time)))
    }
}


fn fields_to_date(fields: iso8601::Date) -> Result<LocalDate, DateTimeError> {
    if let iso8601::Date::YMD { year, month, day } = fields {
        let month_variant = try!(Month::from_one(month as i8));
        LocalDate::ymd(year as i64, month_variant, day as i8)
    }
    else if let iso8601::Date::Week { year, ww, d } = fields {
        let weekday_variant = try!(Weekday::from_one(d as i8));
        LocalDate::ywd(year as i64, ww as i64, weekday_variant)
    }
    else if let iso8601::Date::Ordinal { year, ddd } = fields {
        LocalDate::yd(year as i64, ddd as i64)
    }
    else {
        unreachable!()  // should be unnecessary??
    }
}

fn fields_to_time(fields: iso8601::Time) -> Result<LocalTime, DateTimeError> {
    let h  = fields.hour as i8;
    let m  = fields.minute as i8;
    let s  = fields.second as i8;
    let ms = fields.millisecond as i16;

    LocalTime::hms_ms(h, m, s, ms)
}


#[derive(PartialEq, Debug, Clone)]
pub enum Error<E: ErrorTrait> {
    Date(E),
    Parse(String),
}

impl<E: ErrorTrait> fmt::Display for Error<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Date(ref error)   => write!(f, "{}: {}", self.description(), error),
            Error::Parse(ref string) => write!(f, "{}: {}", self.description(), string),
        }
    }
}

impl<E: ErrorTrait> ErrorTrait for Error<E> {
    fn description(&self) -> &str {
        match *self {
            Error::Date(_)     => "parsing resulted in an invalid date",
            Error::Parse(_)    => "parse error",
        }
    }

    fn cause(&self) -> Option<&ErrorTrait> {
        match *self {
            Error::Date(ref error)   => Some(error),
            Error::Parse(_)          => None,
        }
    }
}