diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index dc9349472a..e3e28934ae 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -23,7 +23,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use crate::format::DelayedFormat; #[cfg(feature = "unstable-locales")] use crate::format::Locale; -use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems}; use crate::format::{Fixed, Item}; use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime}; #[cfg(feature = "clock")] @@ -604,6 +604,25 @@ impl DateTime { parse(&mut parsed, s, StrftimeItems::new(fmt))?; parsed.to_datetime() } + + /// Parses a string from a user-specified format into a `DateTime` value, and a + /// slice with the remaining portion of the string. + /// + /// 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 the [`format::strftime` module](./format/strftime/index.html) for supported format + /// sequences. + pub fn parse_and_remainder<'a>( + s: &'a str, + fmt: &str, + ) -> ParseResult<(DateTime, &'a str)> { + let mut parsed = Parsed::new(); + let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_datetime().map(|d| (d, remainder)) + } } impl DateTime diff --git a/src/format/mod.rs b/src/format/mod.rs index 5685860c7b..6dd6bd4d0e 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -56,7 +56,7 @@ use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday}; #[cfg(feature = "unstable-locales")] pub(crate) mod locales; -pub use parse::parse; +pub use parse::{parse, parse_and_remainder}; pub use parsed::Parsed; /// L10n locales. #[cfg(feature = "unstable-locales")] diff --git a/src/format/parse.rs b/src/format/parse.rs index 69204d2e95..556de27553 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -248,6 +248,36 @@ where parse_internal(parsed, s, items).map(|_| ()).map_err(|(_s, e)| e) } +/// Tries to parse given string into `parsed` with given formatting items. +/// Returns `Ok` with a slice of the unparsed remainder. +/// +/// This particular date and time parser is: +/// +/// - Greedy. It will consume the longest possible prefix. +/// For example, `April` is always consumed entirely when the long month name is requested; +/// it equally accepts `Apr`, but prefers the longer prefix in this case. +/// +/// - Padding-agnostic (for numeric items). +/// The [`Pad`](./enum.Pad.html) field is completely ignored, +/// so one can prepend any number of zeroes before numbers. +/// +/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`. +pub fn parse_and_remainder<'a, 'b, I, B>( + parsed: &mut Parsed, + s: &'b str, + items: I, +) -> ParseResult<&'b str> +where + I: Iterator, + B: Borrow>, +{ + match parse_internal(parsed, s, items) { + Ok(s) => Ok(s), + Err((s, ParseError(ParseErrorKind::TooLong))) => Ok(s), + Err((_s, e)) => Err(e), + } +} + fn parse_internal<'a, 'b, I, B>( parsed: &mut Parsed, mut s: &'b str, diff --git a/src/naive/date.rs b/src/naive/date.rs index 4fabd73809..9d6077df15 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -20,8 +20,10 @@ use pure_rust_locales::Locale; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::format::DelayedFormat; -use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems}; -use crate::format::{Item, Numeric, Pad}; +use crate::format::{ + parse, parse_and_remainder, write_hundreds, Item, Numeric, Pad, ParseError, ParseResult, + Parsed, StrftimeItems, +}; use crate::month::Months; use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime}; use crate::oldtime::Duration as OldDuration; @@ -547,6 +549,16 @@ impl NaiveDate { parsed.to_naive_date() } + /// Parses a string from a user-specified format into a new `NaiveDate` value, and a slice with + /// the remaining portion of the string. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveDate, &'a str)> { + let mut parsed = Parsed::new(); + let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_naive_date().map(|d| (d, remainder)) + } + /// Add a duration in [`Months`] to the date /// /// If the day would be out of range for the resulting month, use the last day for that month. diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 671e9eddf2..00959021fe 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -17,7 +17,7 @@ use rkyv::{Archive, Deserialize, Serialize}; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::format::DelayedFormat; -use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems}; use crate::format::{Fixed, Item, Numeric, Pad}; use crate::naive::{Days, IsoWeek, NaiveDate, NaiveTime}; use crate::oldtime::Duration as OldDuration; @@ -328,6 +328,16 @@ impl NaiveDateTime { parsed.to_naive_datetime_with_offset(0) // no offset adjustment } + /// Parses a string with the specified format string and returns a new `NaiveDateTime`, and a + /// slice with the remaining portion of the string. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveDateTime, &'a str)> { + let mut parsed = Parsed::new(); + let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_naive_datetime_with_offset(0).map(|d| (d, remainder)) // no offset adjustment + } + /// Retrieves a date component. /// /// # Example diff --git a/src/naive/time/mod.rs b/src/naive/time/mod.rs index 14e22fe61b..7390be0e9c 100644 --- a/src/naive/time/mod.rs +++ b/src/naive/time/mod.rs @@ -14,8 +14,10 @@ use rkyv::{Archive, Deserialize, Serialize}; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::format::DelayedFormat; -use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems}; -use crate::format::{Fixed, Item, Numeric, Pad}; +use crate::format::{ + parse, parse_and_remainder, write_hundreds, Fixed, Item, Numeric, Pad, ParseError, ParseResult, + Parsed, StrftimeItems, +}; use crate::oldtime::Duration as OldDuration; use crate::Timelike; @@ -483,6 +485,16 @@ impl NaiveTime { parsed.to_naive_time() } + /// Parses a string from a user-specified format into a new `NaiveTime` value, and a slice with + /// the remaining portion of the string. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveTime, &'a str)> { + let mut parsed = Parsed::new(); + let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_naive_time().map(|t| (t, remainder)) + } + /// Adds given `Duration` to the current time, /// and also returns the number of *seconds* /// in the integral number of days ignored from the addition.