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>