Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 20 May 2018 10:08:13 -0700
From:      Eitan Adler <lists@eitanadler.com>
To:        FreeBSD Standards <freebsd-standards@freebsd.org>
Subject:   gmtime is not POSIX compliant due to leap seconds
Message-ID:  <CAF6rxgk5LKy8%2BXFc2yUY-ROmW=XZ500EStf-B9qi5qdGZ=Eh8g@mail.gmail.com>

next in thread | raw e-mail | index | archive | help
Hi all,

FreeBSD's gmtime(3) appears to be (possibly intentionally) non-POSIX compli=
ant.

Background:

in C (ISO/IEC 9899:201x): The gmtime function converts the calendar
time pointed to by timer into a broken- down time, expressed as UTC.
It is implementation defined as to if leap seconds or such are taken
into account.

in POSIX (EEE Std 1003.1-2017):

http://pubs.opengroup.org/onlinepubs/9699919799/functions/gmtime.html
The gmtime() function shall convert the time in seconds since the
Epoch pointed to by timer into a broken-down time, expressed as
Coordinated Universal Time (UTC).  The phrase "seconds since the
Epoch" is noted as
"As represented in seconds since the Epoch, each and every day shall
be accounted for by exactly 86400 seconds."

(see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.htm=
l#tag_04_16)

This means that gmtime ought not to be affected by leap seconds.

The issue:

On a POSIX-compliant system:

=E2=88=B4./gmtime-print 1234567899
year=3D109
mon=3D1
mday=3D13
hour=3D23
min=3D31
sec=3D39
wday=3D5
yday=3D43
dst=3D0
tzone=3DUTC
gmoff=3D0


On FreeBSD:
FreeBSD fasteagle 12.0-CURRENT FreeBSD 12.0-CURRENT #9
r333704M-4294967295: Thu May 17 05:01:04 UTC 2018
eax@fasteagle:/srv/obj/fbsd/usr/src/amd64.amd64/sys/EADLER  amd64

=E2=88=B4./gmtime-print 1234567899
year=3D109
mon=3D1
mday=3D13
hour=3D23
min=3D31
sec=3D15
wday=3D5
yday=3D43
dst=3D0
tzone=3DUTC
gmoff=3D0

Note the difference in "sec": 39 (correct) vs 15 (incorrect). The
difference is 39-15=3D24 which is the number of leap seconds until
Feburary 2009.   On both systems TZ is unset.

----


Some spelunking.

The relevant code is contributed, though I'm not sure what the current
upstream is. See head/contrib/tzcode/stdtime.

Following either the threaded or non-threaded logic does not change
the analysis :
                gmtsub(timep, 0L, &tm); vs
gmtsub(timep, 0L, p_tm);

Following gmstub:
        result =3D timesub(timep, offset, gmtptr, tmp);

The remainder of the code is for prettyprinting the timezone and thus
not relevant.

Following timesub we see that tzh_leapcnt is explicitly handled which
is defined as
tzfile.h:       char    tzh_leapcnt[4];         /* coded number of
leap seconds */

...

Interestingly the code appears to have accounted for this issue and impleme=
nted

time_t
time2posix(time_t t)
{
        tzset();
        return t - leapcorr(&t);
}

to help resolve the issue. We also have a man page for it
(time2posix). However, it isn't visible in any header I could:
=E2=88=B4grep -ir time2posix /usr/include

returns empty.



If all of this is correct I'd like to take some of the following
actions though I'm not sure what's best

(a) Modify the code to not take into account leap seconds at all. This
is preferred IMHO unless it will break things in ways I don't expect
(b) Make time2posix actually visible to applications (or at least,
correct the documentation of how to make it visible)
(c) semi-related: I can't find an upstream for this code, so move it
out of contrib and into our libc.

Thoughts?

(originally noticed by myself here https://bugs.python.org/issue33579;
addtl debugging done by others on the bug)
--=20
Eitan Adler



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAF6rxgk5LKy8%2BXFc2yUY-ROmW=XZ500EStf-B9qi5qdGZ=Eh8g>