iso-8601 0.4.1

Fully compliant ISO 8601 parser.
Documentation
use ::{
    datetime::*,
    date::*,
    time::*
};
use super::*;

macro_rules! datetime {
    (pub $name:ident, $date:ty, $date_parser:ident, $time:ty, $time_parser:ident) => {
        named!(pub $name <DateTime<$date, $time>>, do_parse!(
            date: $date_parser >>
            char!('T') >>
            peek!(not!(char!('T'))) >>
            time: $time_parser >>
            (DateTime { date, time })
        ));
    }
}
datetime!(pub datetime_global_hms,           Date,       date,        GlobalTime<HmsTime>, time_global_hms);
datetime!(pub datetime_global_hm,            Date,       date,        GlobalTime<HmTime>,  time_global_hm);
datetime!(pub datetime_global_h,             Date,       date,        GlobalTime<HTime>,   time_global_h);
datetime!(pub datetime_local_hms,            Date,       date,        LocalTime<HmsTime>,  time_local_hms);
datetime!(pub datetime_local_hm,             Date,       date,        LocalTime<HmTime>,   time_local_hm);
datetime!(pub datetime_local_h,              Date,       date,        LocalTime<HTime>,    time_local_h);
datetime!(pub datetime_any_hms,              Date,       date,        AnyTime<HmsTime>,    time_any_hms);
datetime!(pub datetime_any_hm,               Date,       date,        AnyTime<HmTime>,     time_any_hm);
datetime!(pub datetime_any_h,                Date,       date,        AnyTime<HTime>,      time_any_h);
datetime!(pub datetime_global_approx,        Date,       date,        ApproxGlobalTime,    time_global_approx);
datetime!(pub datetime_local_approx,         Date,       date,        ApproxLocalTime,     time_local_approx);
datetime!(pub datetime_any_approx,           Date,       date,        ApproxAnyTime,       time_any_approx);
datetime!(pub datetime_approx_global_hms,    ApproxDate, date_approx, GlobalTime<HmsTime>, time_global_hms);
datetime!(pub datetime_approx_global_hm,     ApproxDate, date_approx, GlobalTime<HmTime>,  time_global_hm);
datetime!(pub datetime_approx_global_h,      ApproxDate, date_approx, GlobalTime<HTime>,   time_global_h);
datetime!(pub datetime_approx_local_hms,     ApproxDate, date_approx, LocalTime<HmsTime>,  time_local_hms);
datetime!(pub datetime_approx_local_hm,      ApproxDate, date_approx, LocalTime<HmTime>,   time_local_hm);
datetime!(pub datetime_approx_local_h,       ApproxDate, date_approx, LocalTime<HTime>,    time_local_h);
datetime!(pub datetime_approx_any_hms,       ApproxDate, date_approx, AnyTime<HmsTime>,    time_any_hms);
datetime!(pub datetime_approx_any_hm,        ApproxDate, date_approx, AnyTime<HmTime>,     time_any_hm);
datetime!(pub datetime_approx_any_h,         ApproxDate, date_approx, AnyTime<HTime>,      time_any_h);
datetime!(pub datetime_approx_global_approx, ApproxDate, date_approx, ApproxGlobalTime,    time_global_approx);
datetime!(pub datetime_approx_local_approx,  ApproxDate, date_approx, ApproxLocalTime,     time_local_approx);
datetime!(pub datetime_approx_any_approx,    ApproxDate, date_approx, ApproxAnyTime,       time_any_approx);

