diff options
Diffstat (limited to 'date.c')
-rw-r--r-- | date.c | 190 |
1 files changed, 131 insertions, 59 deletions
@@ -4,12 +4,16 @@ * Copyright (C) Linus Torvalds, 2005 */ -#include "cache.h" +#include "git-compat-util.h" +#include "date.h" +#include "gettext.h" +#include "pager.h" +#include "strbuf.h" /* * This is like mktime, but without normalization of tm_wday and tm_yday. */ -static time_t tm_to_time_t(const struct tm *tm) +time_t tm_to_time_t(const struct tm *tm) { static const int mdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 @@ -203,14 +207,13 @@ void show_date_relative(timestamp_t time, struct strbuf *timebuf) (diff + 183) / 365); } -struct date_mode *date_mode_from_type(enum date_mode_type type) +struct date_mode date_mode_from_type(enum date_mode_type type) { - static struct date_mode mode; + struct date_mode mode = DATE_MODE_INIT; if (type == DATE_STRFTIME) BUG("cannot create anonymous strftime date_mode struct"); mode.type = type; - mode.local = 0; - return &mode; + return mode; } static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm, int tz, struct tm *human_tm, int human_tz, int local) @@ -280,7 +283,7 @@ static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm strbuf_addf(buf, " %+05d", tz); } -const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) +const char *show_date(timestamp_t time, int tz, struct date_mode mode) { struct tm *tm; struct tm tmbuf = { 0 }; @@ -288,13 +291,13 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) int human_tz = -1; static struct strbuf timebuf = STRBUF_INIT; - if (mode->type == DATE_UNIX) { + if (mode.type == DATE_UNIX) { strbuf_reset(&timebuf); strbuf_addf(&timebuf, "%"PRItime, time); return timebuf.buf; } - if (mode->type == DATE_HUMAN) { + if (mode.type == DATE_HUMAN) { struct timeval now; get_time(&now); @@ -303,22 +306,22 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) human_tz = local_time_tzoffset(now.tv_sec, &human_tm); } - if (mode->local) + if (mode.local) tz = local_tzoffset(time); - if (mode->type == DATE_RAW) { + if (mode.type == DATE_RAW) { strbuf_reset(&timebuf); strbuf_addf(&timebuf, "%"PRItime" %+05d", time, tz); return timebuf.buf; } - if (mode->type == DATE_RELATIVE) { + if (mode.type == DATE_RELATIVE) { strbuf_reset(&timebuf); show_date_relative(time, &timebuf); return timebuf.buf; } - if (mode->local) + if (mode.local) tm = time_to_tm_local(time, &tmbuf); else tm = time_to_tm(time, tz, &tmbuf); @@ -328,35 +331,39 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) } strbuf_reset(&timebuf); - if (mode->type == DATE_SHORT) + if (mode.type == DATE_SHORT) strbuf_addf(&timebuf, "%04d-%02d-%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); - else if (mode->type == DATE_ISO8601) + else if (mode.type == DATE_ISO8601) strbuf_addf(&timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tz); - else if (mode->type == DATE_ISO8601_STRICT) { - char sign = (tz >= 0) ? '+' : '-'; - tz = abs(tz); - strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", + else if (mode.type == DATE_ISO8601_STRICT) { + strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - sign, tz / 100, tz % 100); - } else if (mode->type == DATE_RFC2822) + tm->tm_hour, tm->tm_min, tm->tm_sec); + if (tz == 0) { + strbuf_addch(&timebuf, 'Z'); + } else { + strbuf_addch(&timebuf, tz >= 0 ? '+' : '-'); + tz = abs(tz); + strbuf_addf(&timebuf, "%02d:%02d", tz / 100, tz % 100); + } + } else if (mode.type == DATE_RFC2822) strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d", weekday_names[tm->tm_wday], tm->tm_mday, month_names[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, tz); - else if (mode->type == DATE_STRFTIME) - strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, - !mode->local); + else if (mode.type == DATE_STRFTIME) + strbuf_addftime(&timebuf, mode.strftime_fmt, tm, tz, + !mode.local); else - show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode->local); + show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode.local); return timebuf.buf; } @@ -493,11 +500,17 @@ static int match_alpha(const char *date, struct tm *tm, int *offset) return 2; } + /* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */ + if (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) { + tm->tm_min = tm->tm_sec = 0; + return 1; + } + /* BAD CRAP */ 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; @@ -518,9 +531,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); @@ -529,14 +542,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, @@ -556,10 +588,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; @@ -575,10 +611,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] @@ -586,14 +622,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; } @@ -616,6 +652,18 @@ static inline int nodate(struct tm *tm) } /* + * Have we seen an ISO-8601-alike date, i.e. 20220101T0, + * In which, hour is still unset, + * and minutes and second has been set to 0. + */ +static inline int maybeiso8601(struct tm *tm) +{ + return tm->tm_hour == -1 && + tm->tm_min == 0 && + tm->tm_sec == 0; +} + +/* * We've seen a digit. Time? Year? Date? */ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt) @@ -664,6 +712,39 @@ 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; + } + + /* reduced precision of ISO-8601's time: HHMM or HH */ + if (maybeiso8601(tm)) { + unsigned int num1 = num; + unsigned int num2 = 0; + if (n == 4) { + num1 = num / 100; + num2 = num % 100; + } + if ((n == 4 || n == 2) && !nodate(tm) && + set_time(num1, num2, 0, tm) == 0) + return n; + /* + * We thought this is an ISO-8601 time string, + * we set minutes and seconds to 0, + * turn out it isn't, rollback the change. + */ + tm->tm_min = tm->tm_sec = -1; + } + /* Four-digit year or a timezone? */ if (n == 4) { if (num <= 1400 && *offset == -1) { @@ -871,7 +952,7 @@ int parse_expiry_date(const char *date, timestamp_t *timestamp) /* * We take over "now" here, which usually translates * to the current timestamp. This is because the user - * really means to expire everything she has done in + * really means to expire everything that was done in * the past, and by definition reflogs are the record * of the past, and there is nothing from the future * to be kept. @@ -956,6 +1037,11 @@ void parse_date_format(const char *format, struct date_mode *mode) die("unknown date format %s", format); } +void date_mode_release(struct date_mode *mode) +{ + free((char *)mode->strftime_fmt); +} + void datestamp(struct strbuf *out) { time_t now; @@ -1059,7 +1145,7 @@ static void date_tea(struct tm *tm, struct tm *now, int *num) date_time(tm, now, 17); } -static void date_pm(struct tm *tm, struct tm *now, int *num) +static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num) { int hour, n = *num; *num = 0; @@ -1073,7 +1159,7 @@ static void date_pm(struct tm *tm, struct tm *now, int *num) tm->tm_hour = (hour % 12) + 12; } -static void date_am(struct tm *tm, struct tm *now, int *num) +static void date_am(struct tm *tm, struct tm *now UNUSED, int *num) { int hour, n = *num; *num = 0; @@ -1087,7 +1173,7 @@ static void date_am(struct tm *tm, struct tm *now, int *num) tm->tm_hour = (hour % 12); } -static void date_never(struct tm *tm, struct tm *now, int *num) +static void date_never(struct tm *tm, struct tm *now UNUSED, int *num) { time_t n = 0; localtime_r(&n, tm); @@ -1286,20 +1372,6 @@ static timestamp_t approxidate_str(const char *date, return (timestamp_t)update_tm(&tm, &now, 0); } -timestamp_t approxidate_relative(const char *date) -{ - struct timeval tv; - timestamp_t timestamp; - int offset; - int errors = 0; - - if (!parse_date_basic(date, ×tamp, &offset)) - return timestamp; - - get_time(&tv); - return approxidate_str(date, (const struct timeval *) &tv, &errors); -} - timestamp_t approxidate_careful(const char *date, int *error_ret) { struct timeval tv; |