From: Phil Hord <phil.hord@xxxxxxxxx> ISO 8601 permits "reduced precision" time representations to omit the seconds value or both the minutes and the seconds values. The abbreviate times could look like 17:45 or 1745 to omit the seconds, or simply as 17 to omit both the minutes and the seconds. parse_date_basic accepts the 17:45 format but it rejects the other two. Fix it to accept 4-digit and 2-digit time values when they follow a recognized date but no time has yet been parsed. Add tests for these formats and some others, with and without colons. Before this change: $ test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23 2022-12-13T23:00 -> 2022-12-14 07:00:00 +0000 2022-12-13T2300 -> 2022-12-14 03:00:55 +0000 2022-12-13T23 -> 2022-12-14 03:00:55 +0000 After this change: $ test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23 2022-12-13T23:00 -> 2022-12-14 07:00:00 +0000 2022-12-13T2300 -> 2022-12-14 07:00:00 +0000 2022-12-13T23 -> 2022-12-14 07:00:00 +0000 Note: ISO 8601 also allows reduced precision date strings such as "2022-12" and "2022". This patch does not attempt to address these. Reported-by: Pat LaVarre <plavarre@xxxxxxxxxxxxxxx> Signed-off-by: Phil Hord <phil.hord@xxxxxxxxx> --- date.c | 22 +++++++++++++++++++++- t/t0006-date.sh | 6 ++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/date.c b/date.c index 53bd6a7932..b011b9d6b3 100644 --- a/date.c +++ b/date.c @@ -638,6 +638,18 @@ static inline int nodate(struct tm *tm) tm->tm_sec) < 0; } +/* + * Have we filled in any part of the time yet? + * We just do a binary 'and' to see if the sign bit + * is set in all the values. + */ +static inline int notime(struct tm *tm) +{ + return (tm->tm_hour & + tm->tm_min & + tm->tm_sec) < 0; +} + /* * We've seen a digit. Time? Year? Date? */ @@ -689,7 +701,11 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt /* 8 digits, compact style of ISO-8601's date: YYYYmmDD */ /* 6 digits, compact style of ISO-8601's time: HHMMSS */ - if (n == 8 || n == 6) { + /* 4 digits, compact style of ISO-8601's time: HHMM */ + /* 2 digits, compact style of ISO-8601's time: HH */ + if (n == 8 || n == 6 || + (!nodate(tm) && notime(tm) && + (n == 4 || n == 2))) { unsigned int num1 = num / 10000; unsigned int num2 = (num % 10000) / 100; unsigned int num3 = num % 100; @@ -698,6 +714,10 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt else if (n == 6 && set_time(num1, num2, num3, tm) == 0 && *end == '.' && isdigit(end[1])) strtoul(end + 1, &end, 10); + else if (n == 4) + set_time(num2, num3, 0, tm); + else if (n == 2) + set_time(num3, 0, 0, tm); return end - date; } diff --git a/t/t0006-date.sh b/t/t0006-date.sh index 2490162071..16fb0bf4bd 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -88,6 +88,12 @@ check_parse 2008-02-14 bad check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000' check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500' check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500' +check_parse '20080214T20:30:45' '2008-02-14 20:30:45 +0000' +check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000' +check_parse '20080214T20' '2008-02-14 20:00:00 +0000' +check_parse '20080214T203045' '2008-02-14 20:30:45 +0000' +check_parse '20080214T2030' '2008-02-14 20:30:00 +0000' +check_parse '20080214T20' '2008-02-14 20:00:00 +0000' check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400' check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400' check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400' -- 2.39.0.56.g57e2c6ebbe