Bug 17189 - Incorrect result for %s in strftime
Summary: Incorrect result for %s in strftime
Status: RESOLVED INVALID
Alias: None
Product: glibc
Classification: Unclassified
Component: time (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-07-21 16:09 UTC by daurnimator
Modified: 2015-11-20 01:14 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description daurnimator 2014-07-21 16:09:56 UTC
The %s specifier in strftime doesn't respect timezones.

Example:

    #include <time.h>
    #include <stdio.h>

    int main() {
        time_t t = 1234567890;
        struct tm *tmp = gmtime(&t);
        char output[200];
        strftime(output, 200, "%s", tmp);
        printf("%s\n",output);
        return 0;
    }

Run with a non UTC timezone. e.g. TZ=GMT+9
Output will be 1234600290 instead of expected 1234567890


Location in glibc: http://fossies.org/dox/glibc-2.19/strftime__l_8c_source.html#l01133

This bug also exists in BSD's libc: https://svnweb.freebsd.org/base/release/10.0.0/lib/libc/stdtime/strftime.c?view=markup#l312

It does not occur in musl: http://git.musl-libc.org/cgit/musl/tree/src/time/strftime.c?id=ac0acd569e01735fc6052d43fdf57f3a07c93f3d#n127
Comment 1 Nicholas Miell 2015-11-19 23:42:03 UTC
This is the documented behavior in the manual:

Calling 'strftime' also sets the current time zone as if 'tzset'
were called; 'strftime' uses this information instead of
BROKENTIME's 'tm_gmtoff' and 'tm_zone' members.

The %s format isn't specified by POSIX and appears to originate in the strftime implementation accompanying the tzdata database. The glibc behavior is compatible -- the original implementation converts the supplied struct tm back to a time_t using mktime(), and mktime() is defined by POSIX to use the timezone as set by tzset(). (POSIX's definition of struct tm doesn't even have the tm_zone or tm_gmtoff members.)
Comment 2 daurnimator 2015-11-20 00:13:07 UTC
> This is the documented behavior in the manual:

> Calling 'strftime' also sets the current time zone as if 'tzset'
> were called; 'strftime' uses this information instead of
> BROKENTIME's 'tm_gmtoff' and 'tm_zone' members.

The %s specifier is defined as printing the **UTC timestamp**. From the man page:

       %s     The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). (TZ)

In reality, it prints the UTC timestamp, but adjusted for the local time offset.
This is wrong (and not as documented)
At the very least this means we have a documentation bug.
Comment 3 Nicholas Miell 2015-11-20 01:14:22 UTC
Again, from the manual:

Calling 'strftime' also sets the current time zone as if 'tzset'
were called; 'strftime' uses this information instead of
BROKENTIME's 'tm_gmtoff' and 'tm_zone' members.

This means that strftime() always treats the supplied struct tm as if it were in the local time zone; this includes the conversion to UTC.

If you supply a struct tm to strftime() that isn't in the local time zone, naturally the conversion from the local time zone to UTC will be wrong.