Skip to content

Commit

Permalink
Add parse_from_* to DateTime<Utc>
Browse files Browse the repository at this point in the history
As discussed in chronotope#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 chronotope#271 for conversion back into `DateTime<Utc>` from
`DateTime<FixedOffset>` as the latter is used under the hood.
  • Loading branch information
mqudsi committed Apr 8, 2019
1 parent 43447e5 commit 1600d83
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 16 deletions.
62 changes: 54 additions & 8 deletions src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,52 @@ fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz
f(dt.naive_local()).and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single())
}

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_rfc2822(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 DateTime<FixedOffset> {
/// 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`.
Expand Down Expand Up @@ -348,7 +394,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 Down Expand Up @@ -1586,13 +1632,13 @@ mod tests {
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"),
assert_eq!(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"),
assert_eq!(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"),
assert_eq!(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_eq!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"),
assert_eq!(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 @@ -1646,10 +1692,10 @@ mod tests {
#[test]
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"),
assert_eq!(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",
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"),
Expand Down
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,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 @@ -307,7 +307,7 @@
//!
//! ```rust
//! # use chrono::DateTime;
//! # use chrono::Utc;
//! # use chrono::{FixedOffset, Utc};
//! // We need the trait in scope to use Utc::timestamp().
//! use chrono::TimeZone;
//!
Expand All @@ -316,7 +316,7 @@
//! 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
6 changes: 3 additions & 3 deletions src/naive/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,14 @@ use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItem
/// and would be read back to the next non-leap second.
///
/// ~~~~
/// use chrono::{DateTime, Utc, TimeZone};
/// use chrono::{DateTime, Utc, FixedOffset, TimeZone};
///
/// let dt = Utc.ymd(2015, 6, 30).and_hms_milli(23, 56, 4, 1_000);
/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z");
///
/// 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::<FixedOffset>::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt);
/// ~~~~
///
/// Since Chrono alone cannot determine any existence of leap seconds,
Expand Down Expand Up @@ -1431,7 +1431,7 @@ mod serde {
impl<'de> de::Visitor<'de> for NaiveTimeVisitor {
type Value = NaiveTime;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
{
write!(formatter, "a formatted time string")
}
Expand Down

0 comments on commit 1600d83

Please sign in to comment.