@matheusbsbs / matheus-bagliano / commits / b784840ca8
Fork 0

date.c: skip fractional second part of ISO-8601

git-commit(1) says ISO-8601 is one of our supported date format. ISO-8601 allows timestamps to have a fractional number of seconds. We represent time only in terms of whole seconds, so we never bothered parsing fractional seconds. However, it's better for us to parse and throw away the fractional part than to refuse to parse the timestamp at all. And refusing parsing fractional second part may confuse the parse to think fractional and timezone as day and month in this example: 2008-02-14 20:30:45.019-04:00 While doing this, make sure that we only interpret the number after the second and the dot as fractional when and only when the date is known, since only ISO-8601 allows the fractional part, and we've taught our users to interpret "12:34:56.7.days.ago" as a way to specify a time relative to current time. Reported-by: Brian M. Carlson <sandals@crustytoothpaste.net> Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Đoàn Trần Công Danh committed Apr 24, 2020 at 22:07 UTC b784840ca84c708708d1ab0b872eb3a6fb3200b5
3 files changed +19 -1
Documentation/date-formats.txt
+4 -1
@@ -20,7 +20,10 @@ RFC 2822::
20 ISO 8601::
21 Time and date specified by the ISO 8601 standard, for example
22 `2005-04-07T22:13:13`. The parser accepts a space instead of the
23 - `T` character as well.
23 + `T` character as well. Fractional parts of a second will be ignored,
24 + for example `2005-04-07T22:13:13.019` will be treated as
25 + `2005-04-07T22:13:13`
26 +
27 +
28 NOTE: In addition, the date part is accepted in the following formats:
29 `YYYY.MM.DD`, `MM/DD/YYYY` and `DD.MM.YYYY`.
date.c
+12
@@ -553,6 +553,11 @@ static int set_time(long hour, long minute, long second, struct tm *tm)
553 return -1;
554 }
555
556 +static int is_date_known(struct tm *tm)
557 +{
558 + return tm->tm_year != -1 && tm->tm_mon != -1 && tm->tm_mday != -1;
559 +}
560 +
561 static int match_multi_number(timestamp_t num, char c, const char *date,
562 char *end, struct tm *tm, time_t now)
563 {
@@ -571,6 +576,13 @@ static int match_multi_number(timestamp_t num, char c, const char *date,
576 if (num3 < 0)
577 num3 = 0;
578 if (set_time(num, num2, num3, tm) == 0) {
579 + /*
580 + * If %H:%M:%S was just parsed followed by: .<num4>
581 + * Consider (& discard) it as fractional second
582 + * if %Y%m%d is parsed before.
583 + */
584 + if (*end == '.' && isdigit(end[1]) && is_date_known(tm))
585 + strtol(end + 1, &end, 10);
586 break;
587 }
588 return 0;
t/t0006-date.sh
+3
@@ -81,6 +81,8 @@ check_parse 2008-02 bad
81 check_parse 2008-02-14 bad
82 check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
83 check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
84 +check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
85 +check_parse '2008-02-14 20:30:45.019-04:00' '2008-02-14 20:30:45 -0400'
86 check_parse '2008-02-14 20:30:45 -0015' '2008-02-14 20:30:45 -0015'
87 check_parse '2008-02-14 20:30:45 -5' '2008-02-14 20:30:45 +0000'
88 check_parse '2008-02-14 20:30:45 -5:' '2008-02-14 20:30:45 +0000'
@@ -103,6 +105,7 @@ check_approxidate 5.seconds.ago '2009-08-30 19:19:55'
105 check_approxidate 10.minutes.ago '2009-08-30 19:10:00'
106 check_approxidate yesterday '2009-08-29 19:20:00'
107 check_approxidate 3.days.ago '2009-08-27 19:20:00'
108 +check_approxidate '12:34:56.3.days.ago' '2009-08-27 12:34:56'
109 check_approxidate 3.weeks.ago '2009-08-09 19:20:00'
110 check_approxidate 3.months.ago '2009-05-30 19:20:00'
111 check_approxidate 2.years.3.months.ago '2007-05-30 19:20:00'