From 3c30ecb706dc32257d1ec6bdcb0c88eb924483dd Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 5 Sep 2013 23:13:02 -0700 Subject: [PATCH] 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 --- src/libextra/time.rs | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/libextra/time.rs b/src/libextra/time.rs index 6119170f130..7515326a0db 100644 --- a/src/libextra/time.rs +++ b/src/libextra/time.rs @@ -321,6 +321,33 @@ fn do_strptime(s: &str, format: &str) -> Result { 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 { 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");