Skip to content

Commit

Permalink
Add parse_and_remainder methods
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed May 12, 2023
1 parent 6655649 commit d767a4b
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 8 deletions.
39 changes: 37 additions & 2 deletions src/datetime/mod.rs
Expand Up @@ -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")]
Expand Down Expand Up @@ -587,7 +587,8 @@ impl DateTime<FixedOffset> {
///
/// 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.
/// for a version that does not require a timezone in `s`. The returned [`DateTime`] value will
/// have a [`FixedOffset`] reflecting the parsed timezone.
///
/// # Example
///
Expand All @@ -603,6 +604,40 @@ impl DateTime<FixedOffset> {
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parsed.to_datetime()
}

/// Parses a string from a user-specified format into a `DateTime<FixedOffset>` 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_and_remainder`] for a version that does not
/// require a timezone in `s`. 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.
///
/// Similar to [`parse_from_str`](#method.parse_from_str).
///
/// # Example
///
/// ```rust
/// # use chrono::{DateTime, FixedOffset, TimeZone, NaiveDate};
/// let (datetime, remainder) = DateTime::parse_and_remainder(
/// "2015-02-18 23:16:09 +0200 trailing text", "%Y-%m-%d %H:%M:%S %z").unwrap();
/// assert_eq!(
/// datetime,
/// FixedOffset::east_opt(2*3600).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()
/// );
/// assert_eq!(remainder, " trailing text");
/// ```
pub fn parse_and_remainder<'a>(
s: &'a str,
fmt: &str,
) -> ParseResult<(DateTime<FixedOffset>, &'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<Tz: TimeZone> DateTime<Tz>
Expand Down
2 changes: 1 addition & 1 deletion src/format/mod.rs
Expand Up @@ -57,7 +57,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")]
Expand Down
30 changes: 30 additions & 0 deletions src/format/parse.rs
Expand Up @@ -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<Item = B>,
B: Borrow<Item<'a>>,
{
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,
Expand Down
28 changes: 26 additions & 2 deletions src/naive/date.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -547,6 +549,28 @@ 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.
///
/// Similar to [`parse_from_str`](#method.parse_from_str).
///
/// # Example
///
/// ```rust
/// # use chrono::{NaiveDate};
/// let (date, remainder) = NaiveDate::parse_and_remainder(
/// "2015-02-18 trailing text", "%Y-%m-%d").unwrap();
/// assert_eq!(date, NaiveDate::from_ymd_opt(2015, 2, 18).unwrap());
/// assert_eq!(remainder, " trailing text");
/// ```
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.
Expand Down
27 changes: 26 additions & 1 deletion src/naive/datetime/mod.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -328,6 +328,31 @@ 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.
///
/// Similar to [`parse_from_str`](#method.parse_from_str).
///
/// # Example
///
/// ```rust
/// # use chrono::{NaiveDate, NaiveDateTime};
/// let (datetime, remainder) = NaiveDateTime::parse_and_remainder(
/// "2015-02-18 23:16:09 trailing text", "%Y-%m-%d %H:%M:%S").unwrap();
/// assert_eq!(
/// datetime,
/// NaiveDate::from_ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap()
/// );
/// assert_eq!(remainder, " trailing text");
/// ```
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
Expand Down
28 changes: 26 additions & 2 deletions src/naive/time/mod.rs
Expand Up @@ -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;

Expand Down Expand Up @@ -483,6 +485,28 @@ 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.
///
/// Similar to [`parse_from_str`](#method.parse_from_str).
///
/// # Example
///
/// ```rust
/// # use chrono::{NaiveTime};
/// let (time, remainder) = NaiveTime::parse_and_remainder(
/// "3h4m33s trailing text", "%-Hh%-Mm%-Ss").unwrap();
/// assert_eq!(time, NaiveTime::from_hms_opt(3, 4, 33).unwrap());
/// assert_eq!(remainder, " trailing text");
/// ```
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.
Expand Down

0 comments on commit d767a4b

Please sign in to comment.