diff options
author | Rich Felker <dalias@aerifal.cx> | 2013-08-24 12:59:02 -0400 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2013-08-24 12:59:02 -0400 |
commit | d78be392e144c338f58ce6a51d82c859126c137d (patch) | |
tree | 154cc7c69af95a812cfb2d3dab8622e6f9bd5a77 /src/time/strftime.c | |
parent | 0f9b1f672b68b7c3570f07b130cc5c8938b22bad (diff) | |
download | musl-d78be392e144c338f58ce6a51d82c859126c137d.tar.gz musl-d78be392e144c338f58ce6a51d82c859126c137d.tar.xz musl-d78be392e144c338f58ce6a51d82c859126c137d.zip |
fix strftime handling of time zone data
this may need further revision in the future, since POSIX is rather unclear on the requirements, and is designed around the assumption of POSIX TZ specifiers which are not sufficiently powerful to represent real-world timezones (this is why zoneinfo support was added). the basic issue is that strftime gets the string and numeric offset for the timezone from the extra fields in struct tm, which are initialized when calling localtime/gmtime/etc. however, a conforming application might have created its own struct tm without initializing these fields, in which case using __tm_zone (a pointer) could crash. other zoneinfo-based implementations simply check for a null pointer, but otherwise can still crash of the field contains junk. simply ignoring __tm_zone and using tzname[] would "work" but would give incorrect results in time zones with more complex rules. I feel like this would lower the quality of implementation. instead, simply validate __tm_zone: unless it points to one of the zone name strings managed by the timezone system, assume it's invalid. this commit also fixes several other minor bugs with formatting: tm_isdst being negative is required to suppress printing of the zone formats, and %z was using the wrong format specifiers since the type of val was changed, resulting in bogus output.
Diffstat (limited to 'src/time/strftime.c')
-rw-r--r-- | src/time/strftime.c | 16 |
1 files changed, 13 insertions, 3 deletions
diff --git a/src/time/strftime.c b/src/time/strftime.c index 24000f3b..48f65320 100644 --- a/src/time/strftime.c +++ b/src/time/strftime.c @@ -43,6 +43,7 @@ static int week_num(const struct tm *tm) return val; } +const char *__tm_to_tzname(const struct tm *); size_t __strftime_l(char *restrict, size_t, const char *restrict, const struct tm *restrict, locale_t); const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc) @@ -166,11 +167,20 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * width = 4; goto number; case 'z': - val = -tm->__tm_gmtoff; - *l = snprintf(*s, sizeof *s, "%+.2d%.2d", val/3600, abs(val%3600)/60); + if (tm->tm_isdst < 0) { + *l = 0; + return ""; + } + *l = snprintf(*s, sizeof *s, "%+.2d%.2d", + (-tm->__tm_gmtoff)/3600, + abs(tm->__tm_gmtoff%3600)/60); return *s; case 'Z': - fmt = tm->__tm_zone; + if (tm->tm_isdst < 0) { + *l = 0; + return ""; + } + fmt = __tm_to_tzname(tm); goto string; case '%': *l = 1; |