Skip to content

Commit

Permalink
Add parse_from_xxx to DateTime<Utc>
Browse files Browse the repository at this point in the history
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<Utc>` from
`DateTime<FixedOffset>` as the latter is used under the hood.
  • Loading branch information
mqudsi authored and djc committed Sep 12, 2022
1 parent 5305023 commit 925ca99
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 21 deletions.
47 changes: 45 additions & 2 deletions src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ impl DateTime<FixedOffset> {
/// ```
/// # use chrono::{DateTime, FixedOffset, TimeZone};
/// assert_eq!(
/// DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(),
/// DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(),
/// FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)
/// );
/// ```
Expand Down Expand Up @@ -574,7 +574,7 @@ impl DateTime<FixedOffset> {
/// ```rust
/// use chrono::{DateTime, FixedOffset, TimeZone};
///
/// let dt = DateTime::parse_from_str(
/// let dt = DateTime::<FixedOffset>::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)));
/// ```
Expand All @@ -585,6 +585,49 @@ impl DateTime<FixedOffset> {
}
}

impl DateTime<Utc> {
/// Parses an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`,
/// then returns a new `DateTime<Utc>` 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<Utc>> {
DateTime::<FixedOffset>::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<Utc>` 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<Utc>> {
DateTime::<FixedOffset>::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::<Utc>::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<Utc>> {
DateTime::<FixedOffset>::parse_from_str(s, fmt).map(|result| result.into())
}
}

impl<Tz: TimeZone> DateTime<Tz>
where
Tz::Offset: fmt::Display,
Expand Down
23 changes: 13 additions & 10 deletions src/datetime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<FixedOffset>::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::<FixedOffset>::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::<FixedOffset>::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::<FixedOffset>::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::<FixedOffset>::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::<FixedOffset>::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))
);
}
Expand Down Expand Up @@ -209,12 +209,15 @@ 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::<FixedOffset>::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")
.is_err());
assert!(DateTime::<FixedOffset>::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset
assert!(DateTime::<FixedOffset>::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"),
Ok(Utc.ymd(2013, 8, 9).and_hms(23, 54, 35))
Expand Down
6 changes: 3 additions & 3 deletions src/format/strftime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<FixedOffset>::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::<FixedOffset>::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::<FixedOffset>::parse_from_str("2001-07-07t15:04:60.026490708utc", "%+").unwrap()
);

assert_eq!(
Expand Down
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,11 @@
//! assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), 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::<FixedOffset>::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::<FixedOffset>::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::<FixedOffset>::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()));
Expand Down Expand Up @@ -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::<FixedOffset>::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap();
//! assert_eq!(dt.timestamp(), 1_500_000_000);
//! ```
//!
Expand Down
2 changes: 1 addition & 1 deletion src/naive/time/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Utc>::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt);
/// ```
///
/// Since Chrono alone cannot determine any existence of leap seconds,
Expand Down

0 comments on commit 925ca99

Please sign in to comment.