@@ -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+
110118void 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+
194271const 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+
805891static 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