Skip to content

Commit 3c30ecb

Browse files
committed
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 rust-lang#2350
1 parent a980f28 commit 3c30ecb

File tree

1 file changed

+37
-3
lines changed

1 file changed

+37
-3
lines changed

src/libextra/time.rs

+37-3
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,33 @@ fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
321321
Some((value, pos))
322322
}
323323

324+
fn match_fractional_seconds(ss: &str, pos: uint) -> (i32, uint) {
325+
let len = ss.len();
326+
let mut value = 0_i32;
327+
let mut multiplier = NSEC_PER_SEC / 10;
328+
let mut pos = pos;
329+
330+
loop {
331+
if pos >= len {
332+
break;
333+
}
334+
let range = ss.char_range_at(pos);
335+
336+
match range.ch {
337+
'0' .. '9' => {
338+
pos = range.next;
339+
// This will drop digits after the nanoseconds place
340+
let digit = range.ch as i32 - '0' as i32;
341+
value += digit * multiplier;
342+
multiplier /= 10;
343+
}
344+
_ => break
345+
}
346+
}
347+
348+
(value, pos)
349+
}
350+
324351
fn match_digits_in_range(ss: &str, pos: uint, digits: uint, ws: bool,
325352
min: i32, max: i32) -> Option<(i32, uint)> {
326353
match match_digits(ss, pos, digits, ws) {
@@ -441,6 +468,11 @@ fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
441468
Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) }
442469
None => Err(~"Invalid day of the month")
443470
},
471+
'f' => {
472+
let (val, pos) = match_fractional_seconds(s, pos);
473+
tm.tm_nsec = val;
474+
Ok(pos)
475+
}
444476
'F' => {
445477
parse_type(s, pos, 'Y', &mut *tm)
446478
.chain(|pos| parse_char(s, pos, '-'))
@@ -773,6 +805,7 @@ fn do_strftime(format: &str, tm: &Tm) -> ~str {
773805
}
774806
'd' => fmt!("%02d", tm.tm_mday as int),
775807
'e' => fmt!("%2d", tm.tm_mday as int),
808+
'f' => fmt!("%09d", tm.tm_nsec as int),
776809
'F' => {
777810
fmt!("%s-%s-%s",
778811
parse_type('Y', tm),
@@ -1011,12 +1044,12 @@ mod tests {
10111044
Err(_) => ()
10121045
}
10131046
1014-
let format = "%a %b %e %T %Y";
1047+
let format = "%a %b %e %T.%f %Y";
10151048
assert_eq!(strptime("", format), Err(~"Invalid time"));
10161049
assert!(strptime("Fri Feb 13 15:31:30", format)
10171050
== Err(~"Invalid time"));
10181051
1019-
match strptime("Fri Feb 13 15:31:30 2009", format) {
1052+
match strptime("Fri Feb 13 15:31:30.01234 2009", format) {
10201053
Err(e) => fail!(e),
10211054
Ok(ref tm) => {
10221055
assert!(tm.tm_sec == 30_i32);
@@ -1030,7 +1063,7 @@ mod tests {
10301063
assert!(tm.tm_isdst == 0_i32);
10311064
assert!(tm.tm_gmtoff == 0_i32);
10321065
assert!(tm.tm_zone == ~"");
1033-
assert!(tm.tm_nsec == 0_i32);
1066+
assert!(tm.tm_nsec == 12340000_i32);
10341067
}
10351068
}
10361069
@@ -1187,6 +1220,7 @@ mod tests {
11871220
assert_eq!(local.strftime("%D"), ~"02/13/09");
11881221
assert_eq!(local.strftime("%d"), ~"13");
11891222
assert_eq!(local.strftime("%e"), ~"13");
1223+
assert_eq!(local.strftime("%f"), ~"000054321");
11901224
assert_eq!(local.strftime("%F"), ~"2009-02-13");
11911225
// assert!(local.strftime("%G") == "2009");
11921226
// assert!(local.strftime("%g") == "09");

0 commit comments

Comments
 (0)