On Sat, Jun 03, 2017 at 12:40:34PM +0200, René Scharfe wrote: > 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 by passing NULL as timezone name. > > 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 nothing in case of missing info. This direction looks good to me overall. It's not pretty, but I think it's the least-bad option. > --- > Duplicates strbuf_expand to a certain extent, but not too badly, I > think. Leaves the door open for letting strftime handle the local > case. I guess you'd plan to do that like this in the caller: if (date->local) tz_name = NULL; else tz_name = ""; and then your strftime() doesn't do any %z expansion when tz_name is NULL. I was thinking that we would need to have it take the actual time_t, and then it would be able to do the tzset/localtime dance itself. But since I don't think we're planning to do that (if anything we'd just handle the normal localtime() case), the complication it would add to the interface isn't worth it. > -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; Now we're doing two types of munging: sometimes handling %z, and sometimes the extra-space hack. I had to read through it carefully to make sure we handle all cases correctly, but I think it works. In particular, I worried about us setting "fmt" to munged_fmt.buf for the %z case, and then later adding the extra space to it for the zero-length hack, which might reallocate, leaving "fmt" pointing to unallocated memory. But it's OK because at that point we never touch the original "fmt" again. > /** > - * Add the time specified by `tm`, as formatted by `strftime`. > - */ > -extern void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm); > + * Add the time specified by `tm`, as formatted by `strftime`. `tz_offset` > + * and `tz_name` are used to expand %z and %Z internally, unless `tz_name` > + * is NULL. `tz_offset` is in decimal hhmm format, e.g. -600 means six > + * hours west of Greenwich. > + */ > +extern void strbuf_addftime(struct strbuf *sb, const char *fmt, > + const struct tm *tm, int tz_offset, > + const char *tz_name); Good, documentation (the diff order put the implementation first so I scratched my head for a moment before realizing you had already described it). -Peff