1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-18 19:26:07 +02:00

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
This commit is contained in:
Junio C Hamano 2019-02-06 22:05:24 -08:00
commit ecbe1beb8e
7 changed files with 176 additions and 25 deletions

View File

@ -192,6 +192,10 @@ log.date::
Default format for human-readable dates. (Compare the
`--date` option.) Defaults to "default", which means to write
dates like `Sat May 8 19:35:34 2010 -0500`.
+
If the format is set to "auto:foo" and the pager is in use, format
"foo" will be the used for the date format. Otherwise "default" will
be used.
log.follow::
If `true`, `git log` will act as if the `--follow` option was used when

View File

@ -836,6 +836,13 @@ Note that the `-local` option does not affect the seconds-since-epoch
value (which is always measured in UTC), but does switch the accompanying
timezone value.
+
`--date=human` shows the timezone if the timezone does not match the
current time-zone, and doesn't print the whole date if that matches
(ie skip printing year for dates that are "this year", but also skip
the whole date itself if it's in the last few days and we can just say
what weekday it was). For older dates the hour and minute is also
omitted.
+
`--date=unix` shows the date as a Unix epoch timestamp (seconds since
1970). As with `--raw`, this is always in UTC and therefore `-local`
has no effect.

View File

@ -925,6 +925,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
*/
blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
break;
case DATE_HUMAN:
/* If the year is shown, no time is shown */
blame_date_width = sizeof("Thu Oct 19 16:00");
break;
case DATE_NORMAL:
blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
break;

View File

@ -1463,6 +1463,7 @@ extern struct object *peel_to_type(const char *name, int namelen,
enum date_mode_type {
DATE_NORMAL = 0,
DATE_HUMAN,
DATE_RELATIVE,
DATE_SHORT,
DATE_ISO8601,
@ -1490,6 +1491,8 @@ 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, 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);

148
date.c
View File

@ -77,22 +77,16 @@ static struct tm *time_to_tm_local(timestamp_t time)
}
/*
* 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,6 +101,33 @@ static int local_tzoffset(timestamp_t time)
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,
const struct timeval *now,
struct strbuf *timebuf)
@ -191,9 +212,80 @@ 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) {
struct timeval now;
get_time(&now);
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;
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);
@ -215,7 +316,7 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
struct timeval now;
strbuf_reset(&timebuf);
gettimeofday(&now, NULL);
get_time(&now);
show_date_relative(time, &now, &timebuf);
return timebuf.buf;
}
@ -258,14 +359,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,6 +913,8 @@ 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))
@ -833,6 +929,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";
@ -1205,7 +1309,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);
}

View File

@ -3,6 +3,7 @@
static const char *usage_msg = "\n"
" test-tool date relative [time_t]...\n"
" test-tool date human [time_t]...\n"
" test-tool date show:<format> [time_t]...\n"
" test-tool date parse [date]...\n"
" test-tool date approxidate [date]...\n"
@ -22,6 +23,14 @@ static void show_relative_dates(const char **argv, struct timeval *now)
strbuf_release(&buf);
}
static void show_human_dates(const char **argv)
{
for (; *argv; argv++) {
time_t t = atoi(*argv);
printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(HUMAN)));
}
}
static void show_dates(const char **argv, const char *format)
{
struct date_mode mode;
@ -87,7 +96,7 @@ int cmd__date(int argc, const char **argv)
struct timeval now;
const char *x;
x = getenv("TEST_DATE_NOW");
x = getenv("GIT_TEST_DATE_NOW");
if (x) {
now.tv_sec = atoi(x);
now.tv_usec = 0;
@ -100,6 +109,8 @@ int cmd__date(int argc, const char **argv)
usage(usage_msg);
if (!strcmp(*argv, "relative"))
show_relative_dates(argv+1, &now);
else if (!strcmp(*argv, "human"))
show_human_dates(argv+1);
else if (skip_prefix(*argv, "show:", &x))
show_dates(argv+1, x);
else if (!strcmp(*argv, "parse"))

View File

@ -4,10 +4,10 @@ test_description='test date parsing and printing'
. ./test-lib.sh
# arbitrary reference time: 2009-08-30 19:20:00
TEST_DATE_NOW=1251660000; export TEST_DATE_NOW
GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
check_relative() {
t=$(($TEST_DATE_NOW - $1))
t=$(($GIT_TEST_DATE_NOW - $1))
echo "$t -> $2" >expect
test_expect_${3:-success} "relative date ($2)" "
test-tool date relative $t >actual &&
@ -128,4 +128,22 @@ check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00'
check_approxidate '2008-12-01' '2008-12-01 19:20:00'
check_approxidate '2009-12-01' '2009-12-01 19:20:00'
check_date_format_human() {
t=$(($GIT_TEST_DATE_NOW - $1))
echo "$t -> $2" >expect
test_expect_success "human date $t" '
test-tool date human $t >actual &&
test_i18ncmp expect actual
'
}
check_date_format_human 18000 "5 hours ago" # 5 hours ago
check_date_format_human 432000 "Tue Aug 25 19:20" # 5 days ago
check_date_format_human 1728000 "Mon Aug 10 19:20" # 3 weeks ago
check_date_format_human 13000000 "Thu Apr 2 08:13" # 5 months ago
check_date_format_human 31449600 "Aug 31 2008" # 12 months ago
check_date_format_human 37500000 "Jun 22 2008" # 1 year, 2 months ago
check_date_format_human 55188000 "Dec 1 2007" # 1 year, 9 months ago
check_date_format_human 630000000 "Sep 13 1989" # 20 years ago
test_done