Documentation: minor grammatical fixes in git-check-attr.txt
[gitweb.git] / date.c
diff --git a/date.c b/date.c
index 35a52576c53e5e1406d40ed4402b8834a29b9f0e..1165d30adfa783f7cbaa871e38a1370f9f9c4996 100644 (file)
--- a/date.c
+++ b/date.c
@@ -89,6 +89,11 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
        struct tm *tm;
        static char timebuf[200];
 
+       if (mode == DATE_RAW) {
+               snprintf(timebuf, sizeof(timebuf), "%lu %+05d", time, tz);
+               return timebuf;
+       }
+
        if (mode == DATE_RELATIVE) {
                unsigned long diff;
                struct timeval now;
@@ -128,7 +133,25 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
                        snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30);
                        return timebuf;
                }
-               /* Else fall back on absolute format.. */
+               /* Give years and months for 5 years or so */
+               if (diff < 1825) {
+                       unsigned long years = (diff + 183) / 365;
+                       unsigned long months = (diff % 365 + 15) / 30;
+                       int n;
+                       n = snprintf(timebuf, sizeof(timebuf), "%lu year%s",
+                                       years, (years > 1 ? "s" : ""));
+                       if (months)
+                               snprintf(timebuf + n, sizeof(timebuf) - n,
+                                       ", %lu month%s ago",
+                                       months, (months > 1 ? "s" : ""));
+                       else
+                               snprintf(timebuf + n, sizeof(timebuf) - n,
+                                       " ago");
+                       return timebuf;
+               }
+               /* Otherwise, just years. Centuries is probably overkill. */
+               snprintf(timebuf, sizeof(timebuf), "%lu years ago", (diff + 183) / 365);
+               return timebuf;
        }
 
        if (mode == DATE_LOCAL)
@@ -402,6 +425,15 @@ static int match_multi_number(unsigned long num, char c, const char *date, char
        return end - date;
 }
 
+/* Have we filled in any part of the time/date yet? */
+static inline int nodate(struct tm *tm)
+{
+       return tm->tm_year < 0 &&
+               tm->tm_mon < 0 &&
+               tm->tm_mday < 0 &&
+               !(tm->tm_hour | tm->tm_min | tm->tm_sec);
+}
+
 /*
  * We've seen a digit. Time? Year? Date?
  */
@@ -418,7 +450,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
         * more than 8 digits. This is because we don't want to rule out
         * numbers like 20070606 as a YYYYMMDD date.
         */
-       if (num >= 100000000) {
+       if (num >= 100000000 && nodate(tm)) {
                time_t time = num;
                if (gmtime_r(&time, tm)) {
                        *tm_gmt = 1;
@@ -462,6 +494,13 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
                return n;
        }
 
+       /*
+        * Ignore lots of numerals. We took care of 4-digit years above.
+        * Days or months must be one or two digits.
+        */
+       if (n > 2)
+               return n;
+
        /*
         * NOTE! We will give precedence to day-of-month over month or
         * year numbers in the 1-12 range. So 05 is always "mday 5",
@@ -488,10 +527,6 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
 
        if (num > 0 && num < 32) {
                tm->tm_mday = num;
-       } else if (num > 1900) {
-               tm->tm_year = num - 1900;
-       } else if (num > 70) {
-               tm->tm_year = num;
        } else if (num > 0 && num < 13) {
                tm->tm_mon = num-1;
        }
@@ -603,6 +638,8 @@ enum date_mode parse_date_format(const char *format)
                return DATE_LOCAL;
        else if (!strcmp(format, "default"))
                return DATE_NORMAL;
+       else if (!strcmp(format, "raw"))
+               return DATE_RAW;
        else
                die("unknown date format %s", format);
 }
@@ -823,7 +860,9 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
                }
        }
 
-       *num = number;
+       /* Accept zero-padding only for small numbers ("Dec 02", never "Dec 0002") */
+       if (date[0] != '0' || end - date <= 2)
+               *num = number;
        return end;
 }