From 38035cf4a51c48cccf6c5e3977130261bc0c03a7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Apr 2006 15:31:12 -0700 Subject: [PATCH] date parsing: be friendlier to our European friends. This does three things, only applies to cases where the user manually tries to override the author/commit time by environment variables, with non-ISO, non-2822 format date-string: - Refuses to use the interpretation to put the date in the future; recent kernel history has a commit made with 10/03/2006 which is recorded as October 3rd. - Adds '.' as the possible year-month-date separator. We learned from our European friends on the #git channel that dd.mm.yyyy is the norm there. - When the separator is '.', we prefer dd.mm.yyyy over mm.dd.yyyy; otherwise mm/dd/yy[yy] takes precedence over dd/mm/yy[yy]. Signed-off-by: Junio C Hamano --- date.c | 83 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/date.c b/date.c index 376d25d241..034d7228bf 100644 --- a/date.c +++ b/date.c @@ -197,26 +197,43 @@ static int match_alpha(const char *date, struct tm *tm, int *offset) return skip_alpha(date); } -static int is_date(int year, int month, int day, struct tm *tm) +static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm) { if (month > 0 && month < 13 && day > 0 && day < 32) { - if (year == -1) { - tm->tm_mon = month-1; - tm->tm_mday = day; - return 1; - } - if (year >= 1970 && year < 2100) { - year -= 1900; - } else if (year > 70 && year < 100) { - /* ok */ - } else if (year < 38) { - year += 100; - } else - return 0; + struct tm check = *tm; + struct tm *r = (now_tm ? &check : tm); + time_t specified; - tm->tm_mon = month-1; - tm->tm_mday = day; - tm->tm_year = year; + r->tm_mon = month - 1; + r->tm_mday = day; + if (year == -1) { + if (!now_tm) + return 1; + r->tm_year = now_tm->tm_year; + } + else if (year >= 1970 && year < 2100) + r->tm_year = year - 1900; + else if (year > 70 && year < 100) + r->tm_year = year; + else if (year < 38) + r->tm_year = year + 100; + else + return 0; + if (!now_tm) + return 1; + + specified = my_mktime(r); + + /* Be it commit time or author time, it does not make + * sense to specify timestamp way into the future. Make + * sure it is not later than ten days from now... + */ + if (now + 10*24*3600 < specified) + return 0; + tm->tm_mon = r->tm_mon; + tm->tm_mday = r->tm_mday; + if (year != -1) + tm->tm_year = r->tm_year; return 1; } return 0; @@ -224,6 +241,9 @@ static int is_date(int year, int month, int day, struct tm *tm) static int match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm) { + time_t now; + struct tm now_tm; + struct tm *refuse_future; long num2, num3; num2 = strtol(end+1, &end, 10); @@ -246,19 +266,33 @@ static int match_multi_number(unsigned long num, char c, const char *date, char case '-': case '/': + case '.': + now = time(NULL); + refuse_future = NULL; + if (gmtime_r(&now, &now_tm)) + refuse_future = &now_tm; + if (num > 70) { /* yyyy-mm-dd? */ - if (is_date(num, num2, num3, tm)) + if (is_date(num, num2, num3, refuse_future, now, tm)) break; /* yyyy-dd-mm? */ - if (is_date(num, num3, num2, tm)) + if (is_date(num, num3, num2, refuse_future, now, tm)) break; } - /* mm/dd/yy ? */ - if (is_date(num3, num, num2, tm)) + /* Our eastern European friends say dd.mm.yy[yy] + * is the norm there, so giving precedence to + * mm/dd/yy[yy] form only when separator is not '.' + */ + if (c != '.' && + is_date(num3, num, num2, refuse_future, now, tm)) break; - /* dd/mm/yy ? */ - if (is_date(num3, num2, num, tm)) + /* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */ + if (is_date(num3, num2, num, refuse_future, now, tm)) + break; + /* Funny European mm.dd.yy */ + if (c == '.' && + is_date(num3, num, num2, refuse_future, now, tm)) break; return 0; } @@ -288,10 +322,11 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt } /* - * Check for special formats: num[:-/]num[same]num + * Check for special formats: num[-.:/]num[same]num */ switch (*end) { case ':': + case '.': case '/': case '-': if (isdigit(end[1])) {