@@ -77,22 +77,16 @@ static struct tm *time_to_tm_local(timestamp_t time)
77
77
}
78
78
79
79
/*
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.
82
82
*/
83
- static int local_tzoffset ( timestamp_t time )
83
+ static int local_time_tzoffset ( time_t t , struct tm * tm )
84
84
{
85
- time_t t , t_local ;
86
- struct tm tm ;
85
+ time_t t_local ;
87
86
int offset , eastwest ;
88
87
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 );
96
90
if (t_local == -1 )
97
91
return 0 ; /* error; just use +0000 */
98
92
if (t_local < t ) {
@@ -107,6 +101,20 @@ static int local_tzoffset(timestamp_t time)
107
101
return offset * eastwest ;
108
102
}
109
103
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
+
110
118
void show_date_relative (timestamp_t time , int tz ,
111
119
const struct timeval * now ,
112
120
struct strbuf * timebuf )
@@ -191,9 +199,80 @@ struct date_mode *date_mode_from_type(enum date_mode_type type)
191
199
return & mode ;
192
200
}
193
201
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
+
194
271
const char * show_date (timestamp_t time , int tz , const struct date_mode * mode )
195
272
{
196
273
struct tm * tm ;
274
+ struct tm human_tm = { 0 };
275
+ int human_tz = -1 ;
197
276
static struct strbuf timebuf = STRBUF_INIT ;
198
277
199
278
if (mode -> type == DATE_UNIX ) {
@@ -202,6 +281,15 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
202
281
return timebuf .buf ;
203
282
}
204
283
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
+
205
293
if (mode -> local )
206
294
tz = local_tzoffset (time );
207
295
@@ -258,14 +346,7 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
258
346
strbuf_addftime (& timebuf , mode -> strftime_fmt , tm , tz ,
259
347
!mode -> local );
260
348
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 );
269
350
return timebuf .buf ;
270
351
}
271
352
@@ -802,6 +883,11 @@ int parse_date(const char *date, struct strbuf *result)
802
883
return 0 ;
803
884
}
804
885
886
+ static int auto_date_style (void )
887
+ {
888
+ return (isatty (1 ) || pager_in_use ()) ? DATE_HUMAN : DATE_NORMAL ;
889
+ }
890
+
805
891
static enum date_mode_type parse_date_type (const char * format , const char * * end )
806
892
{
807
893
if (skip_prefix (format , "relative" , end ))
@@ -819,6 +905,10 @@ static enum date_mode_type parse_date_type(const char *format, const char **end)
819
905
return DATE_SHORT ;
820
906
if (skip_prefix (format , "default" , end ))
821
907
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 ();
822
912
if (skip_prefix (format , "raw" , end ))
823
913
return DATE_RAW ;
824
914
if (skip_prefix (format , "unix" , end ))
0 commit comments