Skip to content

Commit 74e8221

Browse files
torvaldsgitster
authored andcommitted
Add 'human' date format
This adds --date=human, which skips the timezone if it matches 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 really recent dates (same day), use the relative date stamp, while for old dates (year doesn't match), don't bother with time and timezone. Also add 'auto' date mode, which defaults to human if we're using the pager. So you can do git config --add log.date auto and your "git log" commands will show the human-legible format unless you're scripting things. Note that this time format still shows the timezone for recent enough events (but not so recent that they show up as relative dates). You can combine it with the "-local" suffix to never show timezones for an even more simplified view. Signed-off-by: Linus Torvalds <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e333175 commit 74e8221

File tree

3 files changed

+115
-20
lines changed

3 files changed

+115
-20
lines changed

builtin/blame.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
917917
*/
918918
blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
919919
break;
920+
case DATE_HUMAN:
921+
/* If the year is shown, no time is shown */
922+
blame_date_width = sizeof("Thu Oct 19 16:00");
923+
break;
920924
case DATE_NORMAL:
921925
blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
922926
break;

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,7 @@ extern struct object *peel_to_type(const char *name, int namelen,
14281428
struct date_mode {
14291429
enum date_mode_type {
14301430
DATE_NORMAL = 0,
1431+
DATE_HUMAN,
14311432
DATE_RELATIVE,
14321433
DATE_SHORT,
14331434
DATE_ISO8601,

date.c

Lines changed: 110 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -77,22 +77,16 @@ static struct tm *time_to_tm_local(timestamp_t time)
7777
}
7878

7979
/*
80-
* What value of "tz" was in effect back then at "time" in the
81-
* local timezone?
80+
* Fill in the localtime 'struct tm' for the supplied time,
81+
* and return the local tz.
8282
*/
83-
static int local_tzoffset(timestamp_t time)
83+
static int local_time_tzoffset(time_t t, struct tm *tm)
8484
{
85-
time_t t, t_local;
86-
struct tm tm;
85+
time_t t_local;
8786
int offset, eastwest;
8887

89-
if (date_overflows(time))
90-
die("Timestamp too large for this system: %"PRItime, time);
91-
92-
t = (time_t)time;
93-
localtime_r(&t, &tm);
94-
t_local = tm_to_time_t(&tm);
95-
88+
localtime_r(&t, tm);
89+
t_local = tm_to_time_t(tm);
9690
if (t_local == -1)
9791
return 0; /* error; just use +0000 */
9892
if (t_local < t) {
@@ -107,6 +101,20 @@ static int local_tzoffset(timestamp_t time)
107101
return offset * eastwest;
108102
}
109103

104+
/*
105+
* What value of "tz" was in effect back then at "time" in the
106+
* local timezone?
107+
*/
108+
static int local_tzoffset(timestamp_t time)
109+
{
110+
struct tm tm;
111+
112+
if (date_overflows(time))
113+
die("Timestamp too large for this system: %"PRItime, time);
114+
115+
return local_time_tzoffset((time_t)time, &tm);
116+
}
117+
110118
void show_date_relative(timestamp_t time, int tz,
111119
const struct timeval *now,
112120
struct strbuf *timebuf)
@@ -191,9 +199,80 @@ struct date_mode *date_mode_from_type(enum date_mode_type type)
191199
return &mode;
192200
}
193201

202+
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)
203+
{
204+
struct {
205+
unsigned int year:1,
206+
date:1,
207+
wday:1,
208+
time:1,
209+
seconds:1,
210+
tz:1;
211+
} hide = { 0 };
212+
213+
hide.tz = local || tz == human_tz;
214+
hide.year = tm->tm_year == human_tm->tm_year;
215+
if (hide.year) {
216+
if (tm->tm_mon == human_tm->tm_mon) {
217+
if (tm->tm_mday > human_tm->tm_mday) {
218+
/* Future date: think timezones */
219+
} else if (tm->tm_mday == human_tm->tm_mday) {
220+
hide.date = hide.wday = 1;
221+
} else if (tm->tm_mday + 5 > human_tm->tm_mday) {
222+
/* Leave just weekday if it was a few days ago */
223+
hide.date = 1;
224+
}
225+
}
226+
}
227+
228+
/* Show "today" times as just relative times */
229+
if (hide.wday) {
230+
struct timeval now;
231+
gettimeofday(&now, NULL);
232+
show_date_relative(time, tz, &now, buf);
233+
return;
234+
}
235+
236+
/*
237+
* Always hide seconds for human-readable.
238+
* Hide timezone if showing date.
239+
* Hide weekday and time if showing year.
240+
*
241+
* The logic here is two-fold:
242+
* (a) only show details when recent enough to matter
243+
* (b) keep the maximum length "similar", and in check
244+
*/
245+
if (human_tm->tm_year) {
246+
hide.seconds = 1;
247+
hide.tz |= !hide.date;
248+
hide.wday = hide.time = !hide.year;
249+
}
250+
251+
if (!hide.wday)
252+
strbuf_addf(buf, "%.3s ", weekday_names[tm->tm_wday]);
253+
if (!hide.date)
254+
strbuf_addf(buf, "%.3s %d ", month_names[tm->tm_mon], tm->tm_mday);
255+
256+
/* Do we want AM/PM depending on locale? */
257+
if (!hide.time) {
258+
strbuf_addf(buf, "%02d:%02d", tm->tm_hour, tm->tm_min);
259+
if (!hide.seconds)
260+
strbuf_addf(buf, ":%02d", tm->tm_sec);
261+
} else
262+
strbuf_rtrim(buf);
263+
264+
if (!hide.year)
265+
strbuf_addf(buf, " %d", tm->tm_year + 1900);
266+
267+
if (!hide.tz)
268+
strbuf_addf(buf, " %+05d", tz);
269+
}
270+
194271
const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
195272
{
196273
struct tm *tm;
274+
struct tm human_tm = { 0 };
275+
int human_tz = -1;
197276
static struct strbuf timebuf = STRBUF_INIT;
198277

199278
if (mode->type == DATE_UNIX) {
@@ -202,6 +281,15 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
202281
return timebuf.buf;
203282
}
204283

284+
if (mode->type == DATE_HUMAN) {
285+
struct timeval now;
286+
287+
gettimeofday(&now, NULL);
288+
289+
/* Fill in the data for "current time" in human_tz and human_tm */
290+
human_tz = local_time_tzoffset(now.tv_sec, &human_tm);
291+
}
292+
205293
if (mode->local)
206294
tz = local_tzoffset(time);
207295

@@ -258,14 +346,7 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
258346
strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz,
259347
!mode->local);
260348
else
261-
strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
262-
weekday_names[tm->tm_wday],
263-
month_names[tm->tm_mon],
264-
tm->tm_mday,
265-
tm->tm_hour, tm->tm_min, tm->tm_sec,
266-
tm->tm_year + 1900,
267-
mode->local ? 0 : ' ',
268-
tz);
349+
show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode->local);
269350
return timebuf.buf;
270351
}
271352

@@ -802,6 +883,11 @@ int parse_date(const char *date, struct strbuf *result)
802883
return 0;
803884
}
804885

886+
static int auto_date_style(void)
887+
{
888+
return (isatty(1) || pager_in_use()) ? DATE_HUMAN : DATE_NORMAL;
889+
}
890+
805891
static enum date_mode_type parse_date_type(const char *format, const char **end)
806892
{
807893
if (skip_prefix(format, "relative", end))
@@ -819,6 +905,10 @@ static enum date_mode_type parse_date_type(const char *format, const char **end)
819905
return DATE_SHORT;
820906
if (skip_prefix(format, "default", end))
821907
return DATE_NORMAL;
908+
if (skip_prefix(format, "human", end))
909+
return DATE_HUMAN;
910+
if (skip_prefix(format, "auto", end))
911+
return auto_date_style();
822912
if (skip_prefix(format, "raw", end))
823913
return DATE_RAW;
824914
if (skip_prefix(format, "unix", end))

0 commit comments

Comments
 (0)