Skip to content

Commit 9eafe86

Browse files
committed
Merge branch 'rs/strbuf-addftime-zZ'
As there is no portable way to pass timezone information to strftime, some output format from "git log" and friends are impossible to produce. Teach our own strbuf_addftime to replace %z and %Z with caller-supplied values to help working around this. * rs/strbuf-addftime-zZ: date: use localtime() for "-local" time formats t0006: check --date=format zone offsets strbuf: let strbuf_addftime handle %z and %Z itself
2 parents 1565b18 + 6eced3e commit 9eafe86

File tree

5 files changed

+73
-11
lines changed

5 files changed

+73
-11
lines changed

Documentation/rev-list-options.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,8 @@ timezone value.
769769
1970). As with `--raw`, this is always in UTC and therefore `-local`
770770
has no effect.
771771
+
772-
`--date=format:...` feeds the format `...` to your system `strftime`.
772+
`--date=format:...` feeds the format `...` to your system `strftime`,
773+
except for %z and %Z, which are handled internally.
773774
Use `--date=format:%c` to show the date in your system locale's
774775
preferred format. See the `strftime` manual for a complete list of
775776
format placeholders. When using `-local`, the correct syntax is

date.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ static struct tm *time_to_tm(timestamp_t time, int tz)
7070
return gmtime(&t);
7171
}
7272

73+
static struct tm *time_to_tm_local(timestamp_t time)
74+
{
75+
time_t t = time;
76+
return localtime(&t);
77+
}
78+
7379
/*
7480
* What value of "tz" was in effect back then at "time" in the
7581
* local timezone?
@@ -214,7 +220,10 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
214220
return timebuf.buf;
215221
}
216222

217-
tm = time_to_tm(time, tz);
223+
if (mode->local)
224+
tm = time_to_tm_local(time);
225+
else
226+
tm = time_to_tm(time, tz);
218227
if (!tm) {
219228
tm = time_to_tm(0, 0);
220229
tz = 0;
@@ -246,7 +255,8 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
246255
month_names[tm->tm_mon], tm->tm_year + 1900,
247256
tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
248257
else if (mode->type == DATE_STRFTIME)
249-
strbuf_addftime(&timebuf, mode->strftime_fmt, tm);
258+
strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz,
259+
mode->local ? NULL : "");
250260
else
251261
strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
252262
weekday_names[tm->tm_wday],

strbuf.c

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -785,14 +785,48 @@ char *xstrfmt(const char *fmt, ...)
785785
return ret;
786786
}
787787

788-
void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm)
788+
void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm,
789+
int tz_offset, const char *tz_name)
789790
{
791+
struct strbuf munged_fmt = STRBUF_INIT;
790792
size_t hint = 128;
791793
size_t len;
792794

793795
if (!*fmt)
794796
return;
795797

798+
/*
799+
* There is no portable way to pass timezone information to
800+
* strftime, so we handle %z and %Z here.
801+
*/
802+
for (;;) {
803+
const char *percent = strchrnul(fmt, '%');
804+
strbuf_add(&munged_fmt, fmt, percent - fmt);
805+
if (!*percent)
806+
break;
807+
fmt = percent + 1;
808+
switch (*fmt) {
809+
case '%':
810+
strbuf_addstr(&munged_fmt, "%%");
811+
fmt++;
812+
break;
813+
case 'z':
814+
strbuf_addf(&munged_fmt, "%+05d", tz_offset);
815+
fmt++;
816+
break;
817+
case 'Z':
818+
if (tz_name) {
819+
strbuf_addstr(&munged_fmt, tz_name);
820+
fmt++;
821+
break;
822+
}
823+
/* FALLTHROUGH */
824+
default:
825+
strbuf_addch(&munged_fmt, '%');
826+
}
827+
}
828+
fmt = munged_fmt.buf;
829+
796830
strbuf_grow(sb, hint);
797831
len = strftime(sb->buf + sb->len, sb->alloc - sb->len, fmt, tm);
798832

@@ -804,17 +838,16 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm)
804838
* output contains at least one character, and then drop the extra
805839
* character before returning.
806840
*/
807-
struct strbuf munged_fmt = STRBUF_INIT;
808-
strbuf_addf(&munged_fmt, "%s ", fmt);
841+
strbuf_addch(&munged_fmt, ' ');
809842
while (!len) {
810843
hint *= 2;
811844
strbuf_grow(sb, hint);
812845
len = strftime(sb->buf + sb->len, sb->alloc - sb->len,
813846
munged_fmt.buf, tm);
814847
}
815-
strbuf_release(&munged_fmt);
816848
len--; /* drop munged space */
817849
}
850+
strbuf_release(&munged_fmt);
818851
strbuf_setlen(sb, sb->len + len);
819852
}
820853

strbuf.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,14 @@ extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
340340

341341
/**
342342
* Add the time specified by `tm`, as formatted by `strftime`.
343-
*/
344-
extern void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm);
343+
* `tz_name` is used to expand %Z internally unless it's NULL.
344+
* `tz_offset` is in decimal hhmm format, e.g. -600 means six hours west
345+
* of Greenwich, and it's used to expand %z internally. However, tokens
346+
* with modifiers (e.g. %Ez) are passed to `strftime`.
347+
*/
348+
extern void strbuf_addftime(struct strbuf *sb, const char *fmt,
349+
const struct tm *tm, int tz_offset,
350+
const char *tz_name);
345351

346352
/**
347353
* Read a given size of data from a FILE* pointer to the buffer.

t/t0006-date.sh

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ check_show () {
3131
format=$1
3232
time=$2
3333
expect=$3
34-
test_expect_success $4 "show date ($format:$time)" '
34+
prereqs=$4
35+
zone=$5
36+
test_expect_success $prereqs "show date ($format:$time)" '
3537
echo "$time -> $expect" >expect &&
36-
test-date show:$format "$time" >actual &&
38+
TZ=${zone:-$TZ} test-date show:"$format" "$time" >actual &&
3739
test_cmp expect actual
3840
'
3941
}
@@ -51,6 +53,16 @@ check_show iso-local "$TIME" '2016-06-15 14:13:20 +0000'
5153
check_show raw-local "$TIME" '1466000000 +0000'
5254
check_show unix-local "$TIME" '1466000000'
5355

56+
check_show 'format:%z' "$TIME" '+0200'
57+
check_show 'format-local:%z' "$TIME" '+0000'
58+
check_show 'format:%Z' "$TIME" ''
59+
check_show 'format-local:%Z' "$TIME" 'UTC'
60+
check_show 'format:%%z' "$TIME" '%z'
61+
check_show 'format-local:%%z' "$TIME" '%z'
62+
63+
check_show 'format:%Y-%m-%d %H:%M:%S' "$TIME" '2016-06-15 16:13:20'
64+
check_show 'format-local:%Y-%m-%d %H:%M:%S' "$TIME" '2016-06-15 09:13:20' '' EST5
65+
5466
# arbitrary time absurdly far in the future
5567
FUTURE="5758122296 -0400"
5668
check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT

0 commit comments

Comments
 (0)