diff options
Diffstat (limited to 'date.c')
-rw-r--r-- | date.c | 251 |
1 files changed, 183 insertions, 68 deletions
@@ -64,35 +64,29 @@ 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); } /* - * What value of "tz" was in effect back then at "time" in the - * local timezone? + * Fill in the localtime 'struct tm' for the supplied time, + * and return the local tz. */ -static int local_tzoffset(timestamp_t time) +static int local_time_tzoffset(time_t t, struct tm *tm) { - time_t t, t_local; - struct tm tm; + time_t t_local; int offset, eastwest; - if (date_overflows(time)) - die("Timestamp too large for this system: %"PRItime, time); - - t = (time_t)time; - localtime_r(&t, &tm); - t_local = tm_to_time_t(&tm); - + localtime_r(&t, tm); + t_local = tm_to_time_t(tm); if (t_local == -1) return 0; /* error; just use +0000 */ if (t_local < t) { @@ -107,16 +101,44 @@ static int local_tzoffset(timestamp_t time) return offset * eastwest; } -void show_date_relative(timestamp_t time, int tz, - const struct timeval *now, - struct strbuf *timebuf) +/* + * 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, 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); @@ -191,9 +213,79 @@ struct date_mode *date_mode_from_type(enum date_mode_type type) 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) { + show_date_relative(time, 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; + struct tm tmbuf = { 0 }; + struct tm human_tm = { 0 }; + int human_tz = -1; static struct strbuf timebuf = STRBUF_INIT; if (mode->type == DATE_UNIX) { @@ -202,6 +294,15 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) return timebuf.buf; } + if (mode->type == DATE_HUMAN) { + struct timeval now; + + get_time(&now); + + /* Fill in the data for "current time" in human_tz and human_tm */ + human_tz = local_time_tzoffset(now.tv_sec, &human_tm); + } + if (mode->local) tz = local_tzoffset(time); @@ -212,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); - gettimeofday(&now, NULL); - show_date_relative(time, tz, &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; } @@ -258,14 +356,7 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, !mode->local); else - strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d", - weekday_names[tm->tm_wday], - month_names[tm->tm_mon], - tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_year + 1900, - mode->local ? 0 : ' ', - tz); + show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode->local); return timebuf.buf; } @@ -819,12 +910,18 @@ static enum date_mode_type parse_date_type(const char *format, const char **end) return DATE_SHORT; if (skip_prefix(format, "default", end)) return DATE_NORMAL; + if (skip_prefix(format, "human", end)) + return DATE_HUMAN; if (skip_prefix(format, "raw", end)) return DATE_RAW; if (skip_prefix(format, "unix", 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); } @@ -833,6 +930,14 @@ void parse_date_format(const char *format, struct date_mode *mode) { const char *p; + /* "auto:foo" is "if tty/pager, then foo, otherwise normal" */ + if (skip_prefix(format, "auto:", &p)) { + if (isatty(1) || pager_in_use()) + format = p; + else + format = "default"; + } + /* historical alias */ if (!strcmp(format, "local")) format = "default-local"; @@ -855,10 +960,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); @@ -887,20 +993,49 @@ static time_t update_tm(struct tm *tm, struct tm *now, time_t sec) return n; } +/* + * Do we have a pending number at the end, or when + * we see a new one? Let's assume it's a month day, + * as in "Dec 6, 1992" + */ +static void pending_number(struct tm *tm, int *num) +{ + int number = *num; + + if (number) { + *num = 0; + if (tm->tm_mday < 0 && number < 32) + tm->tm_mday = number; + else if (tm->tm_mon < 0 && number < 13) + tm->tm_mon = number-1; + else if (tm->tm_year < 0) { + if (number > 1969 && number < 2100) + tm->tm_year = number - 1900; + else if (number > 69 && number < 100) + tm->tm_year = number; + else if (number < 38) + tm->tm_year = 100 + number; + /* We screw up for number = 00 ? */ + } + } +} + static void date_now(struct tm *tm, struct tm *now, int *num) { + *num = 0; update_tm(tm, now, 0); } static void date_yesterday(struct tm *tm, struct tm *now, int *num) { + *num = 0; update_tm(tm, now, 24*60*60); } static void date_time(struct tm *tm, struct tm *now, int hour) { if (tm->tm_hour < hour) - date_yesterday(tm, now, NULL); + update_tm(tm, now, 24*60*60); tm->tm_hour = hour; tm->tm_min = 0; tm->tm_sec = 0; @@ -908,16 +1043,19 @@ static void date_time(struct tm *tm, struct tm *now, int hour) static void date_midnight(struct tm *tm, struct tm *now, int *num) { + pending_number(tm, num); date_time(tm, now, 0); } static void date_noon(struct tm *tm, struct tm *now, int *num) { + pending_number(tm, num); date_time(tm, now, 12); } static void date_tea(struct tm *tm, struct tm *now, int *num) { + pending_number(tm, num); date_time(tm, now, 17); } @@ -953,6 +1091,7 @@ static void date_never(struct tm *tm, struct tm *now, int *num) { time_t n = 0; localtime_r(&n, tm); + *num = 0; } static const struct special { @@ -1110,33 +1249,6 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num, return end; } -/* - * Do we have a pending number at the end, or when - * we see a new one? Let's assume it's a month day, - * as in "Dec 6, 1992" - */ -static void pending_number(struct tm *tm, int *num) -{ - int number = *num; - - if (number) { - *num = 0; - if (tm->tm_mday < 0 && number < 32) - tm->tm_mday = number; - else if (tm->tm_mon < 0 && number < 13) - tm->tm_mon = number-1; - else if (tm->tm_year < 0) { - if (number > 1969 && number < 2100) - tm->tm_year = number - 1900; - else if (number > 69 && number < 100) - tm->tm_year = number; - else if (number < 38) - tm->tm_year = 100 + number; - /* We screw up for number = 00 ? */ - } - } -} - static timestamp_t approxidate_str(const char *date, const struct timeval *tv, int *error_ret) @@ -1174,15 +1286,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, ×tamp, &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) @@ -1199,7 +1314,7 @@ timestamp_t approxidate_careful(const char *date, int *error_ret) return timestamp; } - gettimeofday(&tv, NULL); + get_time(&tv); return approxidate_str(date, &tv, error_ret); } |