From 084f2867b6e10aa3f9777c57ad82a4c843e4bbe3 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 8 May 2022 13:56:50 +0300 Subject: [PATCH] time: let time.parse_rfc3339('2015-01-06T15:47:32.080254511Z') succeed (dockerd timestamps, Go's RFC3339Nano). --- vlib/time/parse.c.v | 48 ++++++++++++++++++++++++++++++------------ vlib/time/parse_test.v | 23 ++++++++++++++++++++ 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/vlib/time/parse.c.v b/vlib/time/parse.c.v index 593e83b1fc..e7c2be1cc5 100644 --- a/vlib/time/parse.c.v +++ b/vlib/time/parse.c.v @@ -193,24 +193,46 @@ fn parse_iso8601_time(s string) ?(int, int, int, int, i64, bool) { hour_ := 0 minute_ := 0 second_ := 0 - microsecond_ := 0 + mut microsecond_ := 0 + mut nanosecond_ := 0 plus_min_z := `a` offset_hour := 0 offset_minute := 0 - mut count := unsafe { - C.sscanf(&char(s.str), c'%2d:%2d:%2d.%6d%c%2d:%2d', &hour_, &minute_, &second_, - µsecond_, &char(&plus_min_z), &offset_hour, &offset_minute) + mut count := 0 + count = unsafe { + C.sscanf(&char(s.str), c'%2d:%2d:%2d.%9d%c', &hour_, &minute_, &second_, &nanosecond_, + &char(&plus_min_z)) } - // Missread microsecond ([Sec Hour Minute].len == 3 < 4) - if count < 4 { - count = unsafe { - C.sscanf(&char(s.str), c'%2d:%2d:%2d%c%2d:%2d', &hour_, &minute_, &second_, - &char(&plus_min_z), &offset_hour, &offset_minute) + if count == 5 && plus_min_z == `Z` { + // normalise the nanoseconds: + mut ndigits := 0 + if mut pos := s.index('.') { + pos++ + for ; pos < s.len && s[pos].is_digit(); pos++ { + ndigits++ + } + } + for ndigits < 9 { + nanosecond_ *= 10 + ndigits++ + } + microsecond_ = nanosecond_ / 1000 + } else { + count = unsafe { + C.sscanf(&char(s.str), c'%2d:%2d:%2d.%6d%c%2d:%2d', &hour_, &minute_, &second_, + µsecond_, &char(&plus_min_z), &offset_hour, &offset_minute) + } + // Missread microsecond ([Sec Hour Minute].len == 3 < 4) + if count < 4 { + count = unsafe { + C.sscanf(&char(s.str), c'%2d:%2d:%2d%c%2d:%2d', &hour_, &minute_, &second_, + &char(&plus_min_z), &offset_hour, &offset_minute) + } + count++ // Increment count because skipped microsecond + } + if count < 4 { + return error_invalid_time(10) } - count++ // Increment count because skipped microsecond - } - if count < 4 { - return error_invalid_time(10) } is_local_time := plus_min_z == `a` && count == 4 is_utc := plus_min_z == `Z` && count == 5 diff --git a/vlib/time/parse_test.v b/vlib/time/parse_test.v index f5740057cf..f544e4f95d 100644 --- a/vlib/time/parse_test.v +++ b/vlib/time/parse_test.v @@ -3,6 +3,7 @@ import time fn test_parse() { s := '2018-01-27 12:48:34' t := time.parse(s) or { + eprintln('> failing format: $s | err: $err') assert false return } @@ -23,6 +24,7 @@ fn test_parse_invalid() { fn test_parse_rfc2822() { s1 := 'Thu, 12 Dec 2019 06:07:45 GMT' t1 := time.parse_rfc2822(s1) or { + eprintln('> failing format: $s1 | err: $err') assert false return } @@ -31,6 +33,7 @@ fn test_parse_rfc2822() { assert t1.unix == 1576130865 s2 := 'Thu 12 Dec 2019 06:07:45 +0800' t2 := time.parse_rfc2822(s2) or { + eprintln('> failing format: $s2 | err: $err') assert false return } @@ -67,6 +70,7 @@ fn test_parse_iso8601() { ] for i, format in formats { t := time.parse_iso8601(format) or { + eprintln('>>> failing format: $format | err: $err') assert false continue } @@ -90,6 +94,7 @@ fn test_parse_iso8601() { fn test_parse_iso8601_local() { format := '2020-06-05T15:38:06.015959' t := time.parse_iso8601(format) or { + eprintln('> failing format: $format | err: $err') assert false return } @@ -127,6 +132,7 @@ fn test_parse_iso8601_invalid() { fn test_parse_iso8601_date_only() { format := '2020-06-05' t := time.parse_iso8601(format) or { + eprintln('> failing format: $format | err: $err') assert false return } @@ -161,3 +167,20 @@ fn test_invalid_dates_should_error_during_parse() { check_invalid_date('2008-12-01 00:60:00') check_invalid_date('2008-12-01 00:01:60') } + +fn test_parse_rfc3339() { + pairs := [ + ['2015-01-06T15:47:32.080254511Z', '2015-01-06 15:47:32.080254'], + ['2015-01-06T15:47:32.072697474Z', '2015-01-06 15:47:32.072697'], + ] + for pair in pairs { + input, expected := pair[0], pair[1] + res := time.parse_rfc3339(input) or { + eprintln('>>> failing input: $input | err: $err') + assert false + return + } + output := res.format_ss_micro() + assert expected == output + } +}