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_and_remainder methods #1011

Merged
merged 1 commit into from
May 19, 2023
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
36 changes: 35 additions & 1 deletion src/datetime/mod.rs
Original file line number Diff line number Diff line change
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 @@ -623,6 +623,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>(
pitdicker marked this conversation as resolved.
Show resolved Hide resolved
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,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
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,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 @@ -546,6 +548,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
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,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::offset::Utc;
Expand Down Expand Up @@ -300,6 +300,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.
pitdicker marked this conversation as resolved.
Show resolved Hide resolved
///
/// 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
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,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 @@ -482,6 +484,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