tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

strftime(3) oddities with %s, %z



strftime(3) %s format is not in ISO C or POSIX, though the rumor is
that it will be in the next POSIX version.

Our man page and Solaris man page both just say it's the number of
seconds since the Epoch.

glibc says (ubuntu 22.04):

  The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
  (TZ) (Calculated from mktime(tm).)

We also use mktime internally in our strftime(3).

The problem though is that mktime(3) is specified to convert tm
"expressed as local time".  We used to use mktime(3) directly, we now
use mktime_z(), but with explicit local tz.

The test below produces ("g" is for tm from gmtime, "l" is for tm from
localtime):

    time_t        mktime       %s          %F %T (%z)
netbsd:
g:  1659968100 -> 1659957300 = 1659957300: 2022-08-08 14:15:00 (+0300)
l:  1659968100 -> 1659968100 = 1659968100: 2022-08-08 17:15:00 (+0300)

glic:
g:  1659968100 -> 1659957300 = 1659957300: 2022-08-08 14:15:00 (+0000)
l:  1659968100 -> 1659968100 = 1659968100: 2022-08-08 17:15:00 (+0300)

musl:
g:  1659968100 -> 1659957300 = 1659968100: 2022-08-08 14:15:00 (+0000)
l:  1659968100 -> 1659968100 = 1659968100: 2022-08-08 17:15:00 (+0300)

Note that both netbsd and glibc adjust the %s value by the local tz
offset for struct tm returned by gmtime.

Note that netbsd additionally gets the %z wrong for the tm obtained
from gmtime(3).

musl seems to do the right thing.  Its mktime obeys the "expressed as
local time" requirement and ignores tz-related state in tm, but %s
gets you the original time_t back from the

     time_t -> struct tm -> strftime("%s")

roundtrip regardless of how struct tm was obtained.

Any comments are appreciated.


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

int
main()
{
    time_t clock = 1659968100ULL;

    struct tm gtm, ltm;
    gmtime_r(&clock, &gtm);
    localtime_r(&clock, &ltm);

    char gtime[128], ltime[64];
    strftime(gtime, sizeof(gtime), "%s: %F %T (%z)", &gtm);
    strftime(ltime, sizeof(ltime), "%s: %F %T (%z)", &ltm);
    printf("%llu -> %llu = %s\n",
       (unsigned long long)clock,
          (unsigned long long)mktime(&gtm), gtime);
    printf("%llu -> %llu = %s\n",
       (unsigned long long)clock,
          (unsigned long long)mktime(&ltm), ltime);

    return 0;
}
---8--------8<----

-uwe


Home | Main Index | Thread Index | Old Index