From d203b049ccba58a8bd8c26fc47a0c3c98d6084c4 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 31 Aug 2022 15:57:57 -0500 Subject: [PATCH] Add `parse_from_xxx` to `DateTime` As discussed in #263. Note that this breaks existing code that did not explicitly specify the offset in calls to `DateTime::parse_from_*`, as they are now ambiguous. Relies on #271 for conversion back into `DateTime` from `DateTime` as the latter is used under the hood. --- src/datetime/mod.rs | 50 ++++++++++++++++++++++++++++++++++++++++-- src/datetime/tests.rs | 18 +++++++-------- src/format/strftime.rs | 6 ++--- src/lib.rs | 10 ++++----- src/naive/time/mod.rs | 2 +- 5 files changed, 66 insertions(+), 20 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 1ae0d9c42d..1c2c8db84d 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -533,7 +533,7 @@ impl DateTime { /// ``` /// # use chrono::{DateTime, FixedOffset, TimeZone}; /// assert_eq!( - /// DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(), + /// DateTime::::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(), /// FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9) /// ); /// ``` @@ -574,7 +574,7 @@ impl DateTime { /// ```rust /// use chrono::{DateTime, FixedOffset, TimeZone}; /// - /// let dt = DateTime::parse_from_str( + /// let dt = DateTime::::parse_from_str( /// "1983 Apr 13 12:09:14.274 +0000", "%Y %b %d %H:%M:%S%.3f %z"); /// assert_eq!(dt, Ok(FixedOffset::east(0).ymd(1983, 4, 13).and_hms_milli(12, 9, 14, 274))); /// ``` @@ -585,6 +585,52 @@ impl DateTime { } } +impl DateTime { + /// Parses an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`, + /// then returns a new `DateTime` instance corresponding to the UTC date/time accounting + /// for the difference between UTC and the parsed timezone. + pub fn parse_from_rfc2822(s: &str) -> ParseResult> { + DateTime::::parse_from_rfc2822(s) + .map(|result| result.into()) + } + + /// 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` instance corresponding to the UTC date/time accounting + /// for the difference between UTC and the parsed timezone. + /// + /// 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> { + DateTime::::parse_from_rfc3339(s) + .map(|result| result.into()) + } + + /// Parses a string with the specified format string and + /// returns a new `DateTime` with a parsed `FixedOffset`. + /// See the [`format::strftime` module](./format/strftime/index.html) + /// on the supported escape sequences. + /// + /// See also `Offset::datetime_from_str` which gives a local `DateTime` on specific time zone. + /// + /// Note that this method *requires a timezone* in the string. See + /// [`NaiveDateTime::parse_from_str`](./naive/struct.NaiveDateTime.html#method.parse_from_str) + /// for a version that does not require a timezone in the to-be-parsed str. + /// + /// # Example + /// + /// ```rust + /// use chrono::{DateTime, TimeZone, Utc}; + /// + /// let dt = DateTime::::parse_from_str( + /// "1983 Apr 13 12:09:14.274 +0100", "%Y %b %d %H:%M:%S%.3f %z"); + /// assert_eq!(dt, Ok(Utc.ymd(1983, 4, 13).and_hms_milli(11, 9, 14, 274))); + /// ``` + pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult> { + DateTime::::parse_from_str(s, fmt) + .map(|result| result.into()) + } +} + impl DateTime where Tz::Offset: fmt::Display, diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index 6c2b900f3a..703721b26b 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -111,24 +111,24 @@ fn test_datetime_rfc2822_and_rfc3339() { ); assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), + 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_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"), + 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"), + 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"), + 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!(DateTime::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err()); + assert!(DateTime::::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err()); assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), + 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)) ); } @@ -209,11 +209,11 @@ fn test_datetime_from_str() { fn test_datetime_parse_from_str() { let ymdhms = |y, m, d, h, n, s, off| FixedOffset::east(off).ymd(y, m, d).and_hms(h, n, s); assert_eq!( - DateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + DateTime::::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), Ok(ymdhms(2014, 5, 7, 12, 34, 56, 570 * 60)) ); // ignore offset - assert!(DateTime::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset - assert!(DateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT") + assert!(DateTime::::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset + assert!(DateTime::::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT") .is_err()); assert_eq!( Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"), diff --git a/src/format/strftime.rs b/src/format/strftime.rs index 31509a0ff4..c2d2e4d2fe 100644 --- a/src/format/strftime.rs +++ b/src/format/strftime.rs @@ -622,15 +622,15 @@ fn test_strftime_docs() { ); assert_eq!( dt.with_timezone(&Utc), - DateTime::parse_from_str("2001-07-07T15:04:60.026490708Z", "%+").unwrap() + DateTime::::parse_from_str("2001-07-07T15:04:60.026490708Z", "%+").unwrap() ); assert_eq!( dt.with_timezone(&Utc), - DateTime::parse_from_str("2001-07-07T15:04:60.026490708UTC", "%+").unwrap() + DateTime::::parse_from_str("2001-07-07T15:04:60.026490708UTC", "%+").unwrap() ); assert_eq!( dt.with_timezone(&Utc), - DateTime::parse_from_str("2001-07-07t15:04:60.026490708utc", "%+").unwrap() + DateTime::::parse_from_str("2001-07-07t15:04:60.026490708utc", "%+").unwrap() ); assert_eq!( diff --git a/src/lib.rs b/src/lib.rs index 4c3f086b25..4db2128d47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -268,11 +268,11 @@ //! assert_eq!("2014-11-28T21:00:09+09:00".parse::>(), Ok(fixed_dt.clone())); //! //! // method 2 -//! assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z"), +//! assert_eq!(DateTime::::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z"), //! Ok(fixed_dt.clone())); -//! assert_eq!(DateTime::parse_from_rfc2822("Fri, 28 Nov 2014 21:00:09 +0900"), +//! assert_eq!(DateTime::::parse_from_rfc2822("Fri, 28 Nov 2014 21:00:09 +0900"), //! Ok(fixed_dt.clone())); -//! assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone())); +//! assert_eq!(DateTime::::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone())); //! //! // method 3 //! assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone())); @@ -302,14 +302,14 @@ //! //! ```rust //! // We need the trait in scope to use Utc::timestamp(). -//! use chrono::{DateTime, TimeZone, Utc}; +//! use chrono::{DateTime, FixedOffset, TimeZone, Utc}; //! //! // Construct a datetime from epoch: //! let dt = Utc.timestamp(1_500_000_000, 0); //! assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000"); //! //! // Get epoch value from a datetime: -//! let dt = DateTime::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap(); +//! let dt = DateTime::::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap(); //! assert_eq!(dt.timestamp(), 1_500_000_000); //! ``` //! diff --git a/src/naive/time/mod.rs b/src/naive/time/mod.rs index 84305bf8f5..646421a498 100644 --- a/src/naive/time/mod.rs +++ b/src/naive/time/mod.rs @@ -178,7 +178,7 @@ mod tests; /// /// let dt = Utc.ymd(2015, 6, 30).and_hms(23, 56, 5); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); -/// assert_eq!(DateTime::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt); +/// assert_eq!(DateTime::::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt); /// ``` /// /// Since Chrono alone cannot determine any existence of leap seconds,