Add fractional second support to str{p,f}time

The ISO 8601 standard does not mandate any specific precision for
fractional seconds, so this accepts input of any length, ignoring the
part after the nanoseconds place. It may be more correct to round with
the tenths of nanoseconds digit, but then we'd have to deal with
carrying the round through the entire Tm struct (e.g. for a time like
Dec 31 11:59.999999999999).

%f is the format specifier that Python's datetime library uses for
0-padded microseconds so it seemed appropriate here.

cc #2350
This commit is contained in:
Steven Fackler 2013-09-05 23:13:02 -07:00
parent a980f28028
commit 3c30ecb706

View File

@ -321,6 +321,33 @@ fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
Some((value, pos))
}
fn match_fractional_seconds(ss: &str, pos: uint) -> (i32, uint) {
let len = ss.len();
let mut value = 0_i32;
let mut multiplier = NSEC_PER_SEC / 10;
let mut pos = pos;
loop {
if pos >= len {
break;
}
let range = ss.char_range_at(pos);
match range.ch {
'0' .. '9' => {
pos = range.next;
// This will drop digits after the nanoseconds place
let digit = range.ch as i32 - '0' as i32;
value += digit * multiplier;
multiplier /= 10;
}
_ => break
}
}
(value, pos)
}
fn match_digits_in_range(ss: &str, pos: uint, digits: uint, ws: bool,
min: i32, max: i32) -> Option<(i32, uint)> {
match match_digits(ss, pos, digits, ws) {
@ -441,6 +468,11 @@ fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) }
None => Err(~"Invalid day of the month")
},
'f' => {
let (val, pos) = match_fractional_seconds(s, pos);
tm.tm_nsec = val;
Ok(pos)
}
'F' => {
parse_type(s, pos, 'Y', &mut *tm)
.chain(|pos| parse_char(s, pos, '-'))
@ -773,6 +805,7 @@ fn do_strftime(format: &str, tm: &Tm) -> ~str {
}
'd' => fmt!("%02d", tm.tm_mday as int),
'e' => fmt!("%2d", tm.tm_mday as int),
'f' => fmt!("%09d", tm.tm_nsec as int),
'F' => {
fmt!("%s-%s-%s",
parse_type('Y', tm),
@ -1011,12 +1044,12 @@ mod tests {
Err(_) => ()
}
let format = "%a %b %e %T %Y";
let format = "%a %b %e %T.%f %Y";
assert_eq!(strptime("", format), Err(~"Invalid time"));
assert!(strptime("Fri Feb 13 15:31:30", format)
== Err(~"Invalid time"));
match strptime("Fri Feb 13 15:31:30 2009", format) {
match strptime("Fri Feb 13 15:31:30.01234 2009", format) {
Err(e) => fail!(e),
Ok(ref tm) => {
assert!(tm.tm_sec == 30_i32);
@ -1030,7 +1063,7 @@ mod tests {
assert!(tm.tm_isdst == 0_i32);
assert!(tm.tm_gmtoff == 0_i32);
assert!(tm.tm_zone == ~"");
assert!(tm.tm_nsec == 0_i32);
assert!(tm.tm_nsec == 12340000_i32);
}
}
@ -1187,6 +1220,7 @@ mod tests {
assert_eq!(local.strftime("%D"), ~"02/13/09");
assert_eq!(local.strftime("%d"), ~"13");
assert_eq!(local.strftime("%e"), ~"13");
assert_eq!(local.strftime("%f"), ~"000054321");
assert_eq!(local.strftime("%F"), ~"2009-02-13");
// assert!(local.strftime("%G") == "2009");
// assert!(local.strftime("%g") == "09");