]> git.lizzy.rs Git - rust.git/commitdiff
Add fractional second support to str{p,f}time
authorSteven Fackler <sfackler@gmail.com>
Fri, 6 Sep 2013 06:13:02 +0000 (23:13 -0700)
committerSteven Fackler <sfackler@gmail.com>
Fri, 6 Sep 2013 06:19:41 +0000 (23:19 -0700)
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

index 6119170f13057f75e5fe8ea63f425b4a24e9ba60..7515326a0dbb22ad3968ad0e3ad42fbee9169869 100644 (file)
@@ -321,6 +321,33 @@ fn match_digits(ss: &str, pos: uint, digits: uint, ws: bool)
         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 parse_type(s: &str, pos: uint, ch: char, tm: &mut Tm)
             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 parse_type(ch: char, 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 @@ fn test_strptime() {
           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 @@ fn test_strptime() {
             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 @@ fn test_strftime() {
         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");