]> git.ipfire.org Git - thirdparty/git.git/blobdiff - date.c
Merge branch 'js/reflog-anonymize-for-clone-and-fetch'
[thirdparty/git.git] / date.c
diff --git a/date.c b/date.c
index 9c5870e102951ed25f5def2103dab15bf8ed5c99..f9ea807b3a9f9ee64a4173fa923f31b00455e840 100644 (file)
--- a/date.c
+++ b/date.c
@@ -64,16 +64,16 @@ static time_t gm_time_t(timestamp_t time, int tz)
  * thing, which means that tz -0100 is passed in as the integer -100,
  * even though it means "sixty minutes off"
  */
-static struct tm *time_to_tm(timestamp_t time, int tz)
+static struct tm *time_to_tm(timestamp_t time, int tz, struct tm *tm)
 {
        time_t t = gm_time_t(time, tz);
-       return gmtime(&t);
+       return gmtime_r(&t, tm);
 }
 
-static struct tm *time_to_tm_local(timestamp_t time)
+static struct tm *time_to_tm_local(timestamp_t time, struct tm *tm)
 {
        time_t t = time;
-       return localtime(&t);
+       return localtime_r(&t, tm);
 }
 
 /*
@@ -128,16 +128,17 @@ static void get_time(struct timeval *now)
                gettimeofday(now, NULL);
 }
 
-void show_date_relative(timestamp_t time,
-                       const struct timeval *now,
-                       struct strbuf *timebuf)
+void show_date_relative(timestamp_t time, struct strbuf *timebuf)
 {
+       struct timeval now;
        timestamp_t diff;
-       if (now->tv_sec < time) {
+
+       get_time(&now);
+       if (now.tv_sec < time) {
                strbuf_addstr(timebuf, _("in the future"));
                return;
        }
-       diff = now->tv_sec - time;
+       diff = now.tv_sec - time;
        if (diff < 90) {
                strbuf_addf(timebuf,
                         Q_("%"PRItime" second ago", "%"PRItime" seconds ago", diff), diff);
@@ -240,9 +241,7 @@ static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm
 
        /* Show "today" times as just relative times */
        if (hide.wday) {
-               struct timeval now;
-               get_time(&now);
-               show_date_relative(time, &now, buf);
+               show_date_relative(time, buf);
                return;
        }
 
@@ -284,6 +283,7 @@ static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm
 const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
 {
        struct tm *tm;
+       struct tm tmbuf = { 0 };
        struct tm human_tm = { 0 };
        int human_tz = -1;
        static struct strbuf timebuf = STRBUF_INIT;
@@ -313,20 +313,17 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
        }
 
        if (mode->type == DATE_RELATIVE) {
-               struct timeval now;
-
                strbuf_reset(&timebuf);
-               get_time(&now);
-               show_date_relative(time, &now, &timebuf);
+               show_date_relative(time, &timebuf);
                return timebuf.buf;
        }
 
        if (mode->local)
-               tm = time_to_tm_local(time);
+               tm = time_to_tm_local(time, &tmbuf);
        else
-               tm = time_to_tm(time, tz);
+               tm = time_to_tm(time, tz, &tmbuf);
        if (!tm) {
-               tm = time_to_tm(0, 0);
+               tm = time_to_tm(0, 0, &tmbuf);
                tz = 0;
        }
 
