"Fossies" - the Fresh Open Source Software Archive

Member "dateutils-0.4.6/contrib/tzraw.c" (19 Mar 2019, 17407 Bytes) of package /linux/privat/dateutils-0.4.6.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "tzraw.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.4.5_vs_0.4.6.

    1 /*** tzraw.c -- reader for olson database zoneinfo files
    2  *
    3  * Copyright (C) 2009-2019 Sebastian Freundt
    4  *
    5  * Author:  Sebastian Freundt <freundt@ga-group.nl>
    6  *
    7  * This file is part of uterus and dateutils.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  *
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  *
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  *
   20  * 3. Neither the name of the author nor the names of any contributors
   21  *    may be used to endorse or promote products derived from this
   22  *    software without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
   25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   27  * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
   31  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   32  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
   33  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
   34  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   35  *
   36  ***/
   37 /* implementation part of tzraw.h */
   38 #if !defined INCLUDED_tzraw_c_
   39 #define INCLUDED_tzraw_c_
   40 
   41 #if defined HAVE_CONFIG_H
   42 # include "config.h"
   43 #endif  /* HAVE_CONFIG_H */
   44 #if defined MAP_ANON_NEEDS_DARWIN_SOURCE
   45 # define _DARWIN_C_SOURCE
   46 #endif  /* MAP_ANON_NEEDS_DARWIN_SOURCE */
   47 #if defined MAP_ANON_NEEDS_ALL_SOURCE
   48 # define _ALL_SOURCE
   49 #endif  /* MAP_ANON_NEEDS_ALL_SOURCE */
   50 #include <stddef.h>
   51 #include <stdint.h>
   52 #include <stdbool.h>
   53 #include <stdlib.h>
   54 #include <string.h>
   55 #include <unistd.h>
   56 #include <sys/mman.h>
   57 #include <sys/stat.h>
   58 #include <fcntl.h>
   59 #include <time.h>
   60 #include <limits.h>
   61 
   62 #if defined HAVE_TZFILE_H
   63 # include <tzfile.h>
   64 #endif  /* HAVE_TZFILE_H */
   65 
   66 /* for be/le conversions */
   67 #include "boops.h"
   68 /* for LIKELY/UNLIKELY/etc. */
   69 #include "nifty.h"
   70 /* me own header, innit */
   71 #include "tzraw.h"
   72 /* for leap corrections */
   73 #include "leap-seconds.h"
   74 
   75 #if !defined DEFUN
   76 # define DEFUN
   77 #endif  /* !DEFUN */
   78 
   79 #if !defined MAP_ANONYMOUS && defined MAP_ANON
   80 # define MAP_ANONYMOUS  (MAP_ANON)
   81 #elif !defined MAP_ANON
   82 # define MAP_ANON   (0x1000U)
   83 #endif  /* MAP_ANON->MAP_ANONYMOUS */
   84 
   85 typedef struct zih_s *zih_t;
   86 typedef int32_t *ztr_t;
   87 typedef uint8_t *zty_t;
   88 typedef struct ztrdtl_s *ztrdtl_t;
   89 typedef char *znam_t;
   90 
   91 /* this is tzhead but better */
   92 struct zih_s {
   93     /* magic */
   94     char tzh_magic[4];
   95     /* must be '2' now, as of 2005 */
   96     char tzh_version[1];
   97     /* reserved--must be zero */
   98     char tzh_reserved[15];
   99     /* number of transition time flags in gmt */
  100     uint32_t tzh_ttisgmtcnt;
  101     /* number of transition time flags in local time */
  102     uint32_t tzh_ttisstdcnt;
  103     /* number of recorded leap seconds */
  104     uint32_t tzh_leapcnt;
  105     /* number of recorded transition times */
  106     uint32_t tzh_timecnt;
  107     /* number of local time type */
  108     uint32_t tzh_typecnt;
  109     /* number of abbreviation chars */
  110     uint32_t tzh_charcnt;
  111 };
  112 
  113 /* this one must be packed to account for the packed file layout */
  114 struct ztrdtl_s {
  115     int32_t offs;
  116     uint8_t dstp;
  117     uint8_t abbr;
  118 } __attribute__((packed));
  119 
  120 /* convenience struct where we copy all the good things into one */
  121 struct zspec_s {
  122     int32_t since;
  123     unsigned int offs:31;
  124     unsigned int dstp:1;
  125     znam_t name;
  126 } __attribute__((packed, aligned(16)));
  127 
  128 /* for leap second transitions */
  129 struct zleap_tr_s {
  130     /* cut-off stamp */
  131     int32_t t;
  132     /* cumulative correction since T */
  133     int32_t corr;
  134 };
  135 
  136 /* leap second support missing */
  137 struct zif_s {
  138     size_t mpsz;
  139     zih_t hdr;
  140 
  141     /* transitions */
  142     ztr_t trs;
  143     /* types */
  144     zty_t tys;
  145     /* type array, deser'd, transition details array */
  146     ztrdtl_t tda;
  147     /* zonename array */
  148     znam_t zn;
  149 
  150     /* file descriptor, if >0 this also means all data is in BE */
  151     int fd;
  152 
  153     /* for special zones */
  154     coord_zone_t cz;
  155 
  156     /* zone caching, between PREV and NEXT the offset is OFFS */
  157     struct zrng_s cache;
  158 };
  159 
  160 
  161 #if defined TZDIR
  162 static const char tzdir[] = TZDIR;
  163 #else  /* !TZDIR */
  164 static const char tzdir[] = "/usr/share/zoneinfo";
  165 #endif
  166 
  167 #if defined ZONEINFO_UTC_RIGHT
  168 /* where can we deduce some info for our coordinated zones */
  169 static const char coord_fn[] = ZONEINFO_UTC_RIGHT;
  170 #elif defined ZONEINFO_UTC
  171 static const char coord_fn[] = ZONEINFO_UTC;
  172 #else  /* !ZONEINFO_UTC_RIGHT && !ZONEINFO_UTC */
  173 static const char coord_fn[] = "/usr/share/zoneinfo/UTC";
  174 #endif  /* ZONEINFO_UTC_RIGHT || ZONEINFO_UTC */
  175 
  176 #define PROT_MEMMAP PROT_READ | PROT_WRITE
  177 #define MAP_MEMMAP  MAP_PRIVATE | MAP_ANON
  178 
  179 #define AS_MUT_ZIF(x)   ((struct zif_s*)deconst(x))
  180 
  181 /* special zone names */
  182 static const char coord_zones[][4] = {
  183     "",
  184     "UTC",
  185     "TAI",
  186     "GPS",
  187 };
  188 
  189 static void*
  190 deconst(const void *ptr)
  191 {
  192     return (char*)1 + ((const char*)ptr - (char*)1U);
  193 }
  194 
  195 /**
  196  * Return the total number of transitions in zoneinfo file Z. */
  197 DEFUN inline size_t
  198 zif_ntrans(const struct zif_s z[static 1U])
  199 {
  200     return z->hdr->tzh_timecnt;
  201 }
  202 
  203 /**
  204  * Return the total number of transition types in zoneinfo file Z. */
  205 static inline size_t
  206 zif_ntypes(const struct zif_s z[static 1U])
  207 {
  208     return z->hdr->tzh_typecnt;
  209 }
  210 
  211 /**
  212  * Return the total number of transitions in zoneinfo file Z. */
  213 static inline size_t
  214 zif_nchars(const struct zif_s z[static 1U])
  215 {
  216     return z->hdr->tzh_charcnt;
  217 }
  218 
  219 
  220 /**
  221  * Return the total number of leap second transitions. */
  222 static __attribute__((unused)) inline size_t
  223 zif_nleaps(const struct zif_s z[static 1U])
  224 {
  225     return z->hdr->tzh_leapcnt;
  226 }
  227 
  228 /**
  229  * Return the transition time stamp of the N-th transition in Z. */
  230 static inline int32_t
  231 zif_trans(const struct zif_s z[static 1U], int n)
  232 {
  233     size_t ntr = zif_ntrans(z);
  234 
  235     if (UNLIKELY(!ntr || n < 0)) {
  236         /* return earliest possible stamp */
  237         return INT_MIN;
  238     } else if (UNLIKELY(n >= (ssize_t)ntr)) {
  239         /* return last known stamp */
  240         return z->trs[ntr - 1U];
  241     }
  242     /* otherwise return n-th stamp */
  243     return z->trs[n];
  244 }
  245 
  246 /**
  247  * Return the transition type index of the N-th transition in Z. */
  248 static inline uint8_t
  249 zif_type(const struct zif_s z[static 1U], int n)
  250 {
  251     size_t ntr = zif_ntrans(z);
  252 
  253     if (UNLIKELY(!ntr || n < 0)) {
  254         /* return unknown type */
  255         return 0;
  256     } else if (UNLIKELY(n >= (ssize_t)ntr)) {
  257         /* return last known type */
  258         return z->tys[ntr - 1U];
  259     }
  260     /* otherwise return n-th type */
  261     return z->tys[n];
  262 }
  263 
  264 /**
  265  * Return the transition details after the N-th transition in Z. */
  266 inline struct ztrdtl_s
  267 zif_trdtl(const struct zif_s z[static 1U], int n)
  268 {
  269 /* no bound check! */
  270     struct ztrdtl_s res;
  271     uint8_t idx = zif_type(z, n);
  272     res = z->tda[idx];
  273     return res;
  274 }
  275 
  276 /**
  277  * Return the gmt offset the N-th transition in Z. */
  278 static inline int32_t
  279 zif_troffs(const struct zif_s z[static 1U], int n)
  280 {
  281 /* no bound check! */
  282     uint8_t idx = zif_type(z, n);
  283     return z->tda[idx].offs;
  284 }
  285 
  286 /**
  287  * Return the zonename after the N-th transition in Z. */
  288 static __attribute__((unused)) inline znam_t
  289 zif_trname(const struct zif_s z[static 1U], int n)
  290 {
  291 /* no bound check! */
  292     uint8_t idx = zif_type(z, n);
  293     uint8_t jdx = z->tda[idx].abbr;
  294     return z->zn + jdx;
  295 }
  296 
  297 /**
  298  * Return a succinct summary of the situation after transition N in Z. */
  299 static __attribute__((unused)) inline struct zspec_s
  300 zif_spec(const struct zif_s z[static 1U], int n)
  301 {
  302     struct zspec_s res;
  303     uint8_t idx = zif_type(z, n);
  304     uint8_t jdx = z->tda[idx].abbr;
  305 
  306     res.since = zif_trans(z, n);
  307     res.offs = z->tda[idx].offs;
  308     res.dstp = z->tda[idx].dstp;
  309     res.name = z->zn + jdx;
  310     return res;
  311 }
  312 
  313 
  314 static coord_zone_t
  315 coord_zone(const char *zone)
  316 {
  317     for (coord_zone_t i = TZCZ_UTC; i < TZCZ_NZONE; i++) {
  318         if (strcmp(zone, coord_zones[i]) == 0) {
  319             return i;
  320         }
  321     }
  322     return TZCZ_UNK;
  323 }
  324 
  325 static int
  326 __open_zif(const char *file)
  327 {
  328     if (file == NULL || file[0] == '\0') {
  329         return -1;
  330     } else if (file[0] != '/') {
  331         /* not an absolute file name */
  332         size_t len = strlen(file);
  333         size_t tzd_len = sizeof(tzdir) - 1;
  334         char new[tzd_len + 1U + len + 1U];
  335         char *tmp = new + tzd_len;
  336 
  337         memcpy(new, tzdir, tzd_len);
  338         *tmp++ = '/';
  339         memcpy(tmp, file, len + 1U);
  340         return open(new, O_RDONLY, 0644);
  341     }
  342     /* absolute file name, just try with that one then */
  343     return open(file, O_RDONLY, 0644);
  344 }
  345 
  346 static void
  347 __init_zif(struct zif_s z[static 1U])
  348 {
  349     size_t ntr;
  350     size_t nty;
  351 
  352     if (z->fd > STDIN_FILENO) {
  353         /* probably in BE then, eh? */
  354         ntr = be32toh(zif_ntrans(z));
  355         nty = be32toh(zif_ntypes(z));
  356     } else {
  357         ntr = zif_ntrans(z);
  358         nty = zif_ntypes(z);
  359     }
  360 
  361     z->trs = (ztr_t)(z->hdr + 1);
  362     z->tys = (zty_t)(z->trs + ntr);
  363     z->tda = (ztrdtl_t)(z->tys + ntr);
  364     z->zn = (char*)(z->tda + nty);
  365     return;
  366 }
  367 
  368 static int
  369 __read_zif(struct zif_s tgt[static 1U], int fd)
  370 {
  371     struct stat st;
  372 
  373     if (fstat(fd, &st) < 0) {
  374         return -1;
  375     } else if (st.st_size <= 4) {
  376         return -1;
  377     }
  378     tgt->mpsz = st.st_size;
  379     tgt->fd = fd;
  380     tgt->hdr = mmap(NULL, tgt->mpsz, PROT_READ, MAP_SHARED, fd, 0);
  381     if (tgt->hdr == MAP_FAILED) {
  382         return -1;
  383     }
  384     /* all clear so far, populate */
  385     __init_zif(tgt);
  386     return 0;
  387 }
  388 
  389 static void
  390 __conv_hdr(struct zih_s tgt[static 1U], const struct zih_s src[static 1U])
  391 {
  392 /* copy SRC to TGT doing byte-order conversions on the way */
  393     memcpy(tgt, src, offsetof(struct zih_s, tzh_ttisgmtcnt));
  394     tgt->tzh_ttisgmtcnt = be32toh(src->tzh_ttisgmtcnt);
  395     tgt->tzh_ttisstdcnt = be32toh(src->tzh_ttisstdcnt);
  396     tgt->tzh_leapcnt = be32toh(src->tzh_leapcnt);
  397     tgt->tzh_timecnt = be32toh(src->tzh_timecnt);
  398     tgt->tzh_typecnt = be32toh(src->tzh_typecnt);
  399     tgt->tzh_charcnt = be32toh(src->tzh_charcnt);
  400     return;
  401 }
  402 
  403 static void
  404 __conv_zif(struct zif_s tgt[static 1U], const struct zif_s src[static 1U])
  405 {
  406     size_t ntr;
  407     size_t nty;
  408     size_t nch;
  409 
  410     /* convert header to hbo */
  411     __conv_hdr(tgt->hdr, src->hdr);
  412 
  413     /* everything in host byte-order already */
  414     ntr = zif_ntrans(tgt);
  415     nty = zif_ntypes(tgt);
  416     nch = zif_nchars(tgt);
  417     __init_zif(tgt);
  418 
  419     /* transition vector */
  420     for (size_t i = 0; i < ntr; i++) {
  421         tgt->trs[i] = be32toh(src->trs[i]);
  422     }
  423 
  424     /* type vector, nothing to byte-swap here */
  425     memcpy(tgt->tys, src->tys, ntr * sizeof(*tgt->tys));
  426 
  427     /* transition details vector */
  428     for (size_t i = 0; i < nty; i++) {
  429         tgt->tda[i].offs = be32toh(src->tda[i].offs);
  430         tgt->tda[i].dstp = src->tda[i].dstp;
  431         tgt->tda[i].abbr = src->tda[i].abbr;
  432     }
  433 
  434     /* zone name array */
  435     memcpy(tgt->zn, src->zn, nch * sizeof(*tgt->zn));
  436     return;
  437 }
  438 
  439 static struct zif_s*
  440 __copy_conv(const struct zif_s z[static 1U])
  441 {
  442 /* copy Z and do byte-order conversions */
  443     size_t mpsz;
  444     struct zif_s *res = NULL;
  445 
  446     /* compute a size */
  447     mpsz = z->mpsz + sizeof(*z);
  448 
  449     /* we'll mmap ourselves a slightly larger struct so
  450      * res + 1 points to the header, while res + 0 is the zif_t */
  451     res = mmap(NULL, mpsz, PROT_MEMMAP, MAP_MEMMAP, -1, 0);
  452     if (UNLIKELY(res == MAP_FAILED)) {
  453         return NULL;
  454     }
  455     /* great, now to some initial assignments */
  456     res->mpsz = mpsz;
  457     res->hdr = (void*)(res + 1);
  458     /* make sure we denote that this isnt connected to a file */
  459     res->fd = -1;
  460     /* copy the flags though */
  461     res->cz = z->cz;
  462 
  463     /* convert the header and payload now */
  464     __conv_zif(res, z);
  465 
  466     /* that's all :) */
  467     return res;
  468 }
  469 
  470 static struct zif_s*
  471 __copy(const struct zif_s z[static 1U])
  472 {
  473 /* copy Z into a newly allocated zif_t object
  474  * if applicable also perform byte-order conversions */
  475     struct zif_s *res;
  476 
  477     if (z->fd > STDIN_FILENO) {
  478         return __copy_conv(z);
  479     }
  480     /* otherwise it's a plain copy */
  481     res = mmap(NULL, z->mpsz, PROT_MEMMAP, MAP_MEMMAP, -1, 0);
  482     if (UNLIKELY(res == MAP_FAILED)) {
  483         return NULL;
  484     }
  485     memcpy(res, z, z->mpsz);
  486     __init_zif(res);
  487     return res;
  488 }
  489 
  490 DEFUN zif_t
  491 zif_copy(zif_t z)
  492 {
  493 /* copy Z into a newly allocated zif_t object
  494  * if applicable also perform byte-order conversions */
  495     if (UNLIKELY(z == NULL)) {
  496         /* no need to bother */
  497         return NULL;
  498     }
  499     return __copy(z);
  500 }
  501 
  502 static void
  503 __close(const struct zif_s z[static 1U])
  504 {
  505     if (z->fd > STDIN_FILENO) {
  506         close(z->fd);
  507     }
  508     /* check if z is in mmap()'d space */
  509     if (z->hdr == MAP_FAILED) {
  510         /* not sure what to do */
  511         ;
  512     } else if ((z + 1) != (void*)z->hdr) {
  513         /* z->hdr is mmapped, z is not */
  514         munmap((void*)z->hdr, z->mpsz);
  515     } else {
  516         munmap(deconst(z), z->mpsz);
  517     }
  518     return;
  519 }
  520 
  521 DEFUN void
  522 zif_close(zif_t z)
  523 {
  524     if (UNLIKELY(z == NULL)) {
  525         /* nothing to do */
  526         return;
  527     }
  528     __close((const void*)z);
  529     return;
  530 }
  531 
  532 DEFUN zif_t
  533 zif_open(const char *file)
  534 {
  535     coord_zone_t cz;
  536     int fd;
  537     struct zif_s tmp[1];
  538     struct zif_s *res;
  539 
  540     /* check for special time zones */
  541     if ((cz = coord_zone(file)) > TZCZ_UNK) {
  542         /* use UTC file */
  543         file = coord_fn;
  544     }
  545 
  546     if (UNLIKELY((fd = __open_zif(file)) < STDIN_FILENO)) {
  547         return NULL;
  548     } else if (UNLIKELY(__read_zif(tmp, fd) < 0)) {
  549         return NULL;
  550     }
  551     /* otherwise all's fine, it's still BE
  552      * assign the coord zone type if any and convert to host byte-order */
  553     tmp->cz = cz;
  554     res = __copy(tmp);
  555     __close(tmp);
  556     return res;
  557 }
  558 
  559 
  560 /* for leap corrections */
  561 #include "leap-seconds.def"
  562 
  563 static inline int
  564 __find_trno(const struct zif_s z[static 1U], int32_t t, int min, int max)
  565 {
  566 /* find the last transition before T, T is expected to be UTC
  567  * if T is before any known transition return -1 */
  568     if (UNLIKELY(max == 0)) {
  569         /* special case */
  570         return -1;
  571     } else if (UNLIKELY(t < zif_trans(z, min))) {
  572         return -1;
  573     } else if (UNLIKELY(t > zif_trans(z, max))) {
  574         return max - 1;
  575     }
  576 
  577     do {
  578         int32_t tl, tu;
  579         int this = (min + max) / 2;
  580 
  581         tl = zif_trans(z, this);
  582         tu = zif_trans(z, this + 1);
  583 
  584         if (t >= tl && t < tu) {
  585             /* found him */
  586             return this;
  587         } else if (t >= tu) {
  588             min = this;
  589         } else if (t < tl) {
  590             max = this;
  591         }
  592     } while (true);
  593     /* not reached */
  594 }
  595 
  596 DEFUN inline int
  597 zif_find_trans(zif_t z, int32_t t)
  598 {
  599 /* find the last transition before T, T is expected to be UTC
  600  * if T is before any known transition return -1 */
  601     int max = zif_ntrans(z);
  602     int min = 0;
  603 
  604     return __find_trno(z, t, min, max);
  605 }
  606 
  607 static struct zrng_s
  608 __find_zrng(const struct zif_s z[static 1U], int32_t t, int min, int max)
  609 {
  610     struct zrng_s res;
  611     int trno;
  612 
  613     trno = __find_trno(z, t, min, max);
  614     res.prev = zif_trans(z, trno);
  615     if (UNLIKELY(trno <= 0 && t < res.prev)) {
  616         res.trno = 0U;
  617         res.prev = INT_MIN;
  618         /* assume the first offset has always been there */
  619         res.next = res.prev;
  620     } else if (UNLIKELY(trno < 0)) {
  621         /* special case where no transitions are recorded */
  622         res.trno = 0U;
  623         res.prev = INT_MIN;
  624         res.next = INT_MAX;
  625     } else {
  626         res.trno = (uint8_t)trno;
  627         if (LIKELY(trno + 1U < zif_ntrans(z))) {
  628             res.next = zif_trans(z, trno + 1U);
  629         } else {
  630             res.next = INT_MAX;
  631         }
  632     }
  633     res.offs = zif_troffs(z, res.trno);
  634     return res;
  635 }
  636 
  637 DEFUN inline struct zrng_s
  638 zif_find_zrng(zif_t z, int32_t t)
  639 {
  640 /* find the last transition before time, time is expected to be UTC */
  641     int max = zif_ntrans(z);
  642     int min = 0;
  643 
  644     return __find_zrng(z, t, min, max);
  645 }
  646 
  647 static int32_t
  648 __tai_offs(int32_t t)
  649 {
  650     /* difference of TAI and UTC at epoch instant */
  651     zidx_t zi = leaps_before_si32(leaps_s, nleaps_corr, t);
  652 
  653     return leaps_corr[zi];
  654 }
  655 
  656 static int32_t
  657 __gps_offs(int32_t t)
  658 {
  659 /* TAI - GPS = 19 on 1980-01-06, so use that identity here */
  660     const int32_t gps_offs_epoch = 19;
  661     if (UNLIKELY(t < 315964800)) {
  662         return 0;
  663     }
  664     return __tai_offs(t) - gps_offs_epoch;
  665 }
  666 
  667 static int32_t
  668 __offs(struct zif_s z[static 1U], int32_t t)
  669 {
  670 /* return the offset of T in Z and cache the result. */
  671     int min;
  672     size_t max;
  673 
  674     switch (z->cz) {
  675     default:
  676     case TZCZ_UNK:
  677         break;
  678     case TZCZ_UTC:
  679         return 0;
  680     case TZCZ_TAI:
  681         return __tai_offs(t);
  682     case TZCZ_GPS:
  683         return __gps_offs(t);
  684     }
  685 
  686     /* use the classic code */
  687     if (LIKELY(t >= z->cache.prev && t < z->cache.next)) {
  688         /* use the cached offset */
  689         return z->cache.offs;
  690     } else if (t >= z->cache.next) {
  691         min = z->cache.trno + 1;
  692         max = zif_ntrans(z);
  693     } else if (t < z->cache.prev) {
  694         max = z->cache.trno;
  695         min = 0;
  696     } else {
  697         /* we shouldn't end up here at all */
  698         min = 0;
  699         max = 0;
  700     }
  701     return (z->cache = __find_zrng(z, t, min, max)).offs;
  702 }
  703 
  704 DEFUN int32_t
  705 zif_utc_time(zif_t z, int32_t t)
  706 {
  707 /* here's the setup, given t in local time, we denote the corresponding
  708  * UTC time by t' = t - x' where x' is the true offset
  709  * however, since we do not know the offset in advance, we have to solve
  710  * for an estimate of the offset x:
  711  * t - x + x' = t, or equivalently t - x = t' or as a root finding problem
  712  * x' - x = 0.
  713  * To make this iterative we just solve:
  714  * x_{i+1} - x_i = 0, where x_{i+1} = o(t - x_i) and o maps a given
  715  * time stamp to an offset. */
  716 /* make me use the cache please! */
  717     /* let's go */
  718     int32_t xi = 0;
  719     int32_t xj;
  720     int32_t old = -1;
  721 
  722     /* jump off the cliff if Z is nought */
  723     if (UNLIKELY(z == NULL)) {
  724         return t;
  725     }
  726 
  727     while ((xj = __offs(AS_MUT_ZIF(z), t - xi)) != xi && xi != old) {
  728         old = xi = xj;
  729     }
  730     return t - xj;
  731 }
  732 
  733 /* convert utc to local */
  734 DEFUN int32_t
  735 zif_local_time(zif_t z, int32_t t)
  736 {
  737     /* jump off the cliff if Z is nought */
  738     if (UNLIKELY(z == NULL)) {
  739         return t;
  740     }
  741     return t + __offs(AS_MUT_ZIF(z), t);
  742 }
  743 
  744 #endif  /* INCLUDED_tzraw_c_ */
  745 /* tzraw.c ends here */