From 30322bbc896d3fabcd88092789e672f7bbae7b53 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 19 Feb 2015 04:57:21 +0900 Subject: [PATCH] added `parse_from_rfc{2822,3339}`/`to_rfc{2822,3339}` methods to DateTime. --- src/datetime.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++++ src/format/mod.rs | 3 ++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/datetime.rs b/src/datetime.rs index b169dd3..bace9d6 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -123,6 +123,27 @@ fn map_local(dt: &DateTime, mut f: F) -> Option { + /// Parses an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`, + /// then returns a new `DateTime` with a parsed `FixedOffset`. + pub fn parse_from_rfc2822(s: &str) -> ParseResult> { + const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; + let mut parsed = Parsed::new(); + try!(parse(&mut parsed, s, ITEMS.iter().cloned())); + parsed.to_datetime() + } + + /// Parses an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`, + /// then returns a new `DateTime` with a parsed `FixedOffset`. + /// + /// Why isn't this named `parse_from_iso8601`? That's because ISO 8601 allows some freedom + /// over the syntax and RFC 3339 exercises that freedom to rigidly define a fixed format. + pub fn parse_from_rfc3339(s: &str) -> ParseResult> { + const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; + let mut parsed = Parsed::new(); + try!(parse(&mut parsed, s, ITEMS.iter().cloned())); + parsed.to_datetime() + } + /// Parses a string with the specified format string and /// returns a new `DateTime` with a parsed `FixedOffset`. /// See the `format::strftime` module on the supported escape sequences. @@ -136,6 +157,18 @@ impl DateTime { } impl DateTime where Tz::Offset: fmt::Display { + /// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`. + pub fn to_rfc2822(&self) -> String { + const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; + self.format_with_items(ITEMS.iter().cloned()).to_string() + } + + /// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`. + pub fn to_rfc3339(&self) -> String { + const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; + self.format_with_items(ITEMS.iter().cloned()).to_string() + } + /// Formats the combined date and time with the specified formatting items. #[inline] pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I> @@ -370,6 +403,33 @@ mod tests { NaiveTime::from_hms(7, 8, 9)); } + #[test] + #[allow(non_snake_case)] + fn test_datetime_rfc2822_and_rfc3339() { + let EDT = FixedOffset::east(5*60*60); + assert_eq!(UTC.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc2822(), + "Wed, 18 Feb 2015 23:16:09 +0000"); + assert_eq!(UTC.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc3339(), + "2015-02-18T23:16:09+00:00"); + assert_eq!(EDT.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150).to_rfc2822(), + "Wed, 18 Feb 2015 23:16:09 +0500"); + assert_eq!(EDT.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150).to_rfc3339(), + "2015-02-18T23:16:09.150+05:00"); + assert_eq!(EDT.ymd(2015, 2, 18).and_hms_micro(23, 59, 59, 1_234_567).to_rfc2822(), + "Wed, 18 Feb 2015 23:59:60 +0500"); + assert_eq!(EDT.ymd(2015, 2, 18).and_hms_micro(23, 59, 59, 1_234_567).to_rfc3339(), + "2015-02-18T23:59:60.234567+05:00"); + + assert_eq!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), + Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9))); + assert_eq!(DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"), + Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9))); + assert_eq!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), + Ok(EDT.ymd(2015, 2, 18).and_hms_milli(23, 59, 59, 1_000))); + assert_eq!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), + Ok(EDT.ymd(2015, 2, 18).and_hms_micro(23, 59, 59, 1_234_567))); + } + #[test] fn test_datetime_from_str() { assert_eq!("2015-2-18T23:16:9.15Z".parse::>(), diff --git a/src/format/mod.rs b/src/format/mod.rs index 0634878..9391b89 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -366,10 +366,11 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt off.map(|&(_, off)| write_local_minus_utc(w, off, true, false)), RFC2822 => // same to `%a, %e %b %Y %H:%M:%S %z` if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { + let sec = t.second() + t.nanosecond() / 1_000_000_000; try!(write!(w, "{}, {:2} {} {:04} {:02}:{:02}:{:02} ", SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize], d.day(), SHORT_MONTHS[d.month0() as usize], d.year(), - t.hour(), t.minute(), t.second())); + t.hour(), t.minute(), sec)); Some(write_local_minus_utc(w, off, false, false)) } else { None