Skip to content

Commit 6eced3e

Browse files
peffgitster
authored andcommitted
date: use localtime() for "-local" time formats
When we convert seconds-since-epochs timestamps into a broken-down "struct tm", we do so by adjusting the timestamp according to the known offset and then using gmtime() to break down the result. This means that the resulting struct "knows" that it's in GMT, even though the time it represents is adjusted for a different zone. The fields where it stores this data are not portably accessible, so we have no way to override them to tell them the real zone info. For the most part, this works. Our date-formatting routines don't pay attention to these inaccessible fields, and use the same tz info we provided for adjustment. The one exception is when we call strftime(), whose %Z format reveals this hidden timezone data. We solved that by always showing the empty string for %Z. This is allowed by POSIX, but not very helpful to the user. We can't make this work in the general case, as there's no portable function for setting an arbitrary timezone (and anyway, we don't have the zone name for the author zones, only their offsets). But for the special case of the "-local" formats, we can just skip the adjustment and use localtime() instead of gmtime(). This makes --date=format-local:%Z work correctly, showing the local timezone instead of an empty string. The new test checks the result for "UTC", our default test-lib value for $TZ. Using something like EST5 might be more interesting, but the actual zone string is system-dependent (for instance, on my system it expands to just EST). Hopefully "UTC" is vanilla enough that every system treats it the same. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 22280d7 commit 6eced3e

File tree

2 files changed

+13
-2
lines changed

2 files changed

+13
-2
lines changed

date.c

+12-2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ static struct tm *time_to_tm(unsigned long time, int tz)
6060
return gmtime(&t);
6161
}
6262

63+
static struct tm *time_to_tm_local(unsigned long time)
64+
{
65+
time_t t = time;
66+
return localtime(&t);
67+
}
68+
6369
/*
6470
* What value of "tz" was in effect back then at "time" in the
6571
* local timezone?
@@ -201,7 +207,10 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
201207
return timebuf.buf;
202208
}
203209

204-
tm = time_to_tm(time, tz);
210+
if (mode->local)
211+
tm = time_to_tm_local(time);
212+
else
213+
tm = time_to_tm(time, tz);
205214
if (!tm) {
206215
tm = time_to_tm(0, 0);
207216
tz = 0;
@@ -233,7 +242,8 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
233242
month_names[tm->tm_mon], tm->tm_year + 1900,
234243
tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
235244
else if (mode->type == DATE_STRFTIME)
236-
strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, "");
245+
strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz,
246+
mode->local ? NULL : "");
237247
else
238248
strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
239249
weekday_names[tm->tm_wday],

t/t0006-date.sh

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ check_show unix-local "$TIME" '1466000000'
5656
check_show 'format:%z' "$TIME" '+0200'
5757
check_show 'format-local:%z' "$TIME" '+0000'
5858
check_show 'format:%Z' "$TIME" ''
59+
check_show 'format-local:%Z' "$TIME" 'UTC'
5960
check_show 'format:%%z' "$TIME" '%z'
6061
check_show 'format-local:%%z' "$TIME" '%z'
6162

0 commit comments

Comments
 (0)