Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parse_from_xxx to DateTime<Utc> #806

Merged
merged 2 commits into from
Sep 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 90 additions & 27 deletions src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,16 +524,18 @@ where
}

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`].
/// Parses an RFC 2822 date-and-time string into a `DateTime<FixedOffset>` value.
///
/// RFC 2822 is the internet message standard that specifies the
/// representation of times in HTTP and email headers.
/// This parses valid RFC 2822 datetime strings (such as `Tue, 1 Jul 2003 10:52:37 +0200`)
/// and returns a new [`DateTime`] instance with the parsed timezone as the [`FixedOffset`].
///
/// RFC 2822 is the internet message standard that specifies the representation of times in HTTP
/// and email headers.
///
/// ```
/// # 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 All @@ -544,37 +546,42 @@ impl DateTime<FixedOffset> {
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`].
/// Parses an RFC 3339 date-and-time string into a `DateTime<FixedOffset>` value.
///
/// Parses all valid RFC 3339 values (as well as the subset of valid ISO 8601 values that are
/// also valid RFC 3339 date-and-time values) and returns a new [`DateTime`] with a
/// [`FixedOffset`] corresponding to the parsed timezone. While RFC 3339 values come in a wide
/// variety of shapes and sizes, `1996-12-19T16:39:57-08:00` is an example of the most commonly
/// encountered variety of RFC 3339 formats.
///
/// 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.
/// Why isn't this named `parse_from_iso8601`? That's because ISO 8601 allows representing
/// values in a wide range of formats, only some of which represent actual date-and-time
/// instances (rather than periods, ranges, dates, or times). Some valid ISO 8601 values are
/// also simultaneously valid RFC 3339 values, but not all RFC 3339 values are valid ISO 8601
/// values (or the other way around).
pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
let mut parsed = Parsed::new();
parse(&mut parsed, s, ITEMS.iter())?;
parsed.to_datetime()
}

/// Parses a string with the specified format string and returns a new
/// [`DateTime`] with a parsed [`FixedOffset`].
/// Parses a string from a user-specified format into a `DateTime<FixedOffset>` value.
///
/// See the [`crate::format::strftime`] module on the supported escape
/// sequences.
/// Note that this method *requires a timezone* in the input 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. The returned
/// [`DateTime`] value will have a [`FixedOffset`] reflecting the parsed timezone.
///
/// See also [`TimeZone::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`]
/// for a version that does not require a timezone in the to-be-parsed str.
/// See the [`format::strftime` module](./format/strftime/index.html) for supported format
/// sequences.
///
/// # Example
///
/// ```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 +592,62 @@ impl DateTime<FixedOffset> {
}
}

impl DateTime<Utc> {
/// Parses an RFC 2822 date-and-time string into a `DateTime<Utc>` value.
///
/// This parses valid RFC 2822 datetime values (such as `Tue, 1 Jul 2003 10:52:37 +0200`)
/// and returns a new `DateTime<Utc>` instance corresponding to the UTC date/time, accounting
/// for the difference between UTC and the parsed timezone, should they differ.
///
/// RFC 2822 is the internet message standard that specifies the representation of times in HTTP
/// and email headers.
pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<Utc>> {
DateTime::<FixedOffset>::parse_from_rfc2822(s).map(|result| result.into())
}

/// Parses an RFC 3339 date-and-time string into a `DateTime<Utc>` value.
///
/// Parses all valid RFC 3339 values (as well as the subset of valid ISO 8601 values that are
/// also valid RFC 3339 date-and-time values) and returns a new `DateTime<Utc>` instance
/// corresponding to the matching UTC date/time, accounting for the difference between UTC and
/// the parsed input's timezone, should they differ. While RFC 3339 values come in a wide
/// variety of shapes and sizes, `1996-12-19T16:39:57-08:00` is an example of the most commonly
/// encountered variety of RFC 3339 formats.
///
/// Why isn't this named `parse_from_iso8601`? That's because ISO 8601 allows representing
/// values in a wide range of formats, only some of which represent actual date-and-time
/// instances (rather than periods, ranges, dates, or times). Some valid ISO 8601 values are
/// also simultaneously valid RFC 3339 values, but not all RFC 3339 values are valid ISO 8601
/// values (or the other way around).
pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<Utc>> {
DateTime::<FixedOffset>::parse_from_rfc3339(s).map(|result| result.into())
}

/// Parses a string from a user-specified format into a `DateTime<Utc>` value.
///
/// Note that this method *requires a timezone* in the input 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. The returned
/// `DateTime<Utc>` value will reflect the difference in timezones between UTC and the parsed
/// time zone, should they differ.
///
/// See the [`format::strftime` module](./format/strftime/index.html) for supported format
/// sequences.
///
/// # 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 All @@ -606,10 +669,10 @@ where
}

/// Return an RFC 3339 and ISO 8601 date and time string with subseconds
/// formatted as per a `SecondsFormat`.
/// formatted as per `SecondsFormat`.
///
/// If passed `use_z` true and the timezone is UTC (offset 0), use 'Z', as
/// per [`Fixed::TimezoneOffsetColonZ`] If passed `use_z` false, use
/// If `use_z` is true and the timezone is UTC (offset 0), uses `Z` as
/// per [`Fixed::TimezoneOffsetColonZ`]. If `use_z` is false, uses
/// [`Fixed::TimezoneOffsetColon`]
///
/// # Examples
Expand Down Expand Up @@ -686,9 +749,9 @@ where
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items)
}

/// Formats the combined date and time with the specified format string.
/// See the [`crate::format::strftime`] module
/// on the supported escape sequences.
/// Formats the combined date and time per the specified format string.
///
/// See the [`crate::format::strftime`] module for the supported escape sequences.
///
/// # Example
/// ```rust
Expand Down Expand Up @@ -728,7 +791,7 @@ where
)
}

/// Formats the combined date and time with the specified format string and
/// Formats the combined date and time per the specified format string and
/// locale.
///
/// See the [`crate::format::strftime`] module on the supported escape
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