path: root/date.c
diff options
authorĐoàn Trần Công Danh <>2020-04-24 15:07:31 (GMT)
committerJunio C Hamano <>2020-04-24 21:06:09 (GMT)
commitb784840ca84c708708d1ab0b872eb3a6fb3200b5 (patch)
treeb7703b71375dfb0c06989cfe5ef3fdbf0e37ae1b /date.c
parent4f89f4fc9ac494fde3f3bede19a7599f77afe8dc (diff)
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 <> Helped-by: Junio C Hamano <> Signed-off-by: Đoàn Trần Công Danh <> Signed-off-by: Junio C Hamano <>
Diffstat (limited to 'date.c')
1 files changed, 12 insertions, 0 deletions
diff --git a/date.c b/date.c
index fa39e5e..2c9071d 100644
--- a/date.c
+++ b/date.c
@@ -553,6 +553,11 @@ static int set_time(long hour, long minute, long second, struct tm *tm)
return -1;
+static int is_date_known(struct tm *tm)
+ return tm->tm_year != -1 && tm->tm_mon != -1 && tm->tm_mday != -1;
static int match_multi_number(timestamp_t num, char c, const char *date,
char *end, struct tm *tm, time_t now)
@@ -571,6 +576,13 @@ static int match_multi_number(timestamp_t num, char c, const char *date,
if (num3 < 0)
num3 = 0;
if (set_time(num, num2, num3, tm) == 0) {
+ /*
+ * If %H:%M:%S was just parsed followed by: .<num4>
+ * Consider (& discard) it as fractional second
+ * if %Y%m%d is parsed before.
+ */
+ if (*end == '.' && isdigit(end[1]) && is_date_known(tm))
+ strtol(end + 1, &end, 10);
return 0;