Skip to content

Commit d767a4b

Browse files
committed
Add parse_and_remainder methods
1 parent 6655649 commit d767a4b

File tree

6 files changed

+146
-8
lines changed

6 files changed

+146
-8
lines changed

src/datetime/mod.rs

+37-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
2323
use crate::format::DelayedFormat;
2424
#[cfg(feature = "unstable-locales")]
2525
use crate::format::Locale;
26-
use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems};
26+
use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems};
2727
use crate::format::{Fixed, Item};
2828
use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime};
2929
#[cfg(feature = "clock")]
@@ -587,7 +587,8 @@ impl DateTime<FixedOffset> {
587587
///
588588
/// Note that this method *requires a timezone* in the string. See
589589
/// [`NaiveDateTime::parse_from_str`]
590-
/// for a version that does not require a timezone in the to-be-parsed str.
590+
/// for a version that does not require a timezone in `s`. The returned [`DateTime`] value will
591+
/// have a [`FixedOffset`] reflecting the parsed timezone.
591592
///
592593
/// # Example
593594
///
@@ -603,6 +604,40 @@ impl DateTime<FixedOffset> {
603604
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
604605
parsed.to_datetime()
605606
}
607+
608+
/// Parses a string from a user-specified format into a `DateTime<FixedOffset>` value, and a
609+
/// slice with the remaining portion of the string.
610+
///
611+
/// Note that this method *requires a timezone* in the input string. See
612+
/// [`NaiveDateTime::parse_and_remainder`] for a version that does not
613+
/// require a timezone in `s`. The returned [`DateTime`] value will have a [`FixedOffset`]
614+
/// reflecting the parsed timezone.
615+
///
616+
/// See the [`format::strftime` module](./format/strftime/index.html) for supported format
617+
/// sequences.
618+
///
619+
/// Similar to [`parse_from_str`](#method.parse_from_str).
620+
///
621+
/// # Example
622+
///
623+
/// ```rust
624+
/// # use chrono::{DateTime, FixedOffset, TimeZone, NaiveDate};
625+
/// let (datetime, remainder) = DateTime::parse_and_remainder(
626+
/// "2015-02-18 23:16:09 +0200 trailing text", "%Y-%m-%d %H:%M:%S %z").unwrap();
627+
/// assert_eq!(
628+
/// datetime,
629+
/// FixedOffset::east_opt(2*3600).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()
630+
/// );
631+
/// assert_eq!(remainder, " trailing text");
632+
/// ```
633+
pub fn parse_and_remainder<'a>(
634+
s: &'a str,
635+
fmt: &str,
636+
) -> ParseResult<(DateTime<FixedOffset>, &'a str)> {
637+
let mut parsed = Parsed::new();
638+
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?;
639+
parsed.to_datetime().map(|d| (d, remainder))
640+
}
606641
}
607642

608643
impl<Tz: TimeZone> DateTime<Tz>

src/format/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday};
5757
#[cfg(feature = "unstable-locales")]
5858
pub(crate) mod locales;
5959

60-
pub use parse::parse;
60+
pub use parse::{parse, parse_and_remainder};
6161
pub use parsed::Parsed;
6262
/// L10n locales.
6363
#[cfg(feature = "unstable-locales")]

src/format/parse.rs

+30
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,36 @@ where
248248
parse_internal(parsed, s, items).map(|_| ()).map_err(|(_s, e)| e)
249249
}
250250

251+
/// Tries to parse given string into `parsed` with given formatting items.
252+
/// Returns `Ok` with a slice of the unparsed remainder.
253+
///
254+
/// This particular date and time parser is:
255+
///
256+
/// - Greedy. It will consume the longest possible prefix.
257+
/// For example, `April` is always consumed entirely when the long month name is requested;
258+
/// it equally accepts `Apr`, but prefers the longer prefix in this case.
259+
///
260+
/// - Padding-agnostic (for numeric items).
261+
/// The [`Pad`](./enum.Pad.html) field is completely ignored,
262+
/// so one can prepend any number of zeroes before numbers.
263+
///
264+
/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
265+
pub fn parse_and_remainder<'a, 'b, I, B>(
266+
parsed: &mut Parsed,
267+
s: &'b str,
268+
items: I,
269+
) -> ParseResult<&'b str>
270+
where
271+
I: Iterator<Item = B>,
272+
B: Borrow<Item<'a>>,
273+
{
274+
match parse_internal(parsed, s, items) {
275+
Ok(s) => Ok(s),
276+
Err((s, ParseError(ParseErrorKind::TooLong))) => Ok(s),
277+
Err((_s, e)) => Err(e),
278+
}
279+
}
280+
251281
fn parse_internal<'a, 'b, I, B>(
252282
parsed: &mut Parsed,
253283
mut s: &'b str,

src/naive/date.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ use pure_rust_locales::Locale;
2020

2121
#[cfg(any(feature = "alloc", feature = "std", test))]
2222
use crate::format::DelayedFormat;
23-
use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems};
24-
use crate::format::{Item, Numeric, Pad};
23+
use crate::format::{
24+
parse, parse_and_remainder, write_hundreds, Item, Numeric, Pad, ParseError, ParseResult,
25+
Parsed, StrftimeItems,
26+
};
2527
use crate::month::Months;
2628
use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime};
2729
use crate::oldtime::Duration as OldDuration;
@@ -547,6 +549,28 @@ impl NaiveDate {
547549
parsed.to_naive_date()
548550
}
549551

