Merge pull request #358 from michalsrb/optimize-parsing

Optimize parsing
This commit is contained in:
Brandon W Maister 2019-11-23 19:09:12 -05:00 committed by GitHub
commit 46f8267c61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 270 additions and 166 deletions

View File

@ -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. 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. 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 ## 0.4.9
### Fixes ### Fixes

View File

@ -29,6 +29,7 @@ alloc = []
std = [] std = []
clock = ["time", "std"] clock = ["time", "std"]
wasmbind = ["wasm-bindgen", "js-sys"] wasmbind = ["wasm-bindgen", "js-sys"]
bench = ["std"]
[dependencies] [dependencies]
time = { version = "0.1.39", optional = true } time = { version = "0.1.39", optional = true }

View File

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

View File

@ -25,6 +25,7 @@ use format::{Item, Numeric, Pad, Fixed};
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems}; use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
use format::DelayedFormat; use format::DelayedFormat;
use core::borrow::Borrow;
/// Specific formatting options for seconds. This may be extended in the /// Specific formatting options for seconds. This may be extended in the
/// future, so exhaustive matching in external code is not recommended. /// future, so exhaustive matching in external code is not recommended.
@ -326,7 +327,7 @@ impl DateTime<FixedOffset> {
pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> { pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned())); try!(parse(&mut parsed, s, ITEMS.iter()));
parsed.to_datetime() parsed.to_datetime()
} }
@ -338,7 +339,7 @@ impl DateTime<FixedOffset> {
pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> { pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned())); try!(parse(&mut parsed, s, ITEMS.iter()));
parsed.to_datetime() parsed.to_datetime()
} }
@ -374,14 +375,14 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
pub fn to_rfc2822(&self) -> String { pub fn to_rfc2822(&self) -> String {
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; 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`. /// 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))] #[cfg(any(feature = "alloc", feature = "std", test))]
pub fn to_rfc3339(&self) -> String { pub fn to_rfc3339(&self) -> String {
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; 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 /// Return an RFC 3339 and ISO 8601 date and time string with subseconds
@ -450,11 +451,11 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
match ssitem { match ssitem {
None => None =>
self.format_with_items( self.format_with_items(
PREFIX.iter().chain([tzitem].iter()).cloned() PREFIX.iter().chain([tzitem].iter())
).to_string(), ).to_string(),
Some(s) => Some(s) =>
self.format_with_items( self.format_with_items(
PREFIX.iter().chain([s, tzitem].iter()).cloned() PREFIX.iter().chain([s, tzitem].iter())
).to_string(), ).to_string(),
} }
} }
@ -462,8 +463,8 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
/// Formats the combined date and time with the specified formatting items. /// Formats the combined date and time with the specified formatting items.
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
#[inline] #[inline]
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I> pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone { where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
let local = self.naive_local(); let local = self.naive_local();
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items) DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items)
} }
@ -621,24 +622,24 @@ impl str::FromStr for DateTime<FixedOffset> {
fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> { fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &'static [Item<'static>] = &[ 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::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero), Item::Numeric(Numeric::Month, Pad::Zero),
Item::Space(""), Item::Literal("-"), 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::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::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero), Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Space(""), Item::Literal(":"), Item::Space(""), Item::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero), Item::Numeric(Numeric::Second, Pad::Zero),
Item::Fixed(Fixed::Nanosecond), Item::Fixed(Fixed::Nanosecond),
Item::Space(""), Item::Fixed(Fixed::TimezoneOffsetZ), Item::Space(""), Item::Fixed(Fixed::TimezoneOffsetZ),
Item::Space(""), Item::Space(""),
]; ];
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned())); try!(parse(&mut parsed, s, ITEMS.iter()));
parsed.to_datetime() parsed.to_datetime()
} }
} }
@ -1140,7 +1141,7 @@ pub mod serde {
/// # fn main() { example().unwrap(); } /// # fn main() { example().unwrap(); }
/// ``` /// ```
pub mod ts_nanoseconds_option { pub mod ts_nanoseconds_option {
use std::fmt; use core::fmt;
use serdelib::{ser, de}; use serdelib::{ser, de};
use {DateTime, Utc}; use {DateTime, Utc};
@ -1431,7 +1432,7 @@ pub mod serde {
/// # fn main() { example().unwrap(); } /// # fn main() { example().unwrap(); }
/// ``` /// ```
pub mod ts_milliseconds_option { pub mod ts_milliseconds_option {
use std::fmt; use core::fmt;
use serdelib::{ser, de}; use serdelib::{ser, de};
use {DateTime, Utc}; use {DateTime, Utc};
@ -1718,7 +1719,7 @@ pub mod serde {
/// # fn main() { example().unwrap(); } /// # fn main() { example().unwrap(); }
/// ``` /// ```
pub mod ts_seconds_option { pub mod ts_seconds_option {
use std::fmt; use core::fmt;
use serdelib::{ser, de}; use serdelib::{ser, de};
use {DateTime, Utc}; use {DateTime, Utc};
@ -2213,4 +2214,53 @@ mod tests {
assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd)); assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd));
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");
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()
});
}
} }

View File

@ -17,6 +17,7 @@
#![allow(ellipsis_inclusive_range_patterns)] #![allow(ellipsis_inclusive_range_patterns)]
use core::borrow::Borrow;
use core::fmt; use core::fmt;
use core::str::FromStr; use core::str::FromStr;
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
@ -356,14 +357,14 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
/// Tries to format given arguments with given formatting items. /// Tries to format given arguments with given formatting items.
/// Internally used by `DelayedFormat`. /// Internally used by `DelayedFormat`.
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
pub fn format<'a, I>( pub fn format<'a, I, B>(
w: &mut fmt::Formatter, w: &mut fmt::Formatter,
date: Option<&NaiveDate>, date: Option<&NaiveDate>,
time: Option<&NaiveTime>, time: Option<&NaiveTime>,
off: Option<&(String, FixedOffset)>, off: Option<&(String, FixedOffset)>,
items: I, items: I,
) -> fmt::Result ) -> fmt::Result
where I: Iterator<Item=Item<'a>> where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>
{ {
// full and abbreviated month and weekday names // full and abbreviated month and weekday names
static SHORT_MONTHS: [&'static str; 12] = static SHORT_MONTHS: [&'static str; 12] =
@ -380,12 +381,12 @@ pub fn format<'a, I>(
let mut result = String::new(); let mut result = String::new();
for item in items { for item in items {
match item { match item.borrow() {
Item::Literal(s) | Item::Space(s) => result.push_str(s), &Item::Literal(s) | &Item::Space(s) => result.push_str(s),
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => result.push_str(s), &Item::OwnedLiteral(ref s) | &Item::OwnedSpace(ref s) => result.push_str(s),
Item::Numeric(spec, pad) => { &Item::Numeric(ref spec, ref pad) => {
use self::Numeric::*; use self::Numeric::*;
let week_from_sun = |d: &NaiveDate| let week_from_sun = |d: &NaiveDate|
@ -394,31 +395,31 @@ pub fn format<'a, I>(
(d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7; (d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7;
let (width, v) = match spec { let (width, v) = match spec {
Year => (4, date.map(|d| i64::from(d.year()))), &Year => (4, date.map(|d| i64::from(d.year()))),
YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))), &YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))),
YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))), &YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))),
IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))), &IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))),
IsoYearDiv100 => (2, date.map(|d| div_floor( &IsoYearDiv100 => (2, date.map(|d| div_floor(
i64::from(d.iso_week().year()), 100))), i64::from(d.iso_week().year()), 100))),
IsoYearMod100 => (2, date.map(|d| mod_floor( &IsoYearMod100 => (2, date.map(|d| mod_floor(
i64::from(d.iso_week().year()), 100))), i64::from(d.iso_week().year()), 100))),
Month => (2, date.map(|d| i64::from(d.month()))), &Month => (2, date.map(|d| i64::from(d.month()))),
Day => (2, date.map(|d| i64::from(d.day()))), &Day => (2, date.map(|d| i64::from(d.day()))),
WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))), &WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))), &WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))), &IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday() &NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday()
.num_days_from_sunday()))), .num_days_from_sunday()))),
WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday() &WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday()
.number_from_monday()))), .number_from_monday()))),
Ordinal => (3, date.map(|d| i64::from(d.ordinal()))), &Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
Hour => (2, time.map(|t| i64::from(t.hour()))), &Hour => (2, time.map(|t| i64::from(t.hour()))),
Hour12 => (2, time.map(|t| i64::from(t.hour12().1))), &Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
Minute => (2, time.map(|t| i64::from(t.minute()))), &Minute => (2, time.map(|t| i64::from(t.minute()))),
Second => (2, time.map(|t| i64::from(t.second() + &Second => (2, time.map(|t| i64::from(t.second() +
t.nanosecond() / 1_000_000_000))), t.nanosecond() / 1_000_000_000))),
Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))), &Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
Timestamp => (1, match (date, time, off) { &Timestamp => (1, match (date, time, off) {
(Some(d), Some(t), None) => (Some(d), Some(t), None) =>
Some(d.and_time(*t).timestamp()), Some(d.and_time(*t).timestamp()),
(Some(d), Some(t), Some(&(_, off))) => (Some(d), Some(t), Some(&(_, off))) =>
@ -427,24 +428,24 @@ pub fn format<'a, I>(
}), }),
// for the future expansion // for the future expansion
Internal(ref int) => match int._dummy {}, &Internal(ref int) => match int._dummy {},
}; };
if let Some(v) = v { if let Some(v) = v {
try!( try!(
if (spec == Year || spec == IsoYear) && !(0 <= v && v < 10_000) { if (spec == &Year || spec == &IsoYear) && !(0 <= v && v < 10_000) {
// non-four-digit years require an explicit sign as per ISO 8601 // non-four-digit years require an explicit sign as per ISO 8601
match pad { match pad {
Pad::None => write!(result, "{:+}", v), &Pad::None => write!(result, "{:+}", v),
Pad::Zero => write!(result, "{:+01$}", v, width + 1), &Pad::Zero => write!(result, "{:+01$}", v, width + 1),
Pad::Space => write!(result, "{:+1$}", v, width + 1), &Pad::Space => write!(result, "{:+1$}", v, width + 1),
} }
} else { } else {
match pad { match pad {
Pad::None => write!(result, "{}", v), &Pad::None => write!(result, "{}", v),
Pad::Zero => write!(result, "{:01$}", v, width), &Pad::Zero => write!(result, "{:01$}", v, width),
Pad::Space => write!(result, "{:1$}", v, width), &Pad::Space => write!(result, "{:1$}", v, width),
} }
} }
) )
@ -453,7 +454,7 @@ pub fn format<'a, I>(
} }
}, },
Item::Fixed(spec) => { &Item::Fixed(ref spec) => {
use self::Fixed::*; use self::Fixed::*;
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`. /// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
@ -479,41 +480,41 @@ pub fn format<'a, I>(
} }
let ret = match spec { let ret = match spec {
ShortMonthName => &ShortMonthName =>
date.map(|d| { date.map(|d| {
result.push_str(SHORT_MONTHS[d.month0() as usize]); result.push_str(SHORT_MONTHS[d.month0() as usize]);
Ok(()) Ok(())
}), }),
LongMonthName => &LongMonthName =>
date.map(|d| { date.map(|d| {
result.push_str(LONG_MONTHS[d.month0() as usize]); result.push_str(LONG_MONTHS[d.month0() as usize]);
Ok(()) Ok(())
}), }),
ShortWeekdayName => &ShortWeekdayName =>
date.map(|d| { date.map(|d| {
result.push_str( result.push_str(
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize] SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
); );
Ok(()) Ok(())
}), }),
LongWeekdayName => &LongWeekdayName =>
date.map(|d| { date.map(|d| {
result.push_str( result.push_str(
LONG_WEEKDAYS[d.weekday().num_days_from_monday() as usize] LONG_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
); );
Ok(()) Ok(())
}), }),
LowerAmPm => &LowerAmPm =>
time.map(|t| { time.map(|t| {
result.push_str(if t.hour12().0 {"pm"} else {"am"}); result.push_str(if t.hour12().0 {"pm"} else {"am"});
Ok(()) Ok(())
}), }),
UpperAmPm => &UpperAmPm =>
time.map(|t| { time.map(|t| {
result.push_str(if t.hour12().0 {"PM"} else {"AM"}); result.push_str(if t.hour12().0 {"PM"} else {"AM"});
Ok(()) Ok(())
}), }),
Nanosecond => &Nanosecond =>
time.map(|t| { time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000; let nano = t.nanosecond() % 1_000_000_000;
if nano == 0 { if nano == 0 {
@ -526,52 +527,52 @@ pub fn format<'a, I>(
write!(result, ".{:09}", nano) write!(result, ".{:09}", nano)
} }
}), }),
Nanosecond3 => &Nanosecond3 =>
time.map(|t| { time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000; let nano = t.nanosecond() % 1_000_000_000;
write!(result, ".{:03}", nano / 1_000_000) write!(result, ".{:03}", nano / 1_000_000)
}), }),
Nanosecond6 => &Nanosecond6 =>
time.map(|t| { time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000; let nano = t.nanosecond() % 1_000_000_000;
write!(result, ".{:06}", nano / 1_000) write!(result, ".{:06}", nano / 1_000)
}), }),
Nanosecond9 => &Nanosecond9 =>
time.map(|t| { time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000; let nano = t.nanosecond() % 1_000_000_000;
write!(result, ".{:09}", nano) write!(result, ".{:09}", nano)
}), }),
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => &Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) =>
time.map(|t| { time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000; let nano = t.nanosecond() % 1_000_000_000;
write!(result, "{:03}", nano / 1_000_000) write!(result, "{:03}", nano / 1_000_000)
}), }),
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => &Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) =>
time.map(|t| { time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000; let nano = t.nanosecond() % 1_000_000_000;
write!(result, "{:06}", nano / 1_000) write!(result, "{:06}", nano / 1_000)
}), }),
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => &Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) =>
time.map(|t| { time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000; let nano = t.nanosecond() % 1_000_000_000;
write!(result, "{:09}", nano) write!(result, "{:09}", nano)
}), }),
TimezoneName => &TimezoneName =>
off.map(|&(ref name, _)| { off.map(|&(ref name, _)| {
result.push_str(name); result.push_str(name);
Ok(()) Ok(())
}), }),
TimezoneOffsetColon => &TimezoneOffsetColon =>
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, true)), off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, true)),
TimezoneOffsetColonZ => &TimezoneOffsetColonZ =>
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, true)), off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, true)),
TimezoneOffset => &TimezoneOffset =>
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, false)), off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, false)),
TimezoneOffsetZ => &TimezoneOffsetZ =>
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, false)), off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, false)),
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => &Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) =>
panic!("Do not try to write %#z it is undefined"), panic!("Do not try to write %#z it is undefined"),
RFC2822 => // same to `%a, %e %b %Y %H:%M:%S %z` &RFC2822 => // same to `%a, %e %b %Y %H:%M:%S %z`
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
let sec = t.second() + t.nanosecond() / 1_000_000_000; let sec = t.second() + t.nanosecond() / 1_000_000_000;
try!(write!( try!(write!(
@ -585,7 +586,7 @@ pub fn format<'a, I>(
} else { } else {
None None
}, },
RFC3339 => // same to `%Y-%m-%dT%H:%M:%S%.f%:z` &RFC3339 => // same to `%Y-%m-%dT%H:%M:%S%.f%:z`
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
// reuse `Debug` impls which already print ISO 8601 format. // reuse `Debug` impls which already print ISO 8601 format.
// this is faster in this way. // this is faster in this way.
@ -602,7 +603,7 @@ pub fn format<'a, I>(
} }
}, },
Item::Error => return Err(fmt::Error), &Item::Error => return Err(fmt::Error),
} }
} }
@ -633,7 +634,7 @@ pub struct DelayedFormat<I> {
} }
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
impl<'a, I: Iterator<Item=Item<'a>> + Clone> DelayedFormat<I> { impl<'a, I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
/// Makes a new `DelayedFormat` value out of local date and time. /// Makes a new `DelayedFormat` value out of local date and time.
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> { pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
DelayedFormat { date: date, time: time, off: None, items: items } DelayedFormat { date: date, time: time, off: None, items: items }
@ -649,7 +650,7 @@ impl<'a, I: Iterator<Item=Item<'a>> + Clone> DelayedFormat<I> {
} }
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
impl<'a, I: Iterator<Item=Item<'a>> + Clone> fmt::Display for DelayedFormat<I> { impl<'a, I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>> fmt::Display for DelayedFormat<I> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone()) format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone())
} }

View File

@ -6,6 +6,7 @@
#![allow(deprecated)] #![allow(deprecated)]
use core::borrow::Borrow;
use core::usize; use core::usize;
use Weekday; use Weekday;
@ -204,64 +205,64 @@ fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
/// so one can prepend any number of whitespace then any number of zeroes before numbers. /// so one can prepend any number of whitespace then any number of zeroes before numbers.
/// ///
/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`. /// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<()> pub fn parse<'a, I, B>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<()>
where I: Iterator<Item=Item<'a>> { where I: Iterator<Item=B>, B: Borrow<Item<'a>> {
macro_rules! try_consume { macro_rules! try_consume {
($e:expr) => ({ let (s_, v) = try!($e); s = s_; v }) ($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
} }
for item in items { for item in items {
match item { match item.borrow() {
Item::Literal(prefix) => { &Item::Literal(prefix) => {
if s.len() < prefix.len() { return Err(TOO_SHORT); } if s.len() < prefix.len() { return Err(TOO_SHORT); }
if !s.starts_with(prefix) { return Err(INVALID); } if !s.starts_with(prefix) { return Err(INVALID); }
s = &s[prefix.len()..]; s = &s[prefix.len()..];
} }
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
Item::OwnedLiteral(ref prefix) => { &Item::OwnedLiteral(ref prefix) => {
if s.len() < prefix.len() { return Err(TOO_SHORT); } if s.len() < prefix.len() { return Err(TOO_SHORT); }
if !s.starts_with(&prefix[..]) { return Err(INVALID); } if !s.starts_with(&prefix[..]) { return Err(INVALID); }
s = &s[prefix.len()..]; s = &s[prefix.len()..];
} }
Item::Space(_) => { &Item::Space(_) => {
s = s.trim_left(); s = s.trim_left();
} }
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
Item::OwnedSpace(_) => { &Item::OwnedSpace(_) => {
s = s.trim_left(); s = s.trim_left();
} }
Item::Numeric(spec, _pad) => { &Item::Numeric(ref spec, ref _pad) => {
use super::Numeric::*; use super::Numeric::*;
type Setter = fn(&mut Parsed, i64) -> ParseResult<()>; type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;
let (width, signed, set): (usize, bool, Setter) = match spec { let (width, signed, set): (usize, bool, Setter) = match spec {
Year => (4, true, Parsed::set_year), &Year => (4, true, Parsed::set_year),
YearDiv100 => (2, false, Parsed::set_year_div_100), &YearDiv100 => (2, false, Parsed::set_year_div_100),
YearMod100 => (2, false, Parsed::set_year_mod_100), &YearMod100 => (2, false, Parsed::set_year_mod_100),
IsoYear => (4, true, Parsed::set_isoyear), &IsoYear => (4, true, Parsed::set_isoyear),
IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100), &IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100), &IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
Month => (2, false, Parsed::set_month), &Month => (2, false, Parsed::set_month),
Day => (2, false, Parsed::set_day), &Day => (2, false, Parsed::set_day),
WeekFromSun => (2, false, Parsed::set_week_from_sun), &WeekFromSun => (2, false, Parsed::set_week_from_sun),
WeekFromMon => (2, false, Parsed::set_week_from_mon), &WeekFromMon => (2, false, Parsed::set_week_from_mon),
IsoWeek => (2, false, Parsed::set_isoweek), &IsoWeek => (2, false, Parsed::set_isoweek),
NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday), &NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
WeekdayFromMon => (1, false, set_weekday_with_number_from_monday), &WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
Ordinal => (3, false, Parsed::set_ordinal), &Ordinal => (3, false, Parsed::set_ordinal),
Hour => (2, false, Parsed::set_hour), &Hour => (2, false, Parsed::set_hour),
Hour12 => (2, false, Parsed::set_hour12), &Hour12 => (2, false, Parsed::set_hour12),
Minute => (2, false, Parsed::set_minute), &Minute => (2, false, Parsed::set_minute),
Second => (2, false, Parsed::set_second), &Second => (2, false, Parsed::set_second),
Nanosecond => (9, false, Parsed::set_nanosecond), &Nanosecond => (9, false, Parsed::set_nanosecond),
Timestamp => (usize::MAX, false, Parsed::set_timestamp), &Timestamp => (usize::MAX, false, Parsed::set_timestamp),
// for the future expansion // for the future expansion
Internal(ref int) => match int._dummy {}, &Internal(ref int) => match int._dummy {},
}; };
s = s.trim_left(); s = s.trim_left();
@ -281,31 +282,31 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
try!(set(parsed, v)); try!(set(parsed, v));
} }
Item::Fixed(spec) => { &Item::Fixed(ref spec) => {
use super::Fixed::*; use super::Fixed::*;
match spec { match spec {
ShortMonthName => { &ShortMonthName => {
let month0 = try_consume!(scan::short_month0(s)); let month0 = try_consume!(scan::short_month0(s));
try!(parsed.set_month(i64::from(month0) + 1)); try!(parsed.set_month(i64::from(month0) + 1));
} }
LongMonthName => { &LongMonthName => {
let month0 = try_consume!(scan::short_or_long_month0(s)); let month0 = try_consume!(scan::short_or_long_month0(s));
try!(parsed.set_month(i64::from(month0) + 1)); try!(parsed.set_month(i64::from(month0) + 1));
} }
ShortWeekdayName => { &ShortWeekdayName => {
let weekday = try_consume!(scan::short_weekday(s)); let weekday = try_consume!(scan::short_weekday(s));
try!(parsed.set_weekday(weekday)); try!(parsed.set_weekday(weekday));
} }
LongWeekdayName => { &LongWeekdayName => {
let weekday = try_consume!(scan::short_or_long_weekday(s)); let weekday = try_consume!(scan::short_or_long_weekday(s));
try!(parsed.set_weekday(weekday)); try!(parsed.set_weekday(weekday));
} }
LowerAmPm | UpperAmPm => { &LowerAmPm | &UpperAmPm => {
if s.len() < 2 { return Err(TOO_SHORT); } if s.len() < 2 { return Err(TOO_SHORT); }
let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) { let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) {
(b'a',b'm') => false, (b'a',b'm') => false,
@ -316,56 +317,56 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
s = &s[2..]; s = &s[2..];
} }
Nanosecond | Nanosecond3 | Nanosecond6 | Nanosecond9 => { &Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => {
if s.starts_with('.') { if s.starts_with('.') {
let nano = try_consume!(scan::nanosecond(&s[1..])); let nano = try_consume!(scan::nanosecond(&s[1..]));
try!(parsed.set_nanosecond(nano)); try!(parsed.set_nanosecond(nano));
} }
} }
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => { &Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
if s.len() < 3 { return Err(TOO_SHORT); } if s.len() < 3 { return Err(TOO_SHORT); }
let nano = try_consume!(scan::nanosecond_fixed(s, 3)); let nano = try_consume!(scan::nanosecond_fixed(s, 3));
try!(parsed.set_nanosecond(nano)); try!(parsed.set_nanosecond(nano));
} }
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => { &Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
if s.len() < 6 { return Err(TOO_SHORT); } if s.len() < 6 { return Err(TOO_SHORT); }
let nano = try_consume!(scan::nanosecond_fixed(s, 6)); let nano = try_consume!(scan::nanosecond_fixed(s, 6));
try!(parsed.set_nanosecond(nano)); try!(parsed.set_nanosecond(nano));
} }
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => { &Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
if s.len() < 9 { return Err(TOO_SHORT); } if s.len() < 9 { return Err(TOO_SHORT); }
let nano = try_consume!(scan::nanosecond_fixed(s, 9)); let nano = try_consume!(scan::nanosecond_fixed(s, 9));
try!(parsed.set_nanosecond(nano)); try!(parsed.set_nanosecond(nano));
} }
TimezoneName => return Err(BAD_FORMAT), &TimezoneName => return Err(BAD_FORMAT),
TimezoneOffsetColon | TimezoneOffset => { &TimezoneOffsetColon | &TimezoneOffset => {
let offset = try_consume!(scan::timezone_offset(s.trim_left(), let offset = try_consume!(scan::timezone_offset(s.trim_left(),
scan::colon_or_space)); scan::colon_or_space));
try!(parsed.set_offset(i64::from(offset))); try!(parsed.set_offset(i64::from(offset)));
} }
TimezoneOffsetColonZ | TimezoneOffsetZ => { &TimezoneOffsetColonZ | &TimezoneOffsetZ => {
let offset = try_consume!(scan::timezone_offset_zulu(s.trim_left(), let offset = try_consume!(scan::timezone_offset_zulu(s.trim_left(),
scan::colon_or_space)); scan::colon_or_space));
try!(parsed.set_offset(i64::from(offset))); try!(parsed.set_offset(i64::from(offset)));
} }
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => { &Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => {
let offset = try_consume!(scan::timezone_offset_permissive( let offset = try_consume!(scan::timezone_offset_permissive(
s.trim_left(), scan::colon_or_space)); s.trim_left(), scan::colon_or_space));
try!(parsed.set_offset(i64::from(offset))); try!(parsed.set_offset(i64::from(offset)));
} }
RFC2822 => try_consume!(parse_rfc2822(parsed, s)), &RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
RFC3339 => try_consume!(parse_rfc3339(parsed, s)), &RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
} }
} }
Item::Error => { &Item::Error => {
return Err(BAD_FORMAT); return Err(BAD_FORMAT);
} }
} }
@ -388,7 +389,7 @@ fn test_parse() {
// workaround for Rust issue #22255 // workaround for Rust issue #22255
fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> { fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> {
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, items.iter().cloned())); try!(parse(&mut parsed, s, items.iter()));
Ok(parsed) Ok(parsed)
} }
@ -699,12 +700,12 @@ fn test_rfc2822() {
fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> { fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
try!(parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter().cloned())); try!(parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter()));
parsed.to_datetime() parsed.to_datetime()
} }
fn fmt_rfc2822_datetime(dt: DateTime<FixedOffset>) -> String { fn fmt_rfc2822_datetime(dt: DateTime<FixedOffset>) -> String {
dt.format_with_items([Item::Fixed(Fixed::RFC2822)].iter().cloned()).to_string() dt.format_with_items([Item::Fixed(Fixed::RFC2822)].iter()).to_string()
} }
// Test against test data above // Test against test data above
@ -780,12 +781,12 @@ fn test_rfc3339() {
fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> { fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
try!(parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter().cloned())); try!(parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter()));
parsed.to_datetime() parsed.to_datetime()
} }
fn fmt_rfc3339_datetime(dt: DateTime<FixedOffset>) -> String { fn fmt_rfc3339_datetime(dt: DateTime<FixedOffset>) -> String {
dt.format_with_items([Item::Fixed(Fixed::RFC3339)].iter().cloned()).to_string() dt.format_with_items([Item::Fixed(Fixed::RFC3339)].iter()).to_string()
} }
// Test against test data above // Test against test data above

