about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSteven Fackler <sfackler@gmail.com>2013-09-05 23:13:02 -0700
committerSteven Fackler <sfackler@gmail.com>2013-09-05 23:19:41 -0700
commit3c30ecb706dc32257d1ec6bdcb0c88eb924483dd (patch)
treed86421c6b40f9f4e67f35c8d80d4a52ee791459f
parenta980f28028f4712efbb89017bd23c25183fddbfe (diff)
downloadrust-3c30ecb706dc32257d1ec6bdcb0c88eb924483dd.tar.gz
rust-3c30ecb706dc32257d1ec6bdcb0c88eb924483dd.zip
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
-rw-r--r--src/libextra/time.rs40
1 files 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<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");