From: Junio C Hamano Date: Thu, 7 Feb 2019 06:05:24 +0000 (-0800) Subject: Merge branch 'lt/date-human' X-Git-Tag: v2.21.0-rc0~20 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/ecbe1beb8e41664ac00581234a449c4487600e1d Merge branch 'lt/date-human' A new date format "--date=human" that morphs its output depending on how far the time is from the current time has been introduced. "--date=auto" can be used to use this new format when the output is going to the pager or to the terminal and otherwise the default format. * lt/date-human: Add `human` date format tests. Add `human` format to test-tool Add 'human' date format documentation Replace the proposed 'auto' mode with 'auto:' Add 'human' date format --- ecbe1beb8e41664ac00581234a449c4487600e1d diff --cc cache.h index 400bc0ab25,34e54dee4d..ef9f3c4eaf --- a/cache.h +++ b/cache.h @@@ -1461,20 -1425,19 +1461,21 @@@ extern void *read_object_with_reference extern struct object *peel_to_type(const char *name, int namelen, struct object *o, enum object_type); +enum date_mode_type { + DATE_NORMAL = 0, ++ DATE_HUMAN, + DATE_RELATIVE, + DATE_SHORT, + DATE_ISO8601, + DATE_ISO8601_STRICT, + DATE_RFC2822, + DATE_STRFTIME, + DATE_RAW, + DATE_UNIX +}; + struct date_mode { - enum date_mode_type { - DATE_NORMAL = 0, - DATE_HUMAN, - DATE_RELATIVE, - DATE_SHORT, - DATE_ISO8601, - DATE_ISO8601_STRICT, - DATE_RFC2822, - DATE_STRFTIME, - DATE_RAW, - DATE_UNIX - } type; + enum date_mode_type type; const char *strftime_fmt; int local; }; @@@ -1488,8 -1451,10 +1489,10 @@@ struct date_mode *date_mode_from_type(enum date_mode_type type); const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode); -void show_date_relative(timestamp_t time, int tz, const struct timeval *now, +void show_date_relative(timestamp_t time, const struct timeval *now, struct strbuf *timebuf); + void show_date_human(timestamp_t time, int tz, const struct timeval *now, + struct strbuf *timebuf); int parse_date(const char *date, struct strbuf *out); int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset); int parse_expiry_date(const char *date, timestamp_t *timestamp); diff --cc date.c index 61449f8b2e,7b9e8fa2ba..9c5870e102 --- a/date.c +++ b/date.c @@@ -107,9 -101,36 +101,36 @@@ static int local_time_tzoffset(time_t t return offset * eastwest; } + /* + * What value of "tz" was in effect back then at "time" in the + * local timezone? + */ + static int local_tzoffset(timestamp_t time) + { + struct tm tm; + + if (date_overflows(time)) + die("Timestamp too large for this system: %"PRItime, time); + + return local_time_tzoffset((time_t)time, &tm); + } + + static void get_time(struct timeval *now) + { + const char *x; + + x = getenv("GIT_TEST_DATE_NOW"); + if (x) { + now->tv_sec = atoi(x); + now->tv_usec = 0; + } + else + gettimeofday(now, NULL); + } + -void show_date_relative(timestamp_t time, int tz, - const struct timeval *now, - struct strbuf *timebuf) +void show_date_relative(timestamp_t time, + const struct timeval *now, + struct strbuf *timebuf) { timestamp_t diff; if (now->tv_sec < time) { @@@ -191,6 -212,75 +212,75 @@@ struct date_mode *date_mode_from_type(e 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) + { + struct { + unsigned int year:1, + date:1, + wday:1, + time:1, + seconds:1, + tz:1; + } hide = { 0 }; + + hide.tz = local || tz == human_tz; + hide.year = tm->tm_year == human_tm->tm_year; + if (hide.year) { + if (tm->tm_mon == human_tm->tm_mon) { + if (tm->tm_mday > human_tm->tm_mday) { + /* Future date: think timezones */ + } else if (tm->tm_mday == human_tm->tm_mday) { + hide.date = hide.wday = 1; + } else if (tm->tm_mday + 5 > human_tm->tm_mday) { + /* Leave just weekday if it was a few days ago */ + hide.date = 1; + } + } + } + + /* Show "today" times as just relative times */ + if (hide.wday) { + struct timeval now; + get_time(&now); - show_date_relative(time, tz, &now, buf); ++ show_date_relative(time, &now, buf); + return; + } + + /* + * Always hide seconds for human-readable. + * Hide timezone if showing date. + * Hide weekday and time if showing year. + * + * The logic here is two-fold: + * (a) only show details when recent enough to matter + * (b) keep the maximum length "similar", and in check + */ + if (human_tm->tm_year) { + hide.seconds = 1; + hide.tz |= !hide.date; + hide.wday = hide.time = !hide.year; + } + + if (!hide.wday) + strbuf_addf(buf, "%.3s ", weekday_names[tm->tm_wday]); + if (!hide.date) + strbuf_addf(buf, "%.3s %d ", month_names[tm->tm_mon], tm->tm_mday); + + /* Do we want AM/PM depending on locale? */ + if (!hide.time) { + strbuf_addf(buf, "%02d:%02d", tm->tm_hour, tm->tm_min); + if (!hide.seconds) + strbuf_addf(buf, ":%02d", tm->tm_sec); + } else + strbuf_rtrim(buf); + + if (!hide.year) + strbuf_addf(buf, " %d", tm->tm_year + 1900); + + if (!hide.tz) + strbuf_addf(buf, " %+05d", tz); + } + const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) { struct tm *tm; @@@ -215,8 -316,8 +316,8 @@@ struct timeval now; strbuf_reset(&timebuf); - gettimeofday(&now, NULL); + get_time(&now); - show_date_relative(time, tz, &now, &timebuf); + show_date_relative(time, &now, &timebuf); return timebuf.buf; }