@@ -500,7 +497,7 @@ 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 *now_tm, time_t now, struct tm *tm)
+static int set_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) {
                struct tm check = *tm;
@@ -521,9 +518,9 @@ static int is_date(int year, int month, int day, struct tm *now_tm, time_t now,
                else if (year < 38)
                        r->tm_year = year + 100;
                else
-                       return 0;
+                       return -1;
                if (!now_tm)
-                       return 1;
+                       return 0;
 
                specified = tm_to_time_t(r);
 
@@ -532,14 +529,33 @@ static int is_date(int year, int month, int day, struct tm *now_tm, time_t now,
                 * sure it is not later than ten days from now...
                 */
                if ((specified != -1) && (now + 10*24*3600 < specified))
-                       return 0;
+                       return -1;
                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;
        }
-       return 0;
+       return -1;
+}
+
+static int set_time(long hour, long minute, long second, struct tm *tm)
+{
+       /* We accept 61st second because of leap second */
+       if (0 <= hour && hour <= 24 &&
+           0 <= minute && minute < 60 &&
+           0 <= second && second <= 60) {
+               tm->tm_hour = hour;
+               tm->tm_min = minute;
+               tm->tm_sec = second;
+               return 0;
+       }
+       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,
@@ -559,10 +575,14 @@ static int match_multi_number(timestamp_t num, char c, const char *date,
        case ':':
                if (num3 < 0)
                        num3 = 0;
-               if (num < 25 && num2 >= 0 && num2 < 60 && num3 >= 0 && num3 <= 60) {
-                       tm->tm_hour = num;
-                       tm->tm_min = num2;
-                       tm->tm_sec = num3;
+               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);
                        break;
                }
                return 0;
@@ -578,10 +598,10 @@ static int match_multi_number(timestamp_t num, char c, const char *date,
 
                if (num > 70) {
                        /* yyyy-mm-dd? */
-                       if (is_date(num, num2, num3, NULL, now, tm))
+                       if (set_date(num, num2, num3, NULL, now, tm) == 0)
                                break;
                        /* yyyy-dd-mm? */
-                       if (is_date(num, num3, num2, NULL, now, tm))
+                       if (set_date(num, num3, num2, NULL, now, tm) == 0)
                                break;
                }
                /* Our eastern European friends say dd.mm.yy[yy]
@@ -589,14 +609,14 @@ static int match_multi_number(timestamp_t num, char c, const char *date,
                 * mm/dd/yy[yy] form only when separator is not '.'
                 */
                if (c != '.' &&
-                   is_date(num3, num, num2, refuse_future, now, tm))
+                   set_date(num3, num, num2, refuse_future, now, tm) == 0)
                        break;
                /* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */
-               if (is_date(num3, num2, num, refuse_future, now, tm))
+               if (set_date(num3, num2, num, refuse_future, now, tm) == 0)
                        break;
                /* Funny European mm.dd.yy */
                if (c == '.' &&
-                   is_date(num3, num, num2, refuse_future, now, tm))
+                   set_date(num3, num, num2, refuse_future, now, tm) == 0)
                        break;
                return 0;
        }
@@ -667,6 +687,20 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
                n++;
        } while (isdigit(date[n]));
 
+       /* 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) {
+               unsigned int num1 = num / 10000;
+               unsigned int num2 = (num % 10000) / 100;
+               unsigned int num3 = num % 100;
+               if (n == 8)
+                       set_date(num1, num2, num3, NULL, time(NULL), tm);
+               else if (n == 6 && set_time(num1, num2, num3, tm) == 0 &&
+                        *end == '.' && isdigit(end[1]))
+                       strtoul(end + 1, &end, 10);
+               return end - date;
+       }
+
        /* Four-digit year or a timezone? */
        if (n == 4) {
                if (num <= 1400 && *offset == -1) {
@@ -921,6 +955,10 @@ static enum date_mode_type parse_date_type(const char *format, const char **end)
                return DATE_UNIX;
        if (skip_prefix(format, "format", end))
                return DATE_STRFTIME;
+       /*
+        * Please update $__git_log_date_formats in
+        * git-completion.bash when you add new formats.
+        */
 
        die("unknown date format %s", format);
 }
@@ -959,10 +997,11 @@ void datestamp(struct strbuf *out)
 {
        time_t now;
        int offset;
+       struct tm tm = { 0 };
 
        time(&now);
 
-       offset = tm_to_time_t(localtime(&now)) - now;
+       offset = tm_to_time_t(localtime_r(&now, &tm)) - now;
        offset /= 60;
 
        date_string(now, offset, out);
@@ -1284,15 +1323,18 @@ static timestamp_t approxidate_str(const char *date,
        return (timestamp_t)update_tm(&tm, &now, 0);
 }
 
-timestamp_t approxidate_relative(const char *date, const struct timeval *tv)
+timestamp_t approxidate_relative(const char *date)
 {
+       struct timeval tv;
        timestamp_t timestamp;
        int offset;
        int errors = 0;
 
        if (!parse_date_basic(date, &timestamp, &offset))
                return timestamp;
-       return approxidate_str(date, tv, &errors);
+
+       get_time(&tv);
+       return approxidate_str(date, (const struct timeval *) &tv, &errors);
 }
 
 timestamp_t approxidate_careful(const char *date, int *error_ret)