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

Optimize parsing #358

Merged
merged 9 commits into from Nov 24, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,14 @@ Chrono obeys the principle of [Semantic Versioning](http://semver.org/).
There were/are numerous minor versions before 1.0 due to the language changes.
Versions with only mechanical changes will be omitted from the following list.

## next

### Features

* Functions that were accepting `Iterator` of `Item`s (for example
`format_with_items`) now accept `Iterator` of `Borrow<Item>`, so one can
use values or references.

## 0.4.9

### Fixes
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -29,6 +29,7 @@ alloc = []
std = []
clock = ["time", "std"]
wasmbind = ["wasm-bindgen", "js-sys"]
bench = ["std"]

[dependencies]
time = { version = "0.1.39", optional = true }
Expand Down
5 changes: 3 additions & 2 deletions src/date.rs
Expand Up @@ -3,6 +3,7 @@

//! ISO 8601 calendar date with time zone.

use core::borrow::Borrow;
use core::{fmt, hash};
use core::cmp::Ordering;
use core::ops::{Add, Sub};
Expand Down Expand Up @@ -258,8 +259,8 @@ impl<Tz: TimeZone> Date<Tz> where Tz::Offset: fmt::Display {
/// Formats the date with the specified formatting items.
#[cfg(any(feature = "alloc", feature = "std", test))]
#[inline]
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone {
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items)
}

Expand Down
86 changes: 68 additions & 18 deletions src/datetime.rs
Expand Up @@ -25,6 +25,7 @@ use format::{Item, Numeric, Pad, Fixed};
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
#[cfg(any(feature = "alloc", feature = "std", test))]
use format::DelayedFormat;
use core::borrow::Borrow;

/// Specific formatting options for seconds. This may be extended in the
/// future, so exhaustive matching in external code is not recommended.
Expand Down Expand Up @@ -326,7 +327,7 @@ impl DateTime<FixedOffset> {
pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
try!(parse(&mut parsed, s, ITEMS.iter()));
parsed.to_datetime()
}

Expand All @@ -338,7 +339,7 @@ impl DateTime<FixedOffset> {
pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
try!(parse(&mut parsed, s, ITEMS.iter()));
parsed.to_datetime()
}

Expand Down Expand Up @@ -374,14 +375,14 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
#[cfg(any(feature = "alloc", feature = "std", test))]
pub fn to_rfc2822(&self) -> String {
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
self.format_with_items(ITEMS.iter().cloned()).to_string()
self.format_with_items(ITEMS.iter()).to_string()
}

/// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`.
#[cfg(any(feature = "alloc", feature = "std", test))]
pub fn to_rfc3339(&self) -> String {
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
self.format_with_items(ITEMS.iter().cloned()).to_string()
self.format_with_items(ITEMS.iter()).to_string()
}

/// Return an RFC 3339 and ISO 8601 date and time string with subseconds
Expand Down Expand Up @@ -450,20 +451,20 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
match ssitem {
None =>
self.format_with_items(
PREFIX.iter().chain([tzitem].iter()).cloned()
PREFIX.iter().chain([tzitem].iter())
).to_string(),
Some(s) =>
self.format_with_items(
PREFIX.iter().chain([s, tzitem].iter()).cloned()
PREFIX.iter().chain([s, tzitem].iter())
).to_string(),
}
}

/// Formats the combined date and time with the specified formatting items.
#[cfg(any(feature = "alloc", feature = "std", test))]
#[inline]
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone {
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
let local = self.naive_local();
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items)
}
Expand Down Expand Up @@ -621,24 +622,24 @@ impl str::FromStr for DateTime<FixedOffset> {

fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &'static [Item<'static>] = &[
Item::Space(""), Item::Numeric(Numeric::Year, Pad::Zero),
Item::Numeric(Numeric::Year, Pad::Zero),
Item::Space(""), Item::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero),
Item::Numeric(Numeric::Month, Pad::Zero),
Item::Space(""), Item::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Day, Pad::Zero),
Item::Numeric(Numeric::Day, Pad::Zero),
Item::Space(""), Item::Literal("T"), // XXX shouldn't this be case-insensitive?
Item::Space(""), Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Space(""), Item::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Space(""), Item::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero),
Item::Numeric(Numeric::Second, Pad::Zero),
Item::Fixed(Fixed::Nanosecond),
Item::Space(""), Item::Fixed(Fixed::TimezoneOffsetZ),
Item::Space(""),
];

let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
try!(parse(&mut parsed, s, ITEMS.iter()));
parsed.to_datetime()
}
}
Expand Down Expand Up @@ -1140,7 +1141,7 @@ pub mod serde {
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_nanoseconds_option {
use std::fmt;
use core::fmt;
use serdelib::{ser, de};

use {DateTime, Utc};
Expand Down Expand Up @@ -1431,7 +1432,7 @@ pub mod serde {
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_milliseconds_option {
use std::fmt;
use core::fmt;
use serdelib::{ser, de};

use {DateTime, Utc};
Expand Down Expand Up @@ -1718,7 +1719,7 @@ pub mod serde {
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_seconds_option {
use std::fmt;
use core::fmt;
use serdelib::{ser, de};

use {DateTime, Utc};
Expand Down Expand Up @@ -2213,4 +2214,53 @@ mod tests {
assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd));
assert_eq!(format!(" {} ", ymd_formatted), format!("{:^17}", ymd));
}

#[cfg(feature = "bench")]
#[bench]
fn bench_datetime_parse_from_rfc2822(bh: &mut test::Bencher) {
bh.iter(|| {
let str = test::black_box("Wed, 18 Feb 2015 23:16:09 +0000");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wild, I did not realize that it's possible to use str as a variable name. I always just use s. (this is fine, just surprising to me.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize when I wrote it, but yeah, it is strange as identifier.

DateTime::parse_from_rfc2822(str).unwrap()
});
}

#[cfg(feature = "bench")]
#[bench]
fn bench_datetime_parse_from_rfc3339(bh: &mut test::Bencher) {
bh.iter(|| {
let str = test::black_box("2015-02-18T23:59:60.234567+05:00");
DateTime::parse_from_rfc3339(str).unwrap()
});
}

#[cfg(feature = "bench")]
#[bench]
fn bench_datetime_from_str(bh: &mut test::Bencher) {
use std::str::FromStr;

bh.iter(|| {
let str = test::black_box("2019-03-30T18:46:57.193Z");
DateTime::<Utc>::from_str(str).unwrap()
});
}

#[cfg(feature = "bench")]
#[bench]
fn bench_datetime_to_rfc2822(bh: &mut test::Bencher) {
let pst = FixedOffset::east(8 * 60 * 60);
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000);
bh.iter(|| {
test::black_box(dt).to_rfc2822()
});
}

#[cfg(feature = "bench")]
#[bench]
fn bench_datetime_to_rfc3339(bh: &mut test::Bencher) {
let pst = FixedOffset::east(8 * 60 * 60);
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000);
bh.iter(|| {
test::black_box(dt).to_rfc3339()
});
}
}