diff --git a/bench/benches/chrono.rs b/bench/benches/chrono.rs index 925c2939f1..91ca557307 100644 --- a/bench/benches/chrono.rs +++ b/bench/benches/chrono.rs @@ -185,6 +185,33 @@ fn bench_format_with_items(c: &mut Criterion) { }); } +fn benches_delayed_format(c: &mut Criterion) { + let mut group = c.benchmark_group("delayed_format"); + let dt = Local::now(); + group.bench_function(BenchmarkId::new("with_display", &dt), |b| { + b.iter_batched( + || dt.format("%Y-%m-%dT%H:%M:%S%.f%:z"), + |df| black_box(df).to_string(), + criterion::BatchSize::SmallInput, + ) + }); + group.bench_function(BenchmarkId::new("with_string_buffer", &dt), |b| { + b.iter_batched( + || (dt.format("%Y-%m-%dT%H:%M:%S%.f%:z"), String::with_capacity(256)), + |(df, string)| black_box(df).format_into(&mut black_box(string)), + criterion::BatchSize::SmallInput, + ) + }); + group.bench_function(BenchmarkId::new("with_vec_buffer", &dt), |b| { + b.iter_batched( + || (dt.format("%Y-%m-%dT%H:%M:%S%.f%:z"), String::with_capacity(256)), + |(df, string)| black_box(df).format_into(&mut black_box(string)), + criterion::BatchSize::SmallInput, + ) + }); + group.finish(); +} + fn bench_format_manual(c: &mut Criterion) { let dt = Local::now(); c.bench_function("bench_format_manual", |b| { @@ -237,6 +264,7 @@ criterion_group!( bench_format, bench_format_with_items, bench_format_manual, + benches_delayed_format, bench_naivedate_add_signed, bench_datetime_with, ); diff --git a/src/format/formatting.rs b/src/format/formatting.rs index 967f2d3a68..61a17dbdc1 100644 --- a/src/format/formatting.rs +++ b/src/format/formatting.rs @@ -97,7 +97,34 @@ impl<'a, I: Iterator + Clone, B: Borrow>> DelayedFormat { DelayedFormat { date, time, off: Some(name_and_diff), items, locale } } - fn format(&self, w: &mut impl Write) -> fmt::Result { + + /// Formats `DelayedFormat` into an `std::io::Write` instance. + /// # Errors + /// This function returns an error if formatting into the `std::io::Write` instance fails. + #[cfg(feature = "std")] + pub fn format_into_io( + self, + w: &mut impl std::io::Write, + ) -> fmt::Result { + // wrapper to allow reuse of the existing string based + // writers + struct IoWriter { + writer: W, + } + impl fmt::Write for IoWriter { + #[inline] + fn write_str(&mut self, s: &str) -> fmt::Result { + self.writer.write_all(s.as_bytes()).map_err(|_| fmt::Error) + } + } + let mut writer = IoWriter{ writer: w }; + self.format_into(&mut writer) + } + + /// Formats `DelayedFormat` into a `core::fmt::Write` instance. + /// # Errors + /// This function returns an error if formatting into the `core::fmt::Write` instance fails. + pub fn format_into(&self, w: &mut impl Write) -> fmt::Result { for item in self.items.clone() { match *item.borrow() { Item::Literal(s) | Item::Space(s) => w.write_str(s), @@ -321,7 +348,7 @@ impl<'a, I: Iterator + Clone, B: Borrow>> DelayedFormat { impl<'a, I: Iterator + Clone, B: Borrow>> Display for DelayedFormat { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut result = String::new(); - self.format(&mut result)?; + self.format_into(&mut result)?; f.pad(&result) } } @@ -353,7 +380,7 @@ where /// Formats single formatting item #[cfg(feature = "alloc")] -#[deprecated(since = "0.4.32", note = "Use DelayedFormat::fmt instead")] +#[deprecated(since = "0.4.32", note = "Use DelayedFormat::fmt or DelayedFormat::format instead")] pub fn format_item( w: &mut fmt::Formatter, date: Option<&NaiveDate>, @@ -609,7 +636,41 @@ mod tests { use super::{Colons, OffsetFormat, OffsetPrecision, Pad}; use crate::FixedOffset; #[cfg(feature = "alloc")] - use crate::{NaiveDate, NaiveTime, TimeZone, Timelike, Utc}; + use crate::{NaiveDate, NaiveTime, TimeZone, Timelike, Utc, DateTime}; + + #[cfg(feature = "std")] + #[test] + fn test_delayed_format_into_io_matches_format_into() { + let dt = DateTime::from_timestamp(1643723400, 123456789).unwrap(); + let df = dt.format("%Y-%m-%d %H:%M:%S%.9f"); + + let mut dt_str = String::new(); + let mut dt_vec_str = Vec::new(); + + df.format_into(&mut dt_str).unwrap(); + df.format_into_io(&mut dt_vec_str).unwrap(); + + assert_eq!(dt_str, String::from_utf8(dt_vec_str).unwrap()); + assert_eq!(dt_str, "2022-02-01 13:50:00.123456789"); + } + + #[cfg(all(feature = "std", feature = "unstable-locales", feature = "alloc"))] + #[test] + fn test_with_locale_delayed_format_into_io_matches_format_into() { + use crate::format::locales::Locale; + + let dt = DateTime::from_timestamp(1643723400, 123456789).unwrap(); + let df = dt.format_localized("%A, %B %d, %Y", Locale::ja_JP); + + let mut dt_str = String::new(); + let mut dt_vec_str = Vec::new(); + + df.format_into(&mut dt_str).unwrap(); + df.format_into_io(&mut dt_vec_str).unwrap(); + + assert_eq!(dt_str, String::from_utf8(dt_vec_str).unwrap()); + assert_eq!(dt_str, "火曜日, 2月 01, 2022"); + } #[test] #[cfg(feature = "alloc")]