Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20180118163452.GQ1627@brightrain.aerifal.cx>
Date: Thu, 18 Jan 2018 11:34:52 -0500
From: Rich Felker <dalias@...c.org>
To: musl@...ts.openwall.com
Subject: Re: Differences between strftime in musl and glibc

On Thu, Jan 18, 2018 at 04:25:07PM +0000, Adrián López Tejedor wrote:
> Hi,
> 
> I was having a problem parsing dates with python in an Alpine container and
> after some time digging I have found a difference in how musl and
> glibc calculate the unix Epoch using strftime.
> 
> The format specifier to get the unix epoch is "%s", which is an extension
> of GNU (
> https://www.gnu.org/software/libc/manual/html_node/Formatting-Calendar-Time..html
> ).
> 
> In Glibc they use mktime (
> https://github.com/bminor/glibc/blob/master/time/strftime_l.c#L1127), being
> this function aware of time zone.
> 
> In musl is calculated without mktime, adding the seconds of each part of
> the tm struct (
> https://git.musl-libc.org/cgit/musl/tree/src/time/strftime.c?h=v1.1.18#n132)
> and substracting the GMT offset. This method is unaware of the TZ data (I
> have not seen how to define that gmt offset when using strptime).
> 
> Calling directly to musl/mktime work as expected.
> 
> I have attached a small program which shows the difference between musl and
> glibc.
> 
> Compiled with glibc:
> $ TZ=Europe/Madrid ./a.out
> strftime: -3600
> mktime: -3600
> $ TZ=America/New_York ./a.out
> strftime: 18000
> mktime: 18000
> 
> 
> Compiled with musl:
> $ TZ=Europe/Madrid ./a.out
> strftime: 0
> mktime: -3600
> $ TZ=America/New_York ./a.out
> strftime: 0
> mktime: 18000
> 
> 
> Thanks!
> Adrián

> #define _XOPEN_SOURCE
> #include "stdio.h"
> #include "stdlib.h"
> #include "time.h"
> #include "string.h"
> 
> 
> int main(void) {
>   char buf[100];
>   struct tm tm1, tm2;
>   memset(&tm1, 0, sizeof(struct tm));
>   memset(&tm2, 0, sizeof(struct tm));
>   strptime("1/1/1970 00:00:00", "%d/%m/%Y %H:%M:%S", &tm1);
>   strptime("1/1/1970 00:00:00", "%d/%m/%Y %H:%M:%S", &tm2);
> 
>   strftime(buf, 100, "%s", &tm1);
>   printf("strftime: %s\n", buf);
> 
>   time_t seg = mktime(&tm2);
>   printf("mktime: %ld\n", seg);
> 
>   return 0;
> }

The glibc behavior assumes the tm object is in units of local time (in
the current time zone), which is a really bad assumption, since
everything else about the strftime API is timezone-agnostic and works
with tm objects produced by gmtime() as well as local times. So I
believe the musl behavior is correct and the glibc behavior is wrong,
but I'm open to further discussion. It probably needs to happen on the
Austin Group tracker/list, as %s is scheduled for inclusion in future
versions of POSIX but grossly underspecified:

http://austingroupbugs.net/view.php?id=169

In particular, the glibc behavior requires that strftime become aware
of [changes in] the timezone, which it's currently not specified to
do, and there's no specification of if or how strftime determines if
the argument is a local or UTC time.

My preference would be to say that, unless the struct tm was produced
by localtime[_r] or derived from such a struct tm, it's unspecified
what time zone %s is referenced against. But I'm open to discussion as
part of the standardization process.

Rich

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.