Merge pull request #358 from michalsrb/optimize-parsing
Optimize parsing
This commit is contained in:
commit
46f8267c61
|
@ -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
|
||||
|
|
|
@ -29,6 +29,7 @@ alloc = []
|
|||
std = []
|
||||
clock = ["time", "std"]
|
||||
wasmbind = ["wasm-bindgen", "js-sys"]
|
||||
bench = ["std"]
|
||||
|
||||
[dependencies]
|
||||
time = { version = "0.1.39", optional = true }
|
||||
|
|
|
@ -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};
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -450,11 +451,11 @@ 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(),
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
#[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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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};
|
||||
|
@ -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};
|
||||
|
@ -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");
|
||||
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()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#![allow(ellipsis_inclusive_range_patterns)]
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
#[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.
|
||||
/// Internally used by `DelayedFormat`.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
pub fn format<'a, I>(
|
||||
pub fn format<'a, I, B>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
items: I,
|
||||
) -> 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
|
||||
static SHORT_MONTHS: [&'static str; 12] =
|
||||
|
@ -380,12 +381,12 @@ pub fn format<'a, I>(
|
|||
let mut result = String::new();
|
||||
|
||||
for item in items {
|
||||
match item {
|
||||
Item::Literal(s) | Item::Space(s) => result.push_str(s),
|
||||
match item.borrow() {
|
||||
&Item::Literal(s) | &Item::Space(s) => result.push_str(s),
|
||||
#[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::*;
|
||||
|
||||
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;
|
||||
|
||||
let (width, v) = match spec {
|
||||
Year => (4, date.map(|d| i64::from(d.year()))),
|
||||
YearDiv100 => (2, date.map(|d| div_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()))),
|
||||
IsoYearDiv100 => (2, date.map(|d| div_floor(
|
||||
&Year => (4, date.map(|d| i64::from(d.year()))),
|
||||
&YearDiv100 => (2, date.map(|d| div_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()))),
|
||||
&IsoYearDiv100 => (2, date.map(|d| div_floor(
|
||||
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))),
|
||||
Month => (2, date.map(|d| i64::from(d.month()))),
|
||||
Day => (2, date.map(|d| i64::from(d.day()))),
|
||||
WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
|
||||
WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
|
||||
IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
|
||||
NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday()
|
||||
&Month => (2, date.map(|d| i64::from(d.month()))),
|
||||
&Day => (2, date.map(|d| i64::from(d.day()))),
|
||||
&WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
|
||||
&WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
|
||||
&IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
|
||||
&NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday()
|
||||
.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()))),
|
||||
Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
|
||||
Hour => (2, time.map(|t| i64::from(t.hour()))),
|
||||
Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
|
||||
Minute => (2, time.map(|t| i64::from(t.minute()))),
|
||||
Second => (2, time.map(|t| i64::from(t.second() +
|
||||
&Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
|
||||
&Hour => (2, time.map(|t| i64::from(t.hour()))),
|
||||
&Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
|
||||
&Minute => (2, time.map(|t| i64::from(t.minute()))),
|
||||
&Second => (2, time.map(|t| i64::from(t.second() +
|
||||
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) {
|
||||
&Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
|
||||
&Timestamp => (1, match (date, time, off) {
|
||||
(Some(d), Some(t), None) =>
|
||||
Some(d.and_time(*t).timestamp()),
|
||||
(Some(d), Some(t), Some(&(_, off))) =>
|
||||
|
@ -427,24 +428,24 @@ pub fn format<'a, I>(
|
|||
}),
|
||||
|
||||
// for the future expansion
|
||||
Internal(ref int) => match int._dummy {},
|
||||
&Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
|
||||
if let Some(v) = v {
|
||||
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
|
||||
match pad {
|
||||
Pad::None => write!(result, "{:+}", v),
|
||||
Pad::Zero => write!(result, "{:+01$}", v, width + 1),
|
||||
Pad::Space => write!(result, "{:+1$}", v, width + 1),
|
||||
&Pad::None => write!(result, "{:+}", v),
|
||||
&Pad::Zero => write!(result, "{:+01$}", v, width + 1),
|
||||
&Pad::Space => write!(result, "{:+1$}", v, width + 1),
|
||||
}
|
||||
} else {
|
||||
match pad {
|
||||
Pad::None => write!(result, "{}", v),
|
||||
Pad::Zero => write!(result, "{:01$}", v, width),
|
||||
Pad::Space => write!(result, "{:1$}", v, width),
|
||||
&Pad::None => write!(result, "{}", v),
|
||||
&Pad::Zero => write!(result, "{:01$}", 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::*;
|
||||
|
||||
/// 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 {
|
||||
ShortMonthName =>
|
||||
&ShortMonthName =>
|
||||
date.map(|d| {
|
||||
result.push_str(SHORT_MONTHS[d.month0() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
LongMonthName =>
|
||||
&LongMonthName =>
|
||||
date.map(|d| {
|
||||
result.push_str(LONG_MONTHS[d.month0() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
ShortWeekdayName =>
|
||||
&ShortWeekdayName =>
|
||||
date.map(|d| {
|
||||
result.push_str(
|
||||
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
|
||||
);
|
||||
Ok(())
|
||||
}),
|
||||
LongWeekdayName =>
|
||||
&LongWeekdayName =>
|
||||
date.map(|d| {
|
||||
result.push_str(
|
||||
LONG_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
|
||||
);
|
||||
Ok(())
|
||||
}),
|
||||
LowerAmPm =>
|
||||
&LowerAmPm =>
|
||||
time.map(|t| {
|
||||
result.push_str(if t.hour12().0 {"pm"} else {"am"});
|
||||
Ok(())
|
||||
}),
|
||||
UpperAmPm =>
|
||||
&UpperAmPm =>
|
||||
time.map(|t| {
|
||||
result.push_str(if t.hour12().0 {"PM"} else {"AM"});
|
||||
Ok(())
|
||||
}),
|
||||
Nanosecond =>
|
||||
&Nanosecond =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
if nano == 0 {
|
||||
|
@ -526,52 +527,52 @@ pub fn format<'a, I>(
|
|||
write!(result, ".{:09}", nano)
|
||||
}
|
||||
}),
|
||||
Nanosecond3 =>
|
||||
&Nanosecond3 =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:03}", nano / 1_000_000)
|
||||
}),
|
||||
Nanosecond6 =>
|
||||
&Nanosecond6 =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:06}", nano / 1_000)
|
||||
}),
|
||||
Nanosecond9 =>
|
||||
&Nanosecond9 =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:09}", nano)
|
||||
}),
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) =>
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:03}", nano / 1_000_000)
|
||||
}),
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) =>
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:06}", nano / 1_000)
|
||||
}),
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) =>
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:09}", nano)
|
||||
}),
|
||||
TimezoneName =>
|
||||
&TimezoneName =>
|
||||
off.map(|&(ref name, _)| {
|
||||
result.push_str(name);
|
||||
Ok(())
|
||||
}),
|
||||
TimezoneOffsetColon =>
|
||||
&TimezoneOffsetColon =>
|
||||
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)),
|
||||
TimezoneOffset =>
|
||||
&TimezoneOffset =>
|
||||
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)),
|
||||
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) =>
|
||||
&Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) =>
|
||||
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) {
|
||||
let sec = t.second() + t.nanosecond() / 1_000_000_000;
|
||||
try!(write!(
|
||||
|
@ -585,7 +586,7 @@ pub fn format<'a, I>(
|
|||
} else {
|
||||
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) {
|
||||
// reuse `Debug` impls which already print ISO 8601 format.
|
||||
// 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))]
|
||||
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.
|
||||
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
|
||||
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))]
|
||||
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 {
|
||||
format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone())
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::usize;
|
||||
|
||||
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.
|
||||
///
|
||||
/// - (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<()>
|
||||
where I: Iterator<Item=Item<'a>> {
|
||||
pub fn parse<'a, I, B>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<()>
|
||||
where I: Iterator<Item=B>, B: Borrow<Item<'a>> {
|
||||
macro_rules! try_consume {
|
||||
($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
|
||||
}
|
||||
|
||||
for item in items {
|
||||
match item {
|
||||
Item::Literal(prefix) => {
|
||||
match item.borrow() {
|
||||
&Item::Literal(prefix) => {
|
||||
if s.len() < prefix.len() { return Err(TOO_SHORT); }
|
||||
if !s.starts_with(prefix) { return Err(INVALID); }
|
||||
s = &s[prefix.len()..];
|
||||
}
|
||||
|
||||
#[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.starts_with(&prefix[..]) { return Err(INVALID); }
|
||||
s = &s[prefix.len()..];
|
||||
}
|
||||
|
||||
Item::Space(_) => {
|
||||
&Item::Space(_) => {
|
||||
s = s.trim_left();
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
Item::OwnedSpace(_) => {
|
||||
&Item::OwnedSpace(_) => {
|
||||
s = s.trim_left();
|
||||
}
|
||||
|
||||
Item::Numeric(spec, _pad) => {
|
||||
&Item::Numeric(ref spec, ref _pad) => {
|
||||
use super::Numeric::*;
|
||||
type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;
|
||||
|
||||
let (width, signed, set): (usize, bool, Setter) = match spec {
|
||||
Year => (4, true, Parsed::set_year),
|
||||
YearDiv100 => (2, false, Parsed::set_year_div_100),
|
||||
YearMod100 => (2, false, Parsed::set_year_mod_100),
|
||||
IsoYear => (4, true, Parsed::set_isoyear),
|
||||
IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
|
||||
IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
|
||||
Month => (2, false, Parsed::set_month),
|
||||
Day => (2, false, Parsed::set_day),
|
||||
WeekFromSun => (2, false, Parsed::set_week_from_sun),
|
||||
WeekFromMon => (2, false, Parsed::set_week_from_mon),
|
||||
IsoWeek => (2, false, Parsed::set_isoweek),
|
||||
NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
|
||||
WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
|
||||
Ordinal => (3, false, Parsed::set_ordinal),
|
||||
Hour => (2, false, Parsed::set_hour),
|
||||
Hour12 => (2, false, Parsed::set_hour12),
|
||||
Minute => (2, false, Parsed::set_minute),
|
||||
Second => (2, false, Parsed::set_second),
|
||||
Nanosecond => (9, false, Parsed::set_nanosecond),
|
||||
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
||||
&Year => (4, true, Parsed::set_year),
|
||||
&YearDiv100 => (2, false, Parsed::set_year_div_100),
|
||||
&YearMod100 => (2, false, Parsed::set_year_mod_100),
|
||||
&IsoYear => (4, true, Parsed::set_isoyear),
|
||||
&IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
|
||||
&IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
|
||||
&Month => (2, false, Parsed::set_month),
|
||||
&Day => (2, false, Parsed::set_day),
|
||||
&WeekFromSun => (2, false, Parsed::set_week_from_sun),
|
||||
&WeekFromMon => (2, false, Parsed::set_week_from_mon),
|
||||
&IsoWeek => (2, false, Parsed::set_isoweek),
|
||||
&NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
|
||||
&WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
|
||||
&Ordinal => (3, false, Parsed::set_ordinal),
|
||||
&Hour => (2, false, Parsed::set_hour),
|
||||
&Hour12 => (2, false, Parsed::set_hour12),
|
||||
&Minute => (2, false, Parsed::set_minute),
|
||||
&Second => (2, false, Parsed::set_second),
|
||||
&Nanosecond => (9, false, Parsed::set_nanosecond),
|
||||
&Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
||||
|
||||
// for the future expansion
|
||||
Internal(ref int) => match int._dummy {},
|
||||
&Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
Item::Fixed(spec) => {
|
||||
&Item::Fixed(ref spec) => {
|
||||
use super::Fixed::*;
|
||||
|
||||
match spec {
|
||||
ShortMonthName => {
|
||||
&ShortMonthName => {
|
||||
let month0 = try_consume!(scan::short_month0(s));
|
||||
try!(parsed.set_month(i64::from(month0) + 1));
|
||||
}
|
||||
|
||||
LongMonthName => {
|
||||
&LongMonthName => {
|
||||
let month0 = try_consume!(scan::short_or_long_month0(s));
|
||||
try!(parsed.set_month(i64::from(month0) + 1));
|
||||
}
|
||||
|
||||
ShortWeekdayName => {
|
||||
&ShortWeekdayName => {
|
||||
let weekday = try_consume!(scan::short_weekday(s));
|
||||
try!(parsed.set_weekday(weekday));
|
||||
}
|
||||
|
||||
LongWeekdayName => {
|
||||
&LongWeekdayName => {
|
||||
let weekday = try_consume!(scan::short_or_long_weekday(s));
|
||||
try!(parsed.set_weekday(weekday));
|
||||
}
|
||||
|
||||
LowerAmPm | UpperAmPm => {
|
||||
&LowerAmPm | &UpperAmPm => {
|
||||
if s.len() < 2 { return Err(TOO_SHORT); }
|
||||
let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) {
|
||||
(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..];
|
||||
}
|
||||
|
||||
Nanosecond | Nanosecond3 | Nanosecond6 | Nanosecond9 => {
|
||||
&Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => {
|
||||
if s.starts_with('.') {
|
||||
let nano = try_consume!(scan::nanosecond(&s[1..]));
|
||||
try!(parsed.set_nanosecond(nano));
|
||||
}
|
||||
}
|
||||
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
|
||||
if s.len() < 3 { return Err(TOO_SHORT); }
|
||||
let nano = try_consume!(scan::nanosecond_fixed(s, 3));
|
||||
try!(parsed.set_nanosecond(nano));
|
||||
}
|
||||
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
|
||||
if s.len() < 6 { return Err(TOO_SHORT); }
|
||||
let nano = try_consume!(scan::nanosecond_fixed(s, 6));
|
||||
try!(parsed.set_nanosecond(nano));
|
||||
}
|
||||
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
|
||||
if s.len() < 9 { return Err(TOO_SHORT); }
|
||||
let nano = try_consume!(scan::nanosecond_fixed(s, 9));
|
||||
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(),
|
||||
scan::colon_or_space));
|
||||
try!(parsed.set_offset(i64::from(offset)));
|
||||
}
|
||||
|
||||
TimezoneOffsetColonZ | TimezoneOffsetZ => {
|
||||
&TimezoneOffsetColonZ | &TimezoneOffsetZ => {
|
||||
let offset = try_consume!(scan::timezone_offset_zulu(s.trim_left(),
|
||||
scan::colon_or_space));
|
||||
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(
|
||||
s.trim_left(), scan::colon_or_space));
|
||||
try!(parsed.set_offset(i64::from(offset)));
|
||||
}
|
||||
|
||||
RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
||||
RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
||||
&RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
||||
&RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
||||
}
|
||||
}
|
||||
|
||||
Item::Error => {
|
||||
&Item::Error => {
|
||||
return Err(BAD_FORMAT);
|
||||
}
|
||||
}
|
||||
|
@ -388,7 +389,7 @@ fn test_parse() {
|
|||
// workaround for Rust issue #22255
|
||||
fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> {
|
||||
let mut parsed = Parsed::new();
|
||||
try!(parse(&mut parsed, s, items.iter().cloned()));
|
||||
try!(parse(&mut parsed, s, items.iter()));
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
|
@ -699,12 +700,12 @@ fn test_rfc2822() {
|
|||
|
||||
fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||
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()
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -780,12 +781,12 @@ fn test_rfc3339() {
|
|||
|
||||
fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||
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()
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -112,6 +112,7 @@ pub struct Parsed {
|
|||
|
||||
/// 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.
|
||||
#[inline]
|
||||
fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> {
|
||||
if let Some(ref old) = *old {
|
||||
if *old == new {Ok(())} else {Err(IMPOSSIBLE)}
|
||||
|
@ -141,82 +142,97 @@ impl Parsed {
|
|||
}
|
||||
|
||||
/// Tries to set the [`year`](#structfield.year) field from given value.
|
||||
#[inline]
|
||||
pub fn set_year(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> {
|
||||
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)))
|
||||
}
|
||||
|
||||
/// 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<()> {
|
||||
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)))
|
||||
}
|
||||
|
||||
/// Tries to set the [`isoyear`](#structfield.isoyear) field from given value.
|
||||
#[inline]
|
||||
pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> {
|
||||
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)))
|
||||
}
|
||||
|
||||
/// 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<()> {
|
||||
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)))
|
||||
}
|
||||
|
||||
/// Tries to set the [`month`](#structfield.month) field from given value.
|
||||
#[inline]
|
||||
pub fn set_month(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
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)))
|
||||
}
|
||||
|
||||
/// 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<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> {
|
||||
set_if_consistent(&mut self.weekday, value)
|
||||
}
|
||||
|
||||
/// Tries to set the [`ordinal`](#structfield.ordinal) field from given value.
|
||||
#[inline]
|
||||
pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_day(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
/// (`false` for AM, `true` for PM)
|
||||
#[inline]
|
||||
pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
|
||||
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
|
||||
/// given hour number in 12-hour clocks.
|
||||
#[inline]
|
||||
pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> {
|
||||
if value < 1 || value > 12 { return Err(OUT_OF_RANGE); }
|
||||
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
|
||||
/// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value.
|
||||
#[inline]
|
||||
pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
|
||||
let v = try!(value.to_u32().ok_or(OUT_OF_RANGE));
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_minute(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_second(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> {
|
||||
set_if_consistent(&mut self.timestamp, value)
|
||||
}
|
||||
|
||||
/// Tries to set the [`offset`](#structfield.offset) field from given value.
|
||||
#[inline]
|
||||
pub fn set_offset(&mut self, value: i64) -> ParseResult<()> {
|
||||
set_if_consistent(&mut self.offset, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
|
||||
}
|
||||
|
|
|
@ -30,23 +30,35 @@ fn equals(s: &str, pattern: &str) -> bool {
|
|||
/// The absence of digits at all is an unconditional error.
|
||||
/// More than `max` digits are consumed up to the first `max` digits.
|
||||
/// Any number that does not fit in `i64` is an error.
|
||||
#[inline]
|
||||
pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
||||
assert!(min <= max);
|
||||
|
||||
// limit `s` to given number of digits
|
||||
let mut window = s.as_bytes();
|
||||
if window.len() > max { window = &window[..max]; }
|
||||
|
||||
// scan digits
|
||||
let upto = window.iter().position(|&c| c < b'0' || b'9' < c)
|
||||
.unwrap_or_else(|| window.len());
|
||||
if upto < min {
|
||||
return Err(if window.is_empty() {TOO_SHORT} else {INVALID});
|
||||
// We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on
|
||||
// the first non-numeric byte, which may be another ascii character or beginning of multi-byte
|
||||
// UTF-8 character.
|
||||
let bytes = s.as_bytes();
|
||||
if bytes.len() < min {
|
||||
return Err(TOO_SHORT);
|
||||
}
|
||||
|
||||
// we can overflow here, which is the only possible cause of error from `parse`.
|
||||
let v: i64 = try!(s[..upto].parse().map_err(|_| OUT_OF_RANGE));
|
||||
Ok((&s[upto..], v))
|
||||
let mut n = 0i64;
|
||||
for (i, c) in bytes.iter().take(max).cloned().enumerate() { // cloned() = copied()
|
||||
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.
|
||||
|
|
|
@ -383,9 +383,10 @@
|
|||
|
||||
#![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_debug_implementations)]
|
||||
#![allow(deprecated)]
|
||||
|
||||
#![cfg_attr(not(any(feature = "std", test)), no_std)]
|
||||
|
||||
|
@ -427,6 +428,8 @@ extern crate doc_comment;
|
|||
extern crate wasm_bindgen;
|
||||
#[cfg(all(target_arch = "wasm32", feature="wasmbind"))]
|
||||
extern crate js_sys;
|
||||
#[cfg(feature = "bench")]
|
||||
extern crate test;
|
||||
|
||||
#[cfg(test)]
|
||||
doctest!("../README.md");
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
//! ISO 8601 calendar date without timezone.
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::{str, fmt};
|
||||
use core::ops::{Add, Sub, AddAssign, SubAssign};
|
||||
use num_traits::ToPrimitive;
|
||||
|
@ -920,8 +922,8 @@ impl NaiveDate {
|
|||
/// ~~~~
|
||||
#[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(Some(*self), None, items)
|
||||
}
|
||||
|
||||
|
@ -1507,16 +1509,16 @@ impl str::FromStr for NaiveDate {
|
|||
|
||||
fn from_str(s: &str) -> ParseResult<NaiveDate> {
|
||||
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(""),
|
||||
];
|
||||
|
||||
let mut parsed = Parsed::new();
|
||||
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||
try!(parse(&mut parsed, s, ITEMS.iter()));
|
||||
parsed.to_naive_date()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
//! 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::ops::{Add, Sub, AddAssign, SubAssign};
|
||||
use num_traits::ToPrimitive;
|
||||
|
@ -649,8 +651,8 @@ impl NaiveDateTime {
|
|||
/// ~~~~
|
||||
#[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(Some(self.date), Some(self.time), items)
|
||||
}
|
||||
|
||||
|
@ -1472,22 +1474,22 @@ impl str::FromStr for NaiveDateTime {
|
|||
|
||||
fn from_str(s: &str) -> ParseResult<NaiveDateTime> {
|
||||
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(""),
|
||||
];
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -470,7 +470,6 @@ impl fmt::Debug for Mdf {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(test)] extern crate num_iter;
|
||||
#[cfg(bench)] extern crate test;
|
||||
|
||||
use Weekday;
|
||||
use super::{Of, Mdf};
|
||||
|
@ -517,7 +516,7 @@ mod tests {
|
|||
assert_eq!(GF.nisoweeks(), 52);
|
||||
}
|
||||
|
||||
#[cfg(bench)]
|
||||
#[cfg(feature = "bench")]
|
||||
#[bench]
|
||||
fn bench_year_flags_from_year(bh: &mut test::Bencher) {
|
||||
bh.iter(|| {
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
//! ISO 8601 time without timezone.
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::{str, fmt, hash};
|
||||
use core::ops::{Add, Sub, AddAssign, SubAssign};
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
@ -727,8 +729,8 @@ impl NaiveTime {
|
|||
/// ~~~~
|
||||
#[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(None, Some(*self), items)
|
||||
}
|
||||
|
||||
|
@ -1303,16 +1305,16 @@ impl str::FromStr for NaiveTime {
|
|||
|
||||
fn from_str(s: &str) -> ParseResult<NaiveTime> {
|
||||
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::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(""),
|
||||
];
|
||||
|
||||
let mut parsed = Parsed::new();
|
||||
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||
try!(parse(&mut parsed, s, ITEMS.iter()));
|
||||
parsed.to_naive_time()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue