strbuf: let strbuf_addftime handle %z and %Z itself

There is no portable way to pass timezone information to strftime.  Add
parameters for timezone offset and name to strbuf_addftime and let it
handle the timezone-related format specifiers %z and %Z internally.

Callers can opt out for %Z by passing NULL as timezone name.  %z is
always handled internally -- this helps on Windows, where strftime would
expand it to a timezone name (same as %Z), in violation of POSIX.
Modifiers are not handled, e.g. %Ez is still passed to strftime.

Use an empty string as timezone name in show_date (the only current
caller) for now because we only have the timezone offset in non-local
mode.  POSIX allows %Z to resolve to an empty string in case of missing
information.

Helped-by: Ulrich Mueller <ulm@gentoo.org>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Rene Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
René Scharfe 2017-06-15 14:29:53 +02:00 коммит произвёл Junio C Hamano
Родитель b06d364310
Коммит c3fbf81a85
5 изменённых файлов: 53 добавлений и 7 удалений

Просмотреть файл

@ -764,7 +764,8 @@ timezone value.
1970). As with `--raw`, this is always in UTC and therefore `-local`
has no effect.
+
`--date=format:...` feeds the format `...` to your system `strftime`.
`--date=format:...` feeds the format `...` to your system `strftime`,
except for %z and %Z, which are handled internally.
Use `--date=format:%c` to show the date in your system locale's
preferred format. See the `strftime` manual for a complete list of
format placeholders. When using `-local`, the correct syntax is

2
date.c
Просмотреть файл

@ -233,7 +233,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
month_names[tm->tm_mon], tm->tm_year + 1900,
tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
else if (mode->type == DATE_STRFTIME)
strbuf_addftime(&timebuf, mode->strftime_fmt, tm);
strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, "");
else
strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
weekday_names[tm->tm_wday],

Просмотреть файл

@ -785,14 +785,48 @@ char *xstrfmt(const char *fmt, ...)
return ret;
}
void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm)
void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm,
int tz_offset, const char *tz_name)
{
struct strbuf munged_fmt = STRBUF_INIT;
size_t hint = 128;
size_t len;
if (!*fmt)
return;
/*
* There is no portable way to pass timezone information to
* strftime, so we handle %z and %Z here.
*/
for (;;) {
const char *percent = strchrnul(fmt, '%');
strbuf_add(&munged_fmt, fmt, percent - fmt);
if (!*percent)
break;
fmt = percent + 1;
switch (*fmt) {
case '%':
strbuf_addstr(&munged_fmt, "%%");
fmt++;
break;
case 'z':
strbuf_addf(&munged_fmt, "%+05d", tz_offset);
fmt++;
break;
case 'Z':
if (tz_name) {
strbuf_addstr(&munged_fmt, tz_name);
fmt++;
break;
}
/* FALLTHROUGH */
default:
strbuf_addch(&munged_fmt, '%');
}
}
fmt = munged_fmt.buf;
strbuf_grow(sb, hint);
len = strftime(sb->buf + sb->len, sb->alloc - sb->len, fmt, tm);
@ -804,17 +838,16 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm)
* output contains at least one character, and then drop the extra
* character before returning.
*/
struct strbuf munged_fmt = STRBUF_INIT;
strbuf_addf(&munged_fmt, "%s ", fmt);
strbuf_addch(&munged_fmt, ' ');
while (!len) {
hint *= 2;
strbuf_grow(sb, hint);
len = strftime(sb->buf + sb->len, sb->alloc - sb->len,
munged_fmt.buf, tm);
}
strbuf_release(&munged_fmt);
len--; /* drop munged space */
}
strbuf_release(&munged_fmt);
strbuf_setlen(sb, sb->len + len);
}

Просмотреть файл

@ -340,8 +340,14 @@ extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
/**
* Add the time specified by `tm`, as formatted by `strftime`.
* `tz_name` is used to expand %Z internally unless it's NULL.
* `tz_offset` is in decimal hhmm format, e.g. -600 means six hours west
* of Greenwich, and it's used to expand %z internally. However, tokens
* with modifiers (e.g. %Ez) are passed to `strftime`.
*/
extern void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm);
extern void strbuf_addftime(struct strbuf *sb, const char *fmt,
const struct tm *tm, int tz_offset,
const char *tz_name);
/**
* Read a given size of data from a FILE* pointer to the buffer.

Просмотреть файл

@ -51,6 +51,12 @@ check_show iso-local "$TIME" '2016-06-15 14:13:20 +0000'
check_show raw-local "$TIME" '1466000000 +0000'
check_show unix-local "$TIME" '1466000000'
check_show 'format:%z' "$TIME" '+0200'
check_show 'format-local:%z' "$TIME" '+0000'
check_show 'format:%Z' "$TIME" ''
check_show 'format:%%z' "$TIME" '%z'
check_show 'format-local:%%z' "$TIME" '%z'
# arbitrary time absurdly far in the future
FUTURE="5758122296 -0400"
check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT