/// Parsing iterator for `strftime`-like format strings. /// /// See the [`format::strftime` module](crate::format::strftime) for supported formatting /// specifiers. /// /// `StrftimeItems` is used in combination with more low-level methods such as [`format::parse()`] /// or [`format_with_items`]. /// /// If formatting or parsing date and time values is not performance-critical, the methods /// [`parse_from_str`] and [`format`] on types such as [`DateTime`](crate::DateTime) are easier to /// use. /// /// [`format`]: crate::DateTime::format /// [`format_with_items`]: crate::DateTime::format /// [`parse_from_str`]: crate::DateTime::parse_from_str /// [`DateTime`]: crate::DateTime /// [`format::parse()`]: crate::format::parse() #[derive(Clone, Debug)] pubstruct StrftimeItems<'a> { /// Remaining portion of the string.
remainder: &'a str, /// If the current specifier is composed of multiple formatting items (e.g. `%+`), /// `queue` stores a slice of `Item`s that have to be returned one by one.
queue: &'static [Item<'static>],
lenient: bool, #[cfg(feature = "unstable-locales")]
locale_str: &'a str, #[cfg(feature = "unstable-locales")]
locale: Option<Locale>,
}
impl<'a> StrftimeItems<'a> { /// Creates a new parsing iterator from a `strftime`-like format string. /// /// # Errors /// /// While iterating [`Item::Error`] will be returned if the format string contains an invalid /// or unrecognized formatting specifier. /// /// # Example /// /// ``` /// use chrono::format::*; /// /// let strftime_parser = StrftimeItems::new("%F"); // %F: year-month-day (ISO 8601) /// /// const ISO8601_YMD_ITEMS: &[Item<'static>] = &[ /// Item::Numeric(Numeric::Year, Pad::Zero), /// Item::Literal("-"), /// Item::Numeric(Numeric::Month, Pad::Zero), /// Item::Literal("-"), /// Item::Numeric(Numeric::Day, Pad::Zero), /// ]; /// assert!(strftime_parser.eq(ISO8601_YMD_ITEMS.iter().cloned())); /// ``` #[must_use] pubconstfn new(s: &'a str) -> StrftimeItems<'a> {
StrftimeItems {
remainder: s,
queue: &[],
lenient: false, #[cfg(feature = "unstable-locales")]
locale_str: "", #[cfg(feature = "unstable-locales")]
locale: None,
}
}
/// The same as [`StrftimeItems::new`], but returns [`Item::Literal`] instead of [`Item::Error`]. /// /// Useful for formatting according to potentially invalid format strings. /// /// # Example /// /// ``` /// use chrono::format::*; /// /// let strftime_parser = StrftimeItems::new_lenient("%Y-%Q"); // %Y: year, %Q: invalid /// /// const ITEMS: &[Item<'static>] = &[ /// Item::Numeric(Numeric::Year, Pad::Zero), /// Item::Literal("-"), /// Item::Literal("%Q"), /// ]; /// println!("{:?}", strftime_parser.clone().collect::<Vec<_>>()); /// assert!(strftime_parser.eq(ITEMS.iter().cloned())); /// ``` #[must_use] pubconstfn new_lenient(s: &'a str) -> StrftimeItems<'a> {
StrftimeItems {
remainder: s,
queue: &[],
lenient: true, #[cfg(feature = "unstable-locales")]
locale_str: "", #[cfg(feature = "unstable-locales")]
locale: None,
}
}
/// Creates a new parsing iterator from a `strftime`-like format string, with some formatting /// specifiers adjusted to match [`Locale`]. /// /// Note: `StrftimeItems::new_with_locale` only localizes the *format*. You usually want to /// combine it with other locale-aware methods such as /// [`DateTime::format_localized_with_items`] to get things like localized month or day names. /// /// The `%x` formatting specifier will use the local date format, `%X` the local time format, /// and `%c` the local format for date and time. /// `%r` will use the local 12-hour clock format (e.g., 11:11:04 PM). Not all locales have such /// a format, in which case we fall back to a 24-hour clock (`%X`). /// /// See the [`format::strftime` module](crate::format::strftime) for all supported formatting /// specifiers. /// /// [`DateTime::format_localized_with_items`]: crate::DateTime::format_localized_with_items /// /// # Errors /// /// While iterating [`Item::Error`] will be returned if the format string contains an invalid /// or unrecognized formatting specifier. /// /// # Example /// /// ``` /// # #[cfg(feature = "alloc")] { /// use chrono::format::{Locale, StrftimeItems}; /// use chrono::{FixedOffset, TimeZone}; /// /// let dt = FixedOffset::east_opt(9 * 60 * 60) /// .unwrap() /// .with_ymd_and_hms(2023, 7, 11, 0, 34, 59) /// .unwrap(); /// /// // Note: you usually want to combine `StrftimeItems::new_with_locale` with other /// // locale-aware methods such as `DateTime::format_localized_with_items`. /// // We use the regular `format_with_items` to show only how the formatting changes. /// /// let fmtr = dt.format_with_items(StrftimeItems::new_with_locale("%x", Locale::en_US)); /// assert_eq!(fmtr.to_string(), "07/11/2023"); /// let fmtr = dt.format_with_items(StrftimeItems::new_with_locale("%x", Locale::ko_KR)); /// assert_eq!(fmtr.to_string(), "2023년 07월 11일"); /// let fmtr = dt.format_with_items(StrftimeItems::new_with_locale("%x", Locale::ja_JP)); /// assert_eq!(fmtr.to_string(), "2023年07月11日"); /// # } /// ``` #[cfg(feature = "unstable-locales")] #[must_use] pubconstfn new_with_locale(s: &'a str, locale: Locale) -> StrftimeItems<'a> {
StrftimeItems {
remainder: s,
queue: &[],
lenient: false,
locale_str: "",
locale: Some(locale),
}
}
/// Parse format string into a `Vec` of formatting [`Item`]'s. /// /// If you need to format or parse multiple values with the same format string, it is more /// efficient to convert it to a `Vec` of formatting [`Item`]'s than to re-parse the format /// string on every use. /// /// The `format_with_items` methods on [`DateTime`], [`NaiveDateTime`], [`NaiveDate`] and /// [`NaiveTime`] accept the result for formatting. [`format::parse()`] can make use of it for /// parsing. /// /// [`DateTime`]: crate::DateTime::format_with_items /// [`NaiveDateTime`]: crate::NaiveDateTime::format_with_items /// [`NaiveDate`]: crate::NaiveDate::format_with_items /// [`NaiveTime`]: crate::NaiveTime::format_with_items /// [`format::parse()`]: crate::format::parse() /// /// # Errors /// /// Returns an error if the format string contains an invalid or unrecognized formatting /// specifier and the [`StrftimeItems`] wasn't constructed with [`new_lenient`][Self::new_lenient]. /// /// # Example /// /// ``` /// use chrono::format::{parse, Parsed, StrftimeItems}; /// use chrono::NaiveDate; /// /// let fmt_items = StrftimeItems::new("%e %b %Y %k.%M").parse()?; /// let datetime = NaiveDate::from_ymd_opt(2023, 7, 11).unwrap().and_hms_opt(9, 0, 0).unwrap(); /// /// // Formatting /// assert_eq!( /// datetime.format_with_items(fmt_items.as_slice().iter()).to_string(), /// "11 Jul 2023 9.00" /// ); /// /// // Parsing /// let mut parsed = Parsed::new(); /// parse(&mut parsed, "11 Jul 2023 9.00", fmt_items.as_slice().iter())?; /// let parsed_dt = parsed.to_naive_datetime_with_offset(0)?; /// assert_eq!(parsed_dt, datetime); /// # Ok::<(), chrono::ParseError>(()) /// ``` #[cfg(any(feature = "alloc", feature = "std"))] pubfn parse(self) -> Result<Vec<Item<'a>>, ParseError> { self.into_iter()
.map(|item| match item == Item::Error { false => Ok(item), true => Err(BAD_FORMAT),
})
.collect()
}
/// Parse format string into a `Vec` of [`Item`]'s that contain no references to slices of the /// format string. /// /// A `Vec` created with [`StrftimeItems::parse`] contains references to the format string, /// binding the lifetime of the `Vec` to that string. [`StrftimeItems::parse_to_owned`] will /// convert the references to owned types. /// /// # Errors /// /// Returns an error if the format string contains an invalid or unrecognized formatting /// specifier and the [`StrftimeItems`] wasn't constructed with [`new_lenient`][Self::new_lenient]. /// /// # Example /// /// ``` /// use chrono::format::{Item, ParseError, StrftimeItems}; /// use chrono::NaiveDate; /// /// fn format_items(date_fmt: &str, time_fmt: &str) -> Result<Vec<Item<'static>>, ParseError> { /// // `fmt_string` is dropped at the end of this function. /// let fmt_string = format!("{} {}", date_fmt, time_fmt); /// StrftimeItems::new(&fmt_string).parse_to_owned() /// } /// /// let fmt_items = format_items("%e %b %Y", "%k.%M")?; /// let datetime = NaiveDate::from_ymd_opt(2023, 7, 11).unwrap().and_hms_opt(9, 0, 0).unwrap(); /// /// assert_eq!( /// datetime.format_with_items(fmt_items.as_slice().iter()).to_string(), /// "11 Jul 2023 9.00" /// ); /// # Ok::<(), ParseError>(()) /// ``` #[cfg(any(feature = "alloc", feature = "std"))] pubfn parse_to_owned(self) -> Result<Vec<Item<'static>>, ParseError> { self.into_iter()
.map(|item| match item == Item::Error { false => Ok(item.to_owned()), true => Err(BAD_FORMAT),
})
.collect()
}
fn parse_next_item(&mutself, mut remainder: &'a str) -> Option<(&'a str, Item<'a>)> { use InternalInternal::*; use Item::{Literal, Space}; use Numeric::*;
let (original, mut remainder) = match remainder.chars().next()? { // the next item is a specifier '%' => (remainder, &remainder[1..]),
// the next item is space
c if c.is_whitespace() => { // `%` is not a whitespace, so `c != '%'` is redundant let nextspec =
remainder.find(|c: char| !c.is_whitespace()).unwrap_or(remainder.len());
assert!(nextspec > 0); let item = Space(&remainder[..nextspec]);
remainder = &remainder[nextspec..]; return Some((remainder, item));
}
// the next item is literal
_ => { let nextspec = remainder
.find(|c: char| c.is_whitespace() || c == '%')
.unwrap_or(remainder.len());
assert!(nextspec > 0); let item = Literal(&remainder[..nextspec]);
remainder = &remainder[nextspec..]; return Some((remainder, item));
}
};
macro_rules! next {
() => { match remainder.chars().next() {
Some(x) => {
remainder = &remainder[x.len_utf8()..];
x
}
None => return Some((remainder, self.error(original, remainder))), // premature end of string
}
};
}
let spec = next!(); let pad_override = match spec { '-' => Some(Pad::None), '0' => Some(Pad::Zero), '_' => Some(Pad::Space),
_ => None,
};
let is_alternate = spec == '#'; let spec = if pad_override.is_some() || is_alternate { next!() } else { spec }; if is_alternate && !HAVE_ALTERNATES.contains(spec) { return Some((remainder, self.error(original, remainder)));
}
impl<'a> Iterator for StrftimeItems<'a> { type Item = Item<'a>;
fn next(&mutself) -> Option<Item<'a>> { // We have items queued to return from a specifier composed of multiple formatting items. iflet Some((item, remainder)) = self.queue.split_first() { self.queue = remainder; return Some(item.clone());
}
// We are in the middle of parsing the localized formatting string of a specifier. #[cfg(feature = "unstable-locales")] if !self.locale_str.is_empty() { let (remainder, item) = self.parse_next_item(self.locale_str)?; self.locale_str = remainder; return Some(item);
}
// Normal: we are parsing the formatting string. let (remainder, item) = self.parse_next_item(self.remainder)?; self.remainder = remainder;
Some(item)
}
}
/// Ensure parsing a timestamp with the parse-only stftime formatter "%#z" does /// not cause a panic. /// /// See <https://github.com/chronotope/chrono/issues/1139>. #[test] #[cfg(feature = "alloc")] fn test_parse_only_timezone_offset_permissive_no_panic() { usecrate::NaiveDate; usecrate::{FixedOffset, TimeZone}; use std::fmt::Write;
/// Regression test for https://github.com/chronotope/chrono/issues/1725 #[test] #[cfg(any(feature = "alloc", feature = "std"))] fn test_finite() { letmut i = 0; for item in StrftimeItems::new("%2f") {
println!("{:?}", item);
i += 1; if i > 10 {
panic!("infinite loop");
}
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.34 Sekunden
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.