Take exactly num digits specified for nanosecond fixed

Error if it's too long or short, and don't take any more digits
This commit is contained in:
Evan Schwartz 2018-06-12 10:49:13 -04:00
parent 8a2b09962a
commit 937189ee52
2 changed files with 48 additions and 48 deletions

View File

@ -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);

View File

@ -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); }