named!(pub partial_datetime_approx_any_approx <PartialDateTime<ApproxDate, ApproxAnyTime>>, do_parse!(
    has_date: opt!(peek!(re_bytes_match!("^(.+T.*|[^T:]*)$"))) >>
    date: cond!(has_date.is_some(), date_approx) >>
    opt!(complete!(char!('T'))) >>
    opt!(complete!(peek!(not!(char!('T'))))) >>
    time: opt!(time_any_approx) >>
    (match (date, time) {
        (None, None) => return Err(nom::Err::Incomplete(nom::Needed::Unknown)),
        (Some(date), None) => PartialDateTime::Date(date),
        (None, Some(time)) => PartialDateTime::Time(time),
        (Some(date), Some(time)) => PartialDateTime::DateTime(DateTime { date, time }),
    })
));

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn tt() {
        datetime_approx_any_approx(b"2018-08-02TT22:01:39Z").unwrap();
    }

    #[test]
    fn partial_datetime_approx_any_approx_date_y() {
        assert_eq!(partial_datetime_approx_any_approx(b"2018"), Ok((&[][..], PartialDateTime::Date(ApproxDate::Y(YDate {
            year: 2018,
        })))));
    }

    #[test]
    fn partial_datetime_approx_any_approx_date_ym_basic() {
        assert_eq!(partial_datetime_approx_any_approx(b"201808"), Ok((&[][..], PartialDateTime::Date(ApproxDate::YM(YmDate {
            year: 2018,
            month: 8,
        })))));
    }

    #[test]
    fn partial_datetime_approx_any_approx_date_ym_extended() {
        assert_eq!(partial_datetime_approx_any_approx(b"2018-08"), Ok((&[][..], PartialDateTime::Date(ApproxDate::YM(YmDate {
            year: 2018,
            month: 8,
        })))));
    }

    #[test]
    fn partial_datetime_approx_any_approx_date_ymd_basic() {
        assert_eq!(partial_datetime_approx_any_approx(b"20180802"), Ok((&[][..], PartialDateTime::Date(ApproxDate::YMD(YmdDate {
            year: 2018,
            month: 8,
            day: 2,
        })))));
    }

    #[test]
    fn partial_datetime_approx_any_approx_date_ymd_extended() {
        assert_eq!(partial_datetime_approx_any_approx(b"2018-08-02"), Ok((&[][..], PartialDateTime::Date(ApproxDate::YMD(YmdDate {
            year: 2018,
            month: 8,
            day: 2,
        })))));
    }

    #[test]
    fn partial_datetime_approx_any_approx_time_h() {
        assert_eq!(partial_datetime_approx_any_approx(b"T12"), Ok((&[][..], PartialDateTime::Time(ApproxAnyTime::H(AnyTime::Local(LocalTime {
            naive: HTime {
                hour: 12,
            },
            fraction: 0.,
        }))))));
    }

    #[test]
    fn partial_datetime_approx_any_approx_time_hm_basic() {
        assert_eq!(partial_datetime_approx_any_approx(b"T1230"), Ok((&[][..], PartialDateTime::Time(ApproxAnyTime::HM(AnyTime::Local(LocalTime {
            naive: HmTime {
                hour: 12,
                minute: 30,
            },
            fraction: 0.,
        }))))));
    }

    #[test]
    fn partial_datetime_approx_any_approx_time_hm_extended() {
        let result = PartialDateTime::Time(ApproxAnyTime::HM(AnyTime::Local(LocalTime {
            naive: HmTime {
                hour: 12,
                minute: 30,
            },
            fraction: 0.,
        })));

        assert_eq!(partial_datetime_approx_any_approx(b"T12:30"), Ok((&[][..], result.clone())));
        assert_eq!(partial_datetime_approx_any_approx(b"12:30"),  Ok((&[][..], result        )));
    }

    #[test]
    fn partial_datetime_approx_any_approx_time_hms_basic() {
        assert_eq!(partial_datetime_approx_any_approx(b"T123015"), Ok((&[][..], PartialDateTime::Time(ApproxAnyTime::HMS(AnyTime::Local(LocalTime {
            naive: HmsTime {
                hour: 12,
                minute: 30,
                second: 15,
            },
            fraction: 0.,
        }))))));
    }

    #[test]
    fn partial_datetime_approx_any_approx_time_hms_extended() {
        let result = PartialDateTime::Time(ApproxAnyTime::HMS(AnyTime::Local(LocalTime {
            naive: HmsTime {
                hour: 12,
                minute: 30,
                second: 15,
            },
            fraction: 0.,
        })));

        assert_eq!(partial_datetime_approx_any_approx(b"T12:30:15"), Ok((&[][..], result.clone())));
        assert_eq!(partial_datetime_approx_any_approx(b"12:30:15"),  Ok((&[][..], result        )));
    }

    #[test]
    fn partial_datetime_approx_any_approx_time_hmsf_basic() {
        assert_eq!(partial_datetime_approx_any_approx(b"T123015.2"), Ok((&[][..], PartialDateTime::Time(ApproxAnyTime::HMS(AnyTime::Local(LocalTime {
            naive: HmsTime {
                hour: 12,
                minute: 30,
                second: 15,
            },
            fraction: 0.2,
        }))))));
    }

    #[test]
    fn partial_datetime_approx_any_approx_time_hmsf_extended() {
        let result = PartialDateTime::Time(ApproxAnyTime::HMS(AnyTime::Local(LocalTime {
            naive: HmsTime {
                hour: 12,
                minute: 30,
                second: 15,
            },
            fraction: 0.2,
        })));

        assert_eq!(partial_datetime_approx_any_approx(b"T12:30:15.2"), Ok((&[][..], result.clone())));
        assert_eq!(partial_datetime_approx_any_approx(b"12:30:15.2"),  Ok((&[][..], result        )));
    }

    #[test]
    fn partial_datetime_approx_any_approx_datetime_extended() {
        let result = PartialDateTime::DateTime(DateTime {
            date: ApproxDate::YMD(YmdDate {
                year: 2018,
                month: 8,
                day: 2,
            }),
            time: ApproxAnyTime::HMS(AnyTime::Local(LocalTime {
                naive: HmsTime {
                    hour: 12,
                    minute: 30,
                    second: 15,
                },
                fraction: 0.2,
            }))
        });

        assert_eq!(partial_datetime_approx_any_approx(b"2018-08-02T12:30:15.2"), Ok((&[][..], result.clone())));
        assert_eq!(partial_datetime_approx_any_approx(b"20180802T123015.2"),     Ok((&[][..], result        )));
    }
}