date.c: allow ISO 8601 reduced precision times

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.
Change it to accept 4-digit and 2-digit time values when they follow a
recognized date and a 'T'.

Before this change:

$ TZ=UTC test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23
2022-12-13T23:00 -> 2022-12-13 23:00:00 +0000
2022-12-13T2300 -> 2022-12-13 23:54:13 +0000
2022-12-13T23 -> 2022-12-13 23:54:13 +0000

After this change:

$ TZ=UTC helper/test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23
2022-12-13T23:00 -> 2022-12-13 23:00:00 +0000
2022-12-13T2300 -> 2022-12-13 23:00:00 +0000
2022-12-13T23 -> 2022-12-13 23: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@purestorage.com>
Signed-off-by: Phil Hord <phil.hord@gmail.com>
Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Đoàn Trần Công Danh 2023-01-11 07:10:03 +07:00 committed by Junio C Hamano
parent a38d39a4c5
commit b56be49984
2 changed files with 45 additions and 0 deletions

37
date.c
View File

@ -493,6 +493,12 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)
return 2;
}
/* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */
if (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) {
tm->tm_min = tm->tm_sec = 0;
return 1;
}
/* BAD CRAP */
return skip_alpha(date);
}
@ -638,6 +644,18 @@ static inline int nodate(struct tm *tm)
tm->tm_sec) < 0;
}
/*
* Have we seen an ISO-8601-alike date, i.e. 20220101T0,
* In which, hour is still unset,
* and minutes and second has been set to 0.
*/
static inline int maybeiso8601(struct tm *tm)
{
return tm->tm_hour == -1 &&
tm->tm_min == 0 &&
tm->tm_sec == 0;
}
/*
* We've seen a digit. Time? Year? Date?
*/
@ -701,6 +719,25 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
return end - date;
}
/* reduced precision of ISO-8601's time: HHMM or HH */
if (maybeiso8601(tm)) {
unsigned int num1 = num;
unsigned int num2 = 0;
if (n == 4) {
num1 = num / 100;
num2 = num % 100;
}
if ((n == 4 || n == 2) && !nodate(tm) &&
set_time(num1, num2, 0, tm) == 0)
return n;
/*
* We thought this is an ISO-8601 time string,
* we set minutes and seconds to 0,
* turn out it isn't, rollback the change.
*/
tm->tm_min = tm->tm_sec = -1;
}
/* Four-digit year or a timezone? */
if (n == 4) {
if (num <= 1400 && *offset == -1) {

View File

@ -88,6 +88,13 @@ 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 '20080214T000000.20' '2008-02-14 00:00:00 +0000'
check_parse '20080214T00:00:00.20' '2008-02-14 00: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'
@ -99,6 +106,7 @@ check_parse '2008-02-14 20:30:45 -05' '2008-02-14 20:30:45 -0500'
check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000'
check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500'
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
check_parse 'Thu, 7 Apr 2005 15:14:13 -0700' '2005-04-07 15:14:13 -0700'
check_approxidate() {
echo "$1 -> $2 +0000" >expect