552+
/// Parses a string from a user-specified format into a new `NaiveDate` value, and a slice with
553+
/// the remaining portion of the string.
554+
/// See the [`format::strftime` module](../format/strftime/index.html)
555+
/// on the supported escape sequences.
556+
///
557+
/// Similar to [`parse_from_str`](#method.parse_from_str).
558+
///
559+
/// # Example
560+
///
561+
/// ```rust
562+
/// # use chrono::{NaiveDate};
563+
/// let (date, remainder) = NaiveDate::parse_and_remainder(
564+
/// "2015-02-18 trailing text", "%Y-%m-%d").unwrap();
565+
/// assert_eq!(date, NaiveDate::from_ymd_opt(2015, 2, 18).unwrap());
566+
/// assert_eq!(remainder, " trailing text");
567+
/// ```
568+
pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveDate, &'a str)> {
569+
let mut parsed = Parsed::new();
570+
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?;
571+
parsed.to_naive_date().map(|d| (d, remainder))
572+
}
573+
550574
/// Add a duration in [`Months`] to the date
551575
///
552576
/// If the day would be out of range for the resulting month, use the last day for that month.

src/naive/datetime/mod.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rkyv::{Archive, Deserialize, Serialize};
1717

1818
#[cfg(any(feature = "alloc", feature = "std", test))]
1919
use crate::format::DelayedFormat;
20-
use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems};
20+
use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems};
2121
use crate::format::{Fixed, Item, Numeric, Pad};
2222
use crate::naive::{Days, IsoWeek, NaiveDate, NaiveTime};
2323
use crate::oldtime::Duration as OldDuration;
@@ -328,6 +328,31 @@ impl NaiveDateTime {
328328
parsed.to_naive_datetime_with_offset(0) // no offset adjustment
329329
}
330330

331+
/// Parses a string with the specified format string and returns a new `NaiveDateTime`, and a
332+
/// slice with the remaining portion of the string.
333+
/// See the [`format::strftime` module](../format/strftime/index.html)
334+
/// on the supported escape sequences.
335+
///
336+
/// Similar to [`parse_from_str`](#method.parse_from_str).
337+
///
338+
/// # Example
339+
///
340+
/// ```rust
341+
/// # use chrono::{NaiveDate, NaiveDateTime};
342+
/// let (datetime, remainder) = NaiveDateTime::parse_and_remainder(
343+
/// "2015-02-18 23:16:09 trailing text", "%Y-%m-%d %H:%M:%S").unwrap();
344+
/// assert_eq!(
345+
/// datetime,
346+
/// NaiveDate::from_ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap()
347+
/// );
348+
/// assert_eq!(remainder, " trailing text");
349+
/// ```
350+
pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveDateTime, &'a str)> {
351+
let mut parsed = Parsed::new();
352+
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?;
353+
parsed.to_naive_datetime_with_offset(0).map(|d| (d, remainder)) // no offset adjustment
354+
}
355+
331356
/// Retrieves a date component.
332357
///
333358
/// # Example

src/naive/time/mod.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ use rkyv::{Archive, Deserialize, Serialize};
1414

1515
#[cfg(any(feature = "alloc", feature = "std", test))]
1616
use crate::format::DelayedFormat;
17-
use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems};
18-
use crate::format::{Fixed, Item, Numeric, Pad};
17+
use crate::format::{
18+
parse, parse_and_remainder, write_hundreds, Fixed, Item, Numeric, Pad, ParseError, ParseResult,
19+
Parsed, StrftimeItems,
20+
};
1921
use crate::oldtime::Duration as OldDuration;
2022
use crate::Timelike;
2123

@@ -483,6 +485,28 @@ impl NaiveTime {
483485
parsed.to_naive_time()
484486
}
485487

488+
/// Parses a string from a user-specified format into a new `NaiveTime` value, and a slice with
489+
/// the remaining portion of the string.
490+
/// See the [`format::strftime` module](../format/strftime/index.html)
491+
/// on the supported escape sequences.
492+
///
493+
/// Similar to [`parse_from_str`](#method.parse_from_str).
494+
///
495+
/// # Example
496+
///
497+
/// ```rust
498+
/// # use chrono::{NaiveTime};
499+
/// let (time, remainder) = NaiveTime::parse_and_remainder(
500+
/// "3h4m33s trailing text", "%-Hh%-Mm%-Ss").unwrap();
501+
/// assert_eq!(time, NaiveTime::from_hms_opt(3, 4, 33).unwrap());
502+
/// assert_eq!(remainder, " trailing text");
503+
/// ```
504+
pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveTime, &'a str)> {
505+
let mut parsed = Parsed::new();
506+
let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?;
507+
parsed.to_naive_time().map(|t| (t, remainder))
508+
}
509+
486510
/// Adds given `Duration` to the current time,
487511
/// and also returns the number of *seconds*
488512
/// in the integral number of days ignored from the addition.

0 commit comments

Comments
 (0)