From mboxrd@z Thu Jan 1 00:00:00 1970 X-Msuck: nntp://news.gmane.org/gmane.linux.lib.musl.general/4367 Path: news.gmane.org!not-for-mail From: Laurent Bercot Newsgroups: gmane.linux.lib.musl.general Subject: Re: [PATCHv2] Add support for leap seconds in zoneinfo files Date: Thu, 05 Dec 2013 01:13:30 +0000 Message-ID: <529FD33A.8000509@skarnet.org> References: <5294EE35.8040603@skarnet.org> <20131126233212.GE24286@brightrain.aerifal.cx> <529570BB.2060804@skarnet.org> <20131127042550.GI24286@brightrain.aerifal.cx> <529588D8.3020006@skarnet.org> <20131127184307.GN1685@port70.net> <52965735.9070409@skarnet.org> Reply-To: musl@lists.openwall.com NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------020408060904080305060404" X-Trace: ger.gmane.org 1386205995 16431 80.91.229.3 (5 Dec 2013 01:13:15 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 5 Dec 2013 01:13:15 +0000 (UTC) To: musl@lists.openwall.com Original-X-From: musl-return-4371-gllmg-musl=m.gmane.org@lists.openwall.com Thu Dec 05 02:13:22 2013 Return-path: Envelope-to: gllmg-musl@plane.gmane.org Original-Received: from mother.openwall.net ([195.42.179.200]) by plane.gmane.org with smtp (Exim 4.69) (envelope-from ) id 1VoNVJ-0003t0-U0 for gllmg-musl@plane.gmane.org; Thu, 05 Dec 2013 02:13:22 +0100 Original-Received: (qmail 32218 invoked by uid 550); 5 Dec 2013 01:13:20 -0000 Mailing-List: contact musl-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: Original-Received: (qmail 32210 invoked from network); 5 Dec 2013 01:13:19 -0000 X-SourceIP: 89.100.252.69 User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:24.0) Gecko/20100101 Thunderbird/24.1.1 In-Reply-To: <52965735.9070409@skarnet.org> Xref: news.gmane.org gmane.linux.lib.musl.general:4367 Archived-At: This is a multi-part message in MIME format. --------------020408060904080305060404 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit OK, so actually, the patch was right: in a zoneinfo file containing leap seconds, the transition times are given in TAI-10, not "UTC seconds", i.e. times with leap seconds *added*. (Yes, this is confusing, and the tzfile documentation does not make it clear enough, but it's the only behaviour that makes sense.) So all the calculations involving times given in seconds, including transition times, are done within the same referential. i.e. TAI-10, and this is also valid for __tm_gmtoff. Only when converting from/to "seconds since the Epoch" to/from "struct tm" must leap seconds be substracted/added. The new version of the patch, provided in attachment, reads leap seconds directly from the mmapped zoneinfo file instead of storing them in a static table, as requested by Rich; and it's still not invasive. The leap second table scan is still linear, because: * when substracting leap seconds, the scan is done from the future to the past. Since the huge majority of calls involve the current time or a time close to it, the scan usually stops at the first or second element. * when adding leap seconds, the scan has to be done from the past to the future linearly anyway, to apply successive corrections while testing the correct TAI-10 transition time instead of comparing a UTC value to a TAI-10 value. The previous version of the patch didn't get this right. I would love it if this version, modulo any bugs, could make it into 0.9.15. -- Laurent --------------020408060904080305060404 Content-Type: text/plain; charset=windows-1252; name="leapsecs.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="leapsecs.patch" diff -rNU3 src-old/src/time/__secs_to_tm.c src/src/time/__secs_to_tm.c --- src-old/src/time/__secs_to_tm.c 2013-12-04 15:50:13.000000000 +0100 +++ src/src/time/__secs_to_tm.c 2013-12-05 01:34:35.000000000 +0100 @@ -8,6 +8,23 @@ #define DAYS_PER_100Y (365*100 + 24) #define DAYS_PER_4Y (365*4 + 1) +static int leapsecs_sub(long long *t) +{ + long long trans; + int corr; + unsigned int i = __leapsecs_num; + int hit = 0; + for (; i; i--) { + __leapsecs_read(i-1, &trans, &corr); + if (*t >= trans) break; + } + if (i) { + if (*t == trans) hit = 1; + *t -= corr; + } + return hit; +} + int __secs_to_tm(long long t, struct tm *tm) { long long days, secs; @@ -21,6 +38,7 @@ if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) return -1; + int hit = leapsecs_sub(&t); secs = t - LEAPOCH; days = secs / 86400; remsecs = secs % 86400; @@ -76,6 +94,7 @@ tm->tm_hour = remsecs / 3600; tm->tm_min = remsecs / 60 % 60; tm->tm_sec = remsecs % 60; + if (hit) tm->tm_sec++; return 0; } diff -rNU3 src-old/src/time/__tm_to_secs.c src/src/time/__tm_to_secs.c --- src-old/src/time/__tm_to_secs.c 2013-12-04 15:50:13.000000000 +0100 +++ src/src/time/__tm_to_secs.c 2013-12-05 01:38:39.000000000 +0100 @@ -1,5 +1,21 @@ #include "time_impl.h" +static void leapsecs_add(long long *t, int hit) +{ + int oldcorr = 0; + unsigned int i = 0; + for (; i < __leapsecs_num; i++) { + long long trans; + int newcorr; + __leapsecs_read(i, &trans, &newcorr); + if (*t < trans) break; + if (!hit || (*t > trans)) { + *t += newcorr - oldcorr; + } + oldcorr = newcorr; + } +} + long long __tm_to_secs(const struct tm *tm) { int is_leap; @@ -20,5 +36,6 @@ t += 3600LL * tm->tm_hour; t += 60LL * tm->tm_min; t += tm->tm_sec; + leapsecs_add(&t, tm->tm_sec==60); return t; } diff -rNU3 src-old/src/time/__tz.c src/src/time/__tz.c --- src-old/src/time/__tz.c 2013-12-04 15:50:13.000000000 +0100 +++ src/src/time/__tz.c 2013-12-05 01:42:19.000000000 +0100 @@ -17,6 +17,8 @@ static char dst_name[TZNAME_MAX+1]; const char __gmt[] = "GMT"; +unsigned int __leapsecs_num; + static int dst_off; static int r0[5], r1[5]; @@ -105,6 +107,22 @@ return (unsigned)z[0]<<24 | z[1]<<16 | z[2]<<8 | z[3]; } +static uint64_t zi_read64(const unsigned char *z) +{ + return ((uint64_t)zi_read32(z) << 32) | (uint64_t)zi_read32(z+4); +} + +void __leapsecs_read(unsigned int i, long long *tt, int *corr) +{ + if (trans > zi+44) { + *tt = (long long)zi_read64(abbrevs_end + 12*i); + *corr = (int)zi_read32(abbrevs_end + 12*i + 8); + } else { + *tt = (long long)zi_read32(abbrevs_end + 8*i); + *corr = (int)zi_read32(abbrevs_end + 8*i + 4); + } +} + static size_t zi_dotprod(const unsigned char *z, const unsigned char *v, size_t n) { size_t y; @@ -175,7 +193,7 @@ zi = map; if (map) { int scale = 2; - if (sizeof(time_t) > 4 && map[4]=='2') { + if (sizeof(time_t) > 4 && (map[4]=='2') || (map[4]=='3')) { size_t skip = zi_dotprod(zi+20, VEC(1,1,8,5,6,1), 6); trans = zi+skip+44+44; scale++; @@ -186,6 +204,7 @@ types = index + zi_read32(trans-12); abbrevs = types + 6*zi_read32(trans-8); abbrevs_end = abbrevs + zi_read32(trans-4); + __leapsecs_num = zi_read32(trans-16); if (zi[map_size-1] == '\n') { for (s = (const char *)zi+map_size-2; *s!='\n'; s--); s++; diff -rNU3 src-old/src/time/time_impl.h src/src/time/time_impl.h --- src-old/src/time/time_impl.h 2013-12-04 15:50:13.000000000 +0100 +++ src/src/time/time_impl.h 2013-12-04 16:50:21.000000000 +0100 @@ -7,3 +7,5 @@ int __secs_to_tm(long long, struct tm *); void __secs_to_zone(long long, int, int *, long *, long *, const char **); const unsigned char *__map_file(const char *, size_t *); +extern unsigned int __leapsecs_num; +void __leapsecs_read(unsigned int, long long *, int *); --------------020408060904080305060404--