View File

@ -112,6 +112,7 @@ pub struct Parsed {
/// Checks if `old` is either empty or has the same value to `new` (i.e. "consistent"), /// Checks if `old` is either empty or has the same value to `new` (i.e. "consistent"),
/// and if it is empty, set `old` to `new` as well. /// and if it is empty, set `old` to `new` as well.
#[inline]
fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> { fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> {
if let Some(ref old) = *old { if let Some(ref old) = *old {
if *old == new {Ok(())} else {Err(IMPOSSIBLE)} if *old == new {Ok(())} else {Err(IMPOSSIBLE)}
@ -141,82 +142,97 @@ impl Parsed {
} }
/// Tries to set the [`year`](#structfield.year) field from given value. /// Tries to set the [`year`](#structfield.year) field from given value.
#[inline]
pub fn set_year(&mut self, value: i64) -> ParseResult<()> { pub fn set_year(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.year, try!(value.to_i32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.year, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`year_div_100`](#structfield.year_div_100) field from given value. /// Tries to set the [`year_div_100`](#structfield.year_div_100) field from given value.
#[inline]
pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> { pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> {
if value < 0 { return Err(OUT_OF_RANGE); } if value < 0 { return Err(OUT_OF_RANGE); }
set_if_consistent(&mut self.year_div_100, try!(value.to_i32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.year_div_100, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`year_mod_100`](#structfield.year_mod_100) field from given value. /// Tries to set the [`year_mod_100`](#structfield.year_mod_100) field from given value.
#[inline]
pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> { pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> {
if value < 0 { return Err(OUT_OF_RANGE); } if value < 0 { return Err(OUT_OF_RANGE); }
set_if_consistent(&mut self.year_mod_100, try!(value.to_i32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.year_mod_100, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`isoyear`](#structfield.isoyear) field from given value. /// Tries to set the [`isoyear`](#structfield.isoyear) field from given value.
#[inline]
pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> { pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.isoyear, try!(value.to_i32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.isoyear, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`isoyear_div_100`](#structfield.isoyear_div_100) field from given value. /// Tries to set the [`isoyear_div_100`](#structfield.isoyear_div_100) field from given value.
#[inline]
pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> { pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> {
if value < 0 { return Err(OUT_OF_RANGE); } if value < 0 { return Err(OUT_OF_RANGE); }
set_if_consistent(&mut self.isoyear_div_100, try!(value.to_i32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.isoyear_div_100, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`isoyear_mod_100`](#structfield.isoyear_mod_100) field from given value. /// Tries to set the [`isoyear_mod_100`](#structfield.isoyear_mod_100) field from given value.
#[inline]
pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> { pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> {
if value < 0 { return Err(OUT_OF_RANGE); } if value < 0 { return Err(OUT_OF_RANGE); }
set_if_consistent(&mut self.isoyear_mod_100, try!(value.to_i32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.isoyear_mod_100, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`month`](#structfield.month) field from given value. /// Tries to set the [`month`](#structfield.month) field from given value.
#[inline]
pub fn set_month(&mut self, value: i64) -> ParseResult<()> { pub fn set_month(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.month, try!(value.to_u32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.month, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`week_from_sun`](#structfield.week_from_sun) field from given value. /// Tries to set the [`week_from_sun`](#structfield.week_from_sun) field from given value.
#[inline]
pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> { pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.week_from_sun, try!(value.to_u32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.week_from_sun, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`week_from_mon`](#structfield.week_from_mon) field from given value. /// Tries to set the [`week_from_mon`](#structfield.week_from_mon) field from given value.
#[inline]
pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> { pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.week_from_mon, try!(value.to_u32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.week_from_mon, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`isoweek`](#structfield.isoweek) field from given value. /// Tries to set the [`isoweek`](#structfield.isoweek) field from given value.
#[inline]
pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> { pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.isoweek, try!(value.to_u32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.isoweek, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`weekday`](#structfield.weekday) field from given value. /// Tries to set the [`weekday`](#structfield.weekday) field from given value.
#[inline]
pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> { pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> {
set_if_consistent(&mut self.weekday, value) set_if_consistent(&mut self.weekday, value)
} }
/// Tries to set the [`ordinal`](#structfield.ordinal) field from given value. /// Tries to set the [`ordinal`](#structfield.ordinal) field from given value.
#[inline]
pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> { pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.ordinal, try!(value.to_u32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.ordinal, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`day`](#structfield.day) field from given value. /// Tries to set the [`day`](#structfield.day) field from given value.
#[inline]
pub fn set_day(&mut self, value: i64) -> ParseResult<()> { pub fn set_day(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.day, try!(value.to_u32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.day, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`hour_div_12`](#structfield.hour_div_12) field from given value. /// Tries to set the [`hour_div_12`](#structfield.hour_div_12) field from given value.
/// (`false` for AM, `true` for PM) /// (`false` for AM, `true` for PM)
#[inline]
pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> { pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
set_if_consistent(&mut self.hour_div_12, if value {1} else {0}) set_if_consistent(&mut self.hour_div_12, if value {1} else {0})
} }
/// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from /// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from
/// given hour number in 12-hour clocks. /// given hour number in 12-hour clocks.
#[inline]
pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> { pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> {
if value < 1 || value > 12 { return Err(OUT_OF_RANGE); } if value < 1 || value > 12 { return Err(OUT_OF_RANGE); }
set_if_consistent(&mut self.hour_mod_12, value as u32 % 12) set_if_consistent(&mut self.hour_mod_12, value as u32 % 12)
@ -224,6 +240,7 @@ impl Parsed {
/// Tries to set both [`hour_div_12`](#structfield.hour_div_12) and /// Tries to set both [`hour_div_12`](#structfield.hour_div_12) and
/// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value. /// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value.
#[inline]
pub fn set_hour(&mut self, value: i64) -> ParseResult<()> { pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
let v = try!(value.to_u32().ok_or(OUT_OF_RANGE)); let v = try!(value.to_u32().ok_or(OUT_OF_RANGE));
try!(set_if_consistent(&mut self.hour_div_12, v / 12)); try!(set_if_consistent(&mut self.hour_div_12, v / 12));
@ -232,26 +249,31 @@ impl Parsed {
} }
/// Tries to set the [`minute`](#structfield.minute) field from given value. /// Tries to set the [`minute`](#structfield.minute) field from given value.
#[inline]
pub fn set_minute(&mut self, value: i64) -> ParseResult<()> { pub fn set_minute(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.minute, try!(value.to_u32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.minute, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`second`](#structfield.second) field from given value. /// Tries to set the [`second`](#structfield.second) field from given value.
#[inline]
pub fn set_second(&mut self, value: i64) -> ParseResult<()> { pub fn set_second(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.second, try!(value.to_u32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.second, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`nanosecond`](#structfield.nanosecond) field from given value. /// Tries to set the [`nanosecond`](#structfield.nanosecond) field from given value.
#[inline]
pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> { pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.nanosecond, try!(value.to_u32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.nanosecond, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
} }
/// Tries to set the [`timestamp`](#structfield.timestamp) field from given value. /// Tries to set the [`timestamp`](#structfield.timestamp) field from given value.
#[inline]
pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> { pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.timestamp, value) set_if_consistent(&mut self.timestamp, value)
} }
/// Tries to set the [`offset`](#structfield.offset) field from given value. /// Tries to set the [`offset`](#structfield.offset) field from given value.
#[inline]
pub fn set_offset(&mut self, value: i64) -> ParseResult<()> { pub fn set_offset(&mut self, value: i64) -> ParseResult<()> {
set_if_consistent(&mut self.offset, try!(value.to_i32().ok_or(OUT_OF_RANGE))) set_if_consistent(&mut self.offset, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
} }

View File

@ -30,23 +30,35 @@ fn equals(s: &str, pattern: &str) -> bool {
/// The absence of digits at all is an unconditional error. /// The absence of digits at all is an unconditional error.
/// More than `max` digits are consumed up to the first `max` digits. /// More than `max` digits are consumed up to the first `max` digits.
/// Any number that does not fit in `i64` is an error. /// Any number that does not fit in `i64` is an error.
#[inline]
pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> { pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
assert!(min <= max); assert!(min <= max);
// limit `s` to given number of digits // We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on
let mut window = s.as_bytes(); // the first non-numeric byte, which may be another ascii character or beginning of multi-byte
if window.len() > max { window = &window[..max]; } // UTF-8 character.
let bytes = s.as_bytes();
// scan digits if bytes.len() < min {
let upto = window.iter().position(|&c| c < b'0' || b'9' < c) return Err(TOO_SHORT);
.unwrap_or_else(|| window.len());
if upto < min {
return Err(if window.is_empty() {TOO_SHORT} else {INVALID});
} }
// we can overflow here, which is the only possible cause of error from `parse`. let mut n = 0i64;
let v: i64 = try!(s[..upto].parse().map_err(|_| OUT_OF_RANGE)); for (i, c) in bytes.iter().take(max).cloned().enumerate() { // cloned() = copied()
Ok((&s[upto..], v)) if c < b'0' || b'9' < c {
if i < min {
return Err(INVALID);
} else {
return Ok((&s[i..], n));
}
}
n = match n.checked_mul(10).and_then(|n| n.checked_add((c - b'0') as i64)) {
Some(n) => n,
None => return Err(OUT_OF_RANGE),
};
}
Ok((&s[::core::cmp::min(max, bytes.len())..], n))
} }
/// Tries to consume at least one digits as a fractional second. /// Tries to consume at least one digits as a fractional second.

View File

@ -383,9 +383,10 @@
#![doc(html_root_url = "https://docs.rs/chrono/latest/")] #![doc(html_root_url = "https://docs.rs/chrono/latest/")]
#![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507 #![cfg_attr(feature = "bench", feature(test))] // lib stability features as per RFC #507
#![deny(missing_docs)] #![deny(missing_docs)]
#![deny(missing_debug_implementations)] #![deny(missing_debug_implementations)]
#![allow(deprecated)]
#![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(not(any(feature = "std", test)), no_std)]
@ -427,6 +428,8 @@ extern crate doc_comment;
extern crate wasm_bindgen; extern crate wasm_bindgen;
#[cfg(all(target_arch = "wasm32", feature="wasmbind"))] #[cfg(all(target_arch = "wasm32", feature="wasmbind"))]
extern crate js_sys; extern crate js_sys;
#[cfg(feature = "bench")]
extern crate test;
#[cfg(test)] #[cfg(test)]
doctest!("../README.md"); doctest!("../README.md");

View File

@ -3,6 +3,8 @@
//! ISO 8601 calendar date without timezone. //! ISO 8601 calendar date without timezone.
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::{str, fmt}; use core::{str, fmt};
use core::ops::{Add, Sub, AddAssign, SubAssign}; use core::ops::{Add, Sub, AddAssign, SubAssign};
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
@ -920,8 +922,8 @@ impl NaiveDate {
/// ~~~~ /// ~~~~
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
#[inline] #[inline]
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I> pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone { where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
DelayedFormat::new(Some(*self), None, items) DelayedFormat::new(Some(*self), None, items)
} }
@ -1507,16 +1509,16 @@ impl str::FromStr for NaiveDate {
fn from_str(s: &str) -> ParseResult<NaiveDate> { fn from_str(s: &str) -> ParseResult<NaiveDate> {
const ITEMS: &'static [Item<'static>] = &[ 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::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero), Item::Numeric(Numeric::Month, Pad::Zero),
Item::Space(""), Item::Literal("-"), Item::Space(""), Item::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Day, Pad::Zero), Item::Numeric(Numeric::Day, Pad::Zero),
Item::Space(""), Item::Space(""),
]; ];
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned())); try!(parse(&mut parsed, s, ITEMS.iter()));
parsed.to_naive_date() parsed.to_naive_date()
} }
} }

View File

@ -3,6 +3,8 @@
//! ISO 8601 date and time without timezone. //! ISO 8601 date and time without timezone.
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::{str, fmt, hash}; use core::{str, fmt, hash};
use core::ops::{Add, Sub, AddAssign, SubAssign}; use core::ops::{Add, Sub, AddAssign, SubAssign};
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
@ -649,8 +651,8 @@ impl NaiveDateTime {
/// ~~~~ /// ~~~~
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
#[inline] #[inline]
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I> pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone { where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
DelayedFormat::new(Some(self.date), Some(self.time), items) DelayedFormat::new(Some(self.date), Some(self.time), items)
} }
@ -1472,22 +1474,22 @@ impl str::FromStr for NaiveDateTime {
fn from_str(s: &str) -> ParseResult<NaiveDateTime> { fn from_str(s: &str) -> ParseResult<NaiveDateTime> {
const ITEMS: &'static [Item<'static>] = &[ 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::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero), Item::Numeric(Numeric::Month, Pad::Zero),
Item::Space(""), Item::Literal("-"), 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::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::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero), Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Space(""), Item::Literal(":"), 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::Nanosecond), Item::Space(""),
]; ];
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned())); try!(parse(&mut parsed, s, ITEMS.iter()));
parsed.to_naive_datetime_with_offset(0) parsed.to_naive_datetime_with_offset(0)
} }
} }

View File

@ -470,7 +470,6 @@ impl fmt::Debug for Mdf {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(test)] extern crate num_iter; #[cfg(test)] extern crate num_iter;
#[cfg(bench)] extern crate test;
use Weekday; use Weekday;
use super::{Of, Mdf}; use super::{Of, Mdf};
@ -517,7 +516,7 @@ mod tests {
assert_eq!(GF.nisoweeks(), 52); assert_eq!(GF.nisoweeks(), 52);
} }
#[cfg(bench)] #[cfg(feature = "bench")]
#[bench] #[bench]
fn bench_year_flags_from_year(bh: &mut test::Bencher) { fn bench_year_flags_from_year(bh: &mut test::Bencher) {
bh.iter(|| { bh.iter(|| {

View File

@ -3,6 +3,8 @@
//! ISO 8601 time without timezone. //! ISO 8601 time without timezone.
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::{str, fmt, hash}; use core::{str, fmt, hash};
use core::ops::{Add, Sub, AddAssign, SubAssign}; use core::ops::{Add, Sub, AddAssign, SubAssign};
use oldtime::Duration as OldDuration; use oldtime::Duration as OldDuration;
@ -727,8 +729,8 @@ impl NaiveTime {
/// ~~~~ /// ~~~~
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
#[inline] #[inline]
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I> pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone { where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
DelayedFormat::new(None, Some(*self), items) DelayedFormat::new(None, Some(*self), items)
} }
@ -1303,16 +1305,16 @@ impl str::FromStr for NaiveTime {
fn from_str(s: &str) -> ParseResult<NaiveTime> { fn from_str(s: &str) -> ParseResult<NaiveTime> {
const ITEMS: &'static [Item<'static>] = &[ const ITEMS: &'static [Item<'static>] = &[
Item::Space(""), Item::Numeric(Numeric::Hour, Pad::Zero), Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Space(""), Item::Literal(":"), 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::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero), Item::Numeric(Numeric::Second, Pad::Zero),
Item::Fixed(Fixed::Nanosecond), Item::Space(""), Item::Fixed(Fixed::Nanosecond), Item::Space(""),
]; ];
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned())); try!(parse(&mut parsed, s, ITEMS.iter()));
parsed.to_naive_time() parsed.to_naive_time()
} }
} }