@@ -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,33 @@ 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+
118+ static void get_time (struct timeval * now )
119+ {
120+ const char * x ;
121+
122+ x = getenv ("GIT_TEST_DATE_NOW" );
123+ if (x ) {
124+ now -> tv_sec = atoi (x );
125+ now -> tv_usec = 0 ;
126+ }
127+ else
128+ gettimeofday (now , NULL );
129+ }
130+
110131void show_date_relative (timestamp_t time ,
111132 const struct timeval * now ,
112133 struct strbuf * timebuf )
@@ -191,9 +212,80 @@ struct date_mode *date_mode_from_type(enum date_mode_type type)
191212 return & mode ;
192213}
193214
215+ 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 )
216+ {
217+ struct {
218+ unsigned int year :1 ,
219+ date :1 ,
220+ wday :1 ,
221+ time :1 ,
222+ seconds :1 ,
223+ tz :1 ;
224+ } hide = { 0 };
225+
226+ hide .tz = local || tz == human_tz ;
227+ hide .year = tm -> tm_year == human_tm -> tm_year ;
228+ if (hide .year ) {
229+ if (tm -> tm_mon == human_tm -> tm_mon ) {
230+ if (tm -> tm_mday > human_tm -> tm_mday ) {
231+ /* Future date: think timezones */
232+ } else if (tm -> tm_mday == human_tm -> tm_mday ) {
233+ hide .date = hide .wday = 1 ;
234+ } else if (tm -> tm_mday + 5 > human_tm -> tm_mday ) {
235+ /* Leave just weekday if it was a few days ago */
236+ hide .date = 1 ;
237+ }
238+ }
239+ }
240+
241+ /* Show "today" times as just relative times */
242+ if (hide .wday ) {
243+ struct timeval now ;
244+ get_time (& now );
245+ show_date_relative (time , & now , buf );
246+ return ;
247+ }
248+
249+ /*
250+ * Always hide seconds for human-readable.
251+ * Hide timezone if showing date.
252+ * Hide weekday and time if showing year.
253+ *
254+ * The logic here is two-fold:
255+ * (a) only show details when recent enough to matter
256+ * (b) keep the maximum length "similar", and in check
257+ */
258+ if (human_tm -> tm_year ) {
259+ hide .seconds = 1 ;
260+ hide .tz |= !hide .date ;
261+ hide .wday = hide .time = !hide .year ;
262+ }
263+
264+ if (!hide .wday )
265+ strbuf_addf (buf , "%.3s " , weekday_names [tm -> tm_wday ]);
266+ if (!hide .date )
267+ strbuf_addf (buf , "%.3s %d " , month_names [tm -> tm_mon ], tm -> tm_mday );
268+
269+ /* Do we want AM/PM depending on locale? */
270+ if (!hide .time ) {
271+ strbuf_addf (buf , "%02d:%02d" , tm -> tm_hour , tm -> tm_min );
272+ if (!hide .seconds )
273+ strbuf_addf (buf , ":%02d" , tm -> tm_sec );
274+ } else
275+ strbuf_rtrim (buf );
276+
277+ if (!hide .year )
278+ strbuf_addf (buf , " %d" , tm -> tm_year + 1900 );
279+
280+ if (!hide .tz )
281+ strbuf_addf (buf , " %+05d" , tz );
282+ }
283+
194284const char * show_date (timestamp_t time , int tz , const struct date_mode * mode )
195285{
196286 struct tm * tm ;
287+ struct tm human_tm = { 0 };
288+ int human_tz = -1 ;
197289 static struct strbuf timebuf = STRBUF_INIT ;
198290
199291 if (mode -> type == DATE_UNIX ) {
@@ -202,6 +294,15 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
202294 return timebuf .buf ;
203295 }
204296
297+ if (mode -> type == DATE_HUMAN ) {
298+ struct timeval now ;
299+
300+ get_time (& now );
301+
302+ /* Fill in the data for "current time" in human_tz and human_tm */
303+ human_tz = local_time_tzoffset (now .tv_sec , & human_tm );
304+ }
305+
205306 if (mode -> local )
206307 tz = local_tzoffset (time );
207308
@@ -215,7 +316,7 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
215316 struct timeval now ;
216317
217318 strbuf_reset (& timebuf );
218- gettimeofday (& now , NULL );
319+ get_time (& now );
219320 show_date_relative (time , & now , & timebuf );
220321 return timebuf .buf ;
221322 }
@@ -258,14 +359,7 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
258359 strbuf_addftime (& timebuf , mode -> strftime_fmt , tm , tz ,
259360 !mode -> local );
260361 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 );
362+ show_date_normal (& timebuf , time , tm , tz , & human_tm , human_tz , mode -> local );
269363 return timebuf .buf ;
270364}
271365
@@ -819,6 +913,8 @@ static enum date_mode_type parse_date_type(const char *format, const char **end)
819913 return DATE_SHORT ;
820914 if (skip_prefix (format , "default" , end ))
821915 return DATE_NORMAL ;
916+ if (skip_prefix (format , "human" , end ))
917+ return DATE_HUMAN ;
822918 if (skip_prefix (format , "raw" , end ))
823919 return DATE_RAW ;
824920 if (skip_prefix (format , "unix" , end ))
@@ -833,6 +929,14 @@ void parse_date_format(const char *format, struct date_mode *mode)
833929{
834930 const char * p ;
835931
932+ /* "auto:foo" is "if tty/pager, then foo, otherwise normal" */
933+ if (skip_prefix (format , "auto:" , & p )) {
934+ if (isatty (1 ) || pager_in_use ())
935+ format = p ;
936+ else
937+ format = "default" ;
938+ }
939+
836940 /* historical alias */
837941 if (!strcmp (format , "local" ))
838942 format = "default-local" ;
@@ -1205,7 +1309,7 @@ timestamp_t approxidate_careful(const char *date, int *error_ret)
12051309 return timestamp ;
12061310 }
12071311
1208- gettimeofday (& tv , NULL );
1312+ get_time (& tv );
12091313 return approxidate_str (date , & tv , error_ret );
12101314}
12111315
0 commit comments