diff --git a/src/format/parse.rs b/src/format/parse.rs index 8f64cff2..85c65514 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -5,7 +5,6 @@ //! Date and time parsing routines. use std::usize; -use std::cmp; use Weekday; @@ -317,27 +316,21 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<( } Nanosecond3NoDot => { - if !s.is_empty() { - let max_index = cmp::min(s.len() as u64, 3) as usize; - let nano = try_consume!(scan::nanosecond(&s[..max_index])); - try!(parsed.set_nanosecond(nano)); - } + if s.len() < 3 { return Err(TOO_SHORT); } + let nano = try_consume!(scan::nanosecond_fixed(s, 3)); + try!(parsed.set_nanosecond(nano)); } Nanosecond6NoDot => { - if !s.is_empty() { - let max_index = cmp::min(s.len() as u64, 6) as usize; - let nano = try_consume!(scan::nanosecond(&s[..max_index])); - try!(parsed.set_nanosecond(nano)); - } + if s.len() < 6 { return Err(TOO_SHORT); } + let nano = try_consume!(scan::nanosecond_fixed(s, 6)); + try!(parsed.set_nanosecond(nano)); } Nanosecond9NoDot => { - if !s.is_empty() { - let max_index = cmp::min(s.len() as u64, 9) as usize; - let nano = try_consume!(scan::nanosecond(&s[..max_index])); - try!(parsed.set_nanosecond(nano)); - } + 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), @@ -560,44 +553,37 @@ fn test_parse() { check!(" .4", [fix!(Nanosecond)]; TOO_LONG); // no automatic trimming // fixed: nanoseconds without the dot - check!("", [fix!(Nanosecond3NoDot)]; ); // no field set, but not an error - check!("0", [fix!(Nanosecond3NoDot)]; nanosecond: 0); - check!("4", [fix!(Nanosecond3NoDot)]; nanosecond: 400_000_000); - check!("42", [fix!(Nanosecond3NoDot)]; nanosecond: 420_000_000); + check!("", [fix!(Nanosecond3NoDot)]; TOO_SHORT); + check!("0", [fix!(Nanosecond3NoDot)]; TOO_SHORT); + check!("4", [fix!(Nanosecond3NoDot)]; TOO_SHORT); + check!("42", [fix!(Nanosecond3NoDot)]; TOO_SHORT); check!("421", [fix!(Nanosecond3NoDot)]; nanosecond: 421_000_000); - check!("42195", [fix!(Nanosecond3NoDot)]; nanosecond: 421_000_000); // ignores everything after 3 digits - check!("000000000547", [fix!(Nanosecond3NoDot)]; nanosecond: 0); - check!("4x", [fix!(Nanosecond3NoDot)]; TOO_LONG); + check!("42143", [fix!(Nanosecond3NoDot), num!(Second)]; nanosecond: 421_000_000, second: 43); + check!("42195", [fix!(Nanosecond3NoDot)]; TOO_LONG); + check!("4x", [fix!(Nanosecond3NoDot)]; TOO_SHORT); check!(" 4", [fix!(Nanosecond3NoDot)]; INVALID); check!(".421", [fix!(Nanosecond3NoDot)]; INVALID); - check!("", [fix!(Nanosecond6NoDot)]; ); // no field set, but not an error - check!("0", [fix!(Nanosecond6NoDot)]; nanosecond: 0); - check!("4", [fix!(Nanosecond6NoDot)]; nanosecond: 400_000_000); - check!("42", [fix!(Nanosecond6NoDot)]; nanosecond: 420_000_000); - check!("421", [fix!(Nanosecond6NoDot)]; nanosecond: 421_000_000); - check!("42195", [fix!(Nanosecond6NoDot)]; nanosecond: 421_950_000); - check!("421950803", [fix!(Nanosecond6NoDot)]; nanosecond: 421_950_000); // ignores everything after 6 digits - check!("000003547", [fix!(Nanosecond6NoDot)]; nanosecond: 3000); - check!("000000547", [fix!(Nanosecond6NoDot)]; nanosecond: 0); - check!("4x", [fix!(Nanosecond6NoDot)]; TOO_LONG); - check!(" 4", [fix!(Nanosecond6NoDot)]; INVALID); - check!(".421", [fix!(Nanosecond6NoDot)]; INVALID); + check!("", [fix!(Nanosecond6NoDot)]; TOO_SHORT); + check!("0", [fix!(Nanosecond6NoDot)]; TOO_SHORT); + check!("42195", [fix!(Nanosecond6NoDot)]; TOO_SHORT); + check!("421950", [fix!(Nanosecond6NoDot)]; nanosecond: 421_950_000); + check!("000003", [fix!(Nanosecond6NoDot)]; nanosecond: 3000); + check!("000000", [fix!(Nanosecond6NoDot)]; nanosecond: 0); + check!("4x", [fix!(Nanosecond6NoDot)]; TOO_SHORT); + check!(" 4", [fix!(Nanosecond6NoDot)]; INVALID); + check!(".42100", [fix!(Nanosecond6NoDot)]; INVALID); - check!("", [fix!(Nanosecond9NoDot)]; ); // no field set, but not an error - check!("0", [fix!(Nanosecond9NoDot)]; nanosecond: 0); - check!("4", [fix!(Nanosecond9NoDot)]; nanosecond: 400_000_000); - check!("42", [fix!(Nanosecond9NoDot)]; nanosecond: 420_000_000); - check!("421", [fix!(Nanosecond9NoDot)]; nanosecond: 421_000_000); - check!("42195", [fix!(Nanosecond9NoDot)]; nanosecond: 421_950_000); + check!("", [fix!(Nanosecond9NoDot)]; TOO_SHORT); + check!("42195", [fix!(Nanosecond9NoDot)]; TOO_SHORT); check!("421950803", [fix!(Nanosecond9NoDot)]; nanosecond: 421_950_803); - check!("421950803547", [fix!(Nanosecond9NoDot)]; nanosecond: 421_950_803); - // check!("421950803547", [fix!(Nanosecond9NoDot), num!(Second)]; nanosecond: 421_950_803, second: 54); // don't skip digits that come after the 9 - check!("000000003547", [fix!(Nanosecond9NoDot)]; nanosecond: 3); - check!("000000000547", [fix!(Nanosecond9NoDot)]; nanosecond: 0); - check!("4x", [fix!(Nanosecond9NoDot)]; TOO_LONG); - check!(" 4", [fix!(Nanosecond9NoDot)]; INVALID); - check!(".421", [fix!(Nanosecond9NoDot)]; INVALID); + check!("000000003", [fix!(Nanosecond9NoDot)]; nanosecond: 3); + check!("42195080354", [fix!(Nanosecond9NoDot), num!(Second)]; nanosecond: 421_950_803, second: 54); // don't skip digits that come after the 9 + check!("421950803547", [fix!(Nanosecond9NoDot)]; TOO_LONG); + check!("000000000", [fix!(Nanosecond9NoDot)]; nanosecond: 0); + check!("00000000x", [fix!(Nanosecond9NoDot)]; INVALID); + check!(" 4", [fix!(Nanosecond9NoDot)]; INVALID); + check!(".42100000", [fix!(Nanosecond9NoDot)]; INVALID); // fixed: timezone offsets check!("+00:00", [fix!(TimezoneOffset)]; offset: 0); diff --git a/src/format/scan.rs b/src/format/scan.rs index 4a006419..f2f97d91 100644 --- a/src/format/scan.rs +++ b/src/format/scan.rs @@ -66,6 +66,20 @@ pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> { Ok((s, v)) } +/// Tries to consume a fixed number of digits as a fractional second. +/// Returns the number of whole nanoseconds (0--999,999,999). +pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> { + // record the number of digits consumed for later scaling. + let (s, v) = try!(number(s, digits, digits)); + + // scale the number accordingly. + static SCALE: [i64; 10] = [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, + 1_000, 100, 10, 1]; + let v = try!(v.checked_mul(SCALE[digits]).ok_or(OUT_OF_RANGE)); + + Ok((s, v)) +} + /// Tries to parse the month index (0 through 11) with the first three ASCII letters. pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> { if s.len() < 3 { return Err(TOO_SHORT); }