]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
got rid of crlf windows endings on timezone.c
authorcypromis <michal.bielicki@seventhsignal.de>
Sat, 24 Jul 2010 21:07:27 +0000 (23:07 +0200)
committercypromis <michal.bielicki@seventhsignal.de>
Sat, 24 Jul 2010 21:07:27 +0000 (23:07 +0200)
libs/spandsp/src/timezone.c

index d0cab0989ec9421463f939304e9742df4aa8b639..b991531e6926a73559c3a4f8fd3762aa2670f42b 100644 (file)
-/*\r
- * SpanDSP - a series of DSP components for telephony\r
- *\r
- * timezone.c - Timezone handling for time interpretation\r
- *\r
- * Written by Steve Underwood <steveu@coppice.org>\r
- *\r
- * Copyright (C) 2010 Steve Underwood\r
- *\r
- * All rights reserved.\r
- *\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU Lesser General Public License version 2.1,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
- */\r
-\r
-/*! \file */\r
-\r
-/* Timezone processing might not seem like a DSP activity, but getting the headers\r
-   right on FAXes demands it. We need to handle multiple time zones within a process,\r
-   for FAXes related to different parts of the globe, so the system timezone handling\r
-   is not adequate. */\r
-\r
-/* This timezone handling is derived from public domain software by Arthur David Olson\r
-   <arthur_david_olson@nih.gov> which you may download from ftp://elsie.nci.nih.gov/pub\r
-   at the time of writing. */\r
-\r
-#if defined(HAVE_CONFIG_H)\r
-#include "config.h"\r
-#endif\r
-\r
-#include <stdlib.h>\r
-#include <inttypes.h>\r
-#include <stdio.h>\r
-#include <string.h>\r
-#include <time.h>\r
-#include <stdlib.h>\r
-#include <assert.h>\r
-\r
-#include "spandsp/telephony.h"\r
-#include "spandsp/timezone.h"\r
-\r
-#include "spandsp/private/timezone.h"\r
-\r
-#if !defined(FALSE)\r
-#define FALSE    0\r
-#endif\r
-\r
-#if !defined(TRUE)\r
-#define TRUE    (!FALSE)\r
-#endif\r
-\r
-#define SECS_PER_MIN            60\r
-#define MINS_PER_HOUR           60\r
-#define HOURS_PER_DAY           24\r
-#define DAYS_PER_WEEK           7\r
-#define DAYS_PER_NON_LEAP_YEAR  365\r
-#define DAYS_PER_LEAP_YEAR      366\r
-#define SECS_PER_HOUR           (SECS_PER_MIN*MINS_PER_HOUR)\r
-#define SECS_PER_DAY            ((long int) SECS_PER_HOUR*HOURS_PER_DAY)\r
-#define MONTHS_PER_YEAR         12\r
-\r
-#define TM_YEAR_BASE            1900\r
-\r
-#define EPOCH_YEAR              1970\r
-#define EPOCH_WDAY              TM_THURSDAY\r
-\r
-#define isleap(y)               (((y)%4) == 0  &&  (((y)%100) != 0  ||  ((y)%400) == 0))\r
-\r
-#define isleap_sum(a, b)        isleap((a)%400 + (b)%400)\r
-\r
-/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */\r
-#define is_digit(c)             ((unsigned int) (c) - '0' <= 9)\r
-\r
-#define TZ_DEF_RULE_STRING      ",M4.1.0,M10.5.0"\r
-\r
-#define JULIAN_DAY              0       /* Jn - Julian day */\r
-#define DAY_OF_YEAR             1       /* n - day of year */\r
-#define MONTH_NTH_DAY_OF_WEEK   2       /* Mm.n.d - month, week, day of week */\r
-\r
-static const char wildabbr[] = "   ";\r
-\r
-static const char gmt[] = "GMT";\r
-\r
-struct tz_rule_s\r
-{\r
-    int r_type;                         /* Type of rule--see below */\r
-    int r_day;                          /* Day number of rule */\r
-    int r_week;                         /* Week number of rule */\r
-    int r_mon;                          /* Month number of rule */\r
-    long int r_time;                    /* Transition time of rule */\r
-};\r
-\r
-static const int mon_lengths[2][MONTHS_PER_YEAR] =\r
-{\r
-    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},\r
-    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}\r
-};\r
-\r
-static const int year_lengths[2] =\r
-{\r
-    DAYS_PER_NON_LEAP_YEAR,\r
-    DAYS_PER_LEAP_YEAR\r
-};\r
-\r
-static int increment_overflow(int *number, int delta)\r
-{\r
-    int number0;\r
-\r
-    number0 = *number;\r
-    *number += delta;\r
-    return (*number < number0) != (delta < 0);\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-static void set_tzname(tz_t *tz)\r
-{\r
-    struct tz_state_s *sp;\r
-    const struct tz_ttinfo_s *ttisp;\r
-    int i;\r
-\r
-    sp = &tz->state;\r
-    tz->tzname[0] = wildabbr;\r
-    tz->tzname[1] = wildabbr;\r
-    for (i = 0;  i < sp->typecnt;  i++)\r
-    {\r
-        ttisp = &sp->ttis[i];\r
-        tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];\r
-    }\r
-    for (i = 0;  i < sp->timecnt;  i++)\r
-    {\r
-        ttisp = &sp->ttis[sp->types[i]];\r
-        tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];\r
-    }\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-/* Return the number of leap years through the end of the given year\r
-   where, to make the math easy, the answer for year zero is defined as zero. */\r
-static int leaps_thru_end_of(const int y)\r
-{\r
-    return (y >= 0)  ?  (y/4 - y/100 + y/400)  :  -(leaps_thru_end_of(-(y + 1)) + 1);\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-static struct tm *time_sub(const time_t * const timep, const long int offset, const struct tz_state_s * const sp, struct tm * const tmp)\r
-{\r
-    const struct tz_lsinfo_s *lp;\r
-    time_t tdays;\r
-    const int *ip;\r
-    int32_t corr;\r
-    int32_t seconds;\r
-    int32_t rem;\r
-    int idays;\r
-    int y;\r
-    int hit;\r
-    int i;\r
-\r
-    corr = 0;\r
-    hit = 0;\r
-    i = sp->leapcnt;\r
-    while (--i >= 0)\r
-    {\r
-        lp = &sp->lsis[i];\r
-        if (*timep >= lp->trans)\r
-        {\r
-            if (*timep == lp->trans)\r
-            {\r
-                hit = ((i == 0  &&  lp->corr > 0)  ||  lp->corr > sp->lsis[i - 1].corr);\r
-                if (hit)\r
-                {\r
-                    while (i > 0\r
-                           &&\r
-                           sp->lsis[i].trans == sp->lsis[i - 1].trans + 1\r
-                           &&\r
-                           sp->lsis[i].corr == sp->lsis[i - 1].corr + 1)\r
-                    {\r
-                        hit++;\r
-                        --i;\r
-                    }\r
-                }\r
-            }\r
-            corr = lp->corr;\r
-            break;\r
-        }\r
-    }\r
-    y = EPOCH_YEAR;\r
-    tdays = *timep/SECS_PER_DAY;\r
-    rem = *timep - tdays*SECS_PER_DAY;\r
-    while (tdays < 0  ||  tdays >= year_lengths[isleap(y)])\r
-    {\r
-        int newy;\r
-        time_t tdelta;\r
-        int idelta;\r
-        int leapdays;\r
-\r
-        tdelta = tdays / DAYS_PER_LEAP_YEAR;\r
-        idelta = tdelta;\r
-        if (tdelta - idelta >= 1  ||  idelta - tdelta >= 1)\r
-            return NULL;\r
-        if (idelta == 0)\r
-            idelta = (tdays < 0)  ?  -1  :  1;\r
-        newy = y;\r
-        if (increment_overflow(&newy, idelta))\r
-            return NULL;\r
-        leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1);\r
-        tdays -= ((time_t) newy - y)*DAYS_PER_NON_LEAP_YEAR;\r
-        tdays -= leapdays;\r
-        y = newy;\r
-    }\r
-    seconds = tdays*SECS_PER_DAY;\r
-    tdays = seconds/SECS_PER_DAY;\r
-    rem += seconds - tdays*SECS_PER_DAY;\r
-    /* Given the range, we can now fearlessly cast... */\r
-    idays = tdays;\r
-    rem += (offset - corr);\r
-    while (rem < 0)\r
-    {\r
-        rem += SECS_PER_DAY;\r
-        idays--;\r
-    }\r
-    while (rem >= SECS_PER_DAY)\r
-    {\r
-        rem -= SECS_PER_DAY;\r
-        idays++;\r
-    }\r
-    while (idays < 0)\r
-    {\r
-        if (increment_overflow(&y, -1))\r
-            return NULL;\r
-        idays += year_lengths[isleap(y)];\r
-    }\r
-    while (idays >= year_lengths[isleap(y)])\r
-    {\r
-        idays -= year_lengths[isleap(y)];\r
-        if (increment_overflow(&y, 1))\r
-            return NULL;\r
-    }\r
-    tmp->tm_year = y;\r
-    if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))\r
-        return NULL;\r
-    tmp->tm_yday = idays;\r
-    /* The "extra" mods below avoid overflow problems. */\r
-    tmp->tm_wday = EPOCH_WDAY\r
-                 + ((y - EPOCH_YEAR) % DAYS_PER_WEEK)*(DAYS_PER_NON_LEAP_YEAR % DAYS_PER_WEEK)\r
-                 + leaps_thru_end_of(y - 1)\r
-                 - leaps_thru_end_of(EPOCH_YEAR - 1)\r
-                 + idays;\r
-    tmp->tm_wday %= DAYS_PER_WEEK;\r
-    if (tmp->tm_wday < 0)\r
-        tmp->tm_wday += DAYS_PER_WEEK;\r
-    tmp->tm_hour = (int) (rem/SECS_PER_HOUR);\r
-    rem %= SECS_PER_HOUR;\r
-    tmp->tm_min = (int) (rem/SECS_PER_MIN);\r
-    /* A positive leap second requires a special\r
-     * representation. This uses "... ??:59:60" et seq. */\r
-    tmp->tm_sec = (int) (rem%SECS_PER_MIN) + hit;\r
-    ip = mon_lengths[isleap(y)];\r
-    for (tmp->tm_mon = 0;  idays >= ip[tmp->tm_mon];  (tmp->tm_mon)++)\r
-        idays -= ip[tmp->tm_mon];\r
-    tmp->tm_mday = (int) (idays + 1);\r
-    tmp->tm_isdst = 0;\r
-    return tmp;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-/* Given a pointer into a time zone string, scan until a character that is not\r
- * a valid character in a zone name is found. Return a pointer to that\r
- * character. */\r
-static const char *get_tzname(const char *strp)\r
-{\r
-    char c;\r
-\r
-    while ((c = *strp) != '\0'  &&  !is_digit(c)  &&  c != ','  &&  c != '-'  &&  c != '+')\r
-        strp++;\r
-    return strp;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-/* Given a pointer into a time zone string, extract a number from that string.\r
- * Check that the number is within a specified range; if it is not, return\r
- * NULL.\r
- * Otherwise, return a pointer to the first character not part of the number. */\r
-static const char *get_num(const char *strp, int * const nump, const int min, const int max)\r
-{\r
-    char c;\r
-    int num;\r
-\r
-    if (strp == NULL  ||  !is_digit(c = *strp))\r
-        return NULL;\r
-    num = 0;\r
-    do\r
-    {\r
-        num = num*10 + (c - '0');\r
-        if (num > max)\r
-            return NULL;    /* Illegal value */\r
-        c = *++strp;\r
-    }\r
-    while (is_digit(c));\r
-    if (num < min)\r
-        return NULL;        /* Illegal value */\r
-    *nump = num;\r
-    return strp;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-/* Given a pointer into a time zone string, extract a number of seconds,\r
- * in hh[:mm[:ss]] form, from the string.\r
- * If any error occurs, return NULL.\r
- * Otherwise, return a pointer to the first character not part of the number\r
- * of seconds. */\r
-static const char *get_secs(const char *strp, long int * const secsp)\r
-{\r
-    int num;\r
-\r
-    /* HOURS_PER_DAY*DAYS_PER_WEEK - 1 allows quasi-Posix rules like\r
-     * "M10.4.6/26", which does not conform to Posix,\r
-     * but which specifies the equivalent of\r
-     * "02:00 on the first Sunday on or after 23 Oct". */\r
-    strp = get_num(strp, &num, 0, HOURS_PER_DAY*DAYS_PER_WEEK - 1);\r
-    if (strp == NULL)\r
-        return NULL;\r
-    *secsp = num*(long int) SECS_PER_HOUR;\r
-    if (*strp == ':')\r
-    {\r
-        strp = get_num(strp + 1, &num, 0, MINS_PER_HOUR - 1);\r
-        if (strp == NULL)\r
-            return NULL;\r
-        *secsp += num*SECS_PER_MIN;\r
-        if (*strp == ':')\r
-        {\r
-            /* SECS_PER_MIN allows for leap seconds. */\r
-            strp = get_num(strp + 1, &num, 0, SECS_PER_MIN);\r
-            if (strp == NULL)\r
-                return NULL;\r
-            *secsp += num;\r
-        }\r
-    }\r
-    return strp;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-/* Given a pointer into a time zone string, extract an offset, in\r
- * [+-]hh[:mm[:ss]] form, from the string.\r
- * If any error occurs, return NULL.\r
- * Otherwise, return a pointer to the first character not part of the time. */\r
-static const char *get_offset(const char *strp, long int * const offsetp)\r
-{\r
-    int neg = 0;\r
-\r
-    if (*strp == '-')\r
-    {\r
-        neg = 1;\r
-        strp++;\r
-    }\r
-    else if (*strp == '+')\r
-    {\r
-        strp++;\r
-    }\r
-    strp = get_secs(strp, offsetp);\r
-    if (strp == NULL)\r
-        return NULL;        /* Illegal time */\r
-    if (neg)\r
-        *offsetp = -*offsetp;\r
-    return strp;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-/* Given a pointer into a time zone string, extract a rule in the form\r
- * date[/time]. See POSIX section 8 for the format of "date" and "time".\r
- * If a valid rule is not found, return NULL.\r
- * Otherwise, return a pointer to the first character not part of the rule. */\r
-static const char *get_rule(const char *strp, struct tz_rule_s * const rulep)\r
-{\r
-    if (*strp == 'J')\r
-    {\r
-        /* Julian day. */\r
-        rulep->r_type = JULIAN_DAY;\r
-        strp = get_num(strp + 1, &rulep->r_day, 1, DAYS_PER_NON_LEAP_YEAR);\r
-    }\r
-    else if (*strp == 'M')\r
-    {\r
-        /* Month, week, day. */\r
-        rulep->r_type = MONTH_NTH_DAY_OF_WEEK;\r
-        strp = get_num(strp + 1, &rulep->r_mon, 1, MONTHS_PER_YEAR);\r
-        if (strp == NULL  ||  *strp++ != '.')\r
-            return NULL;\r
-        strp = get_num(strp, &rulep->r_week, 1, 5);\r
-        if (strp == NULL  ||  *strp++ != '.')\r
-            return NULL;\r
-        strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_WEEK - 1);\r
-    }\r
-    else if (is_digit(*strp))\r
-    {\r
-        /* Day of the year. */\r
-        rulep->r_type = DAY_OF_YEAR;\r
-        strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_LEAP_YEAR - 1);\r
-    }\r
-    else\r
-    {\r
-        /* Invalid format */\r
-        return NULL;\r
-    }\r
-    if (strp == NULL)\r
-        return NULL;\r
-    if (*strp == '/')\r
-    {\r
-        /* Time specified. */\r
-        strp = get_secs(strp + 1, &rulep->r_time);\r
-    }\r
-    else\r
-    {\r
-        /* Default = 2:00:00 */\r
-        rulep->r_time = 2*SECS_PER_HOUR;\r
-    }\r
-    return strp;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-/* Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the\r
- * year, a rule, and the offset from UTC at the time that rule takes effect,\r
- * calculate the Epoch-relative time that rule takes effect. */\r
-static time_t trans_time(const time_t janfirst, const int year, const struct tz_rule_s * const rulep, const long int offset)\r
-{\r
-    int leapyear;\r
-    time_t value;\r
-    int i;\r
-    int d;\r
-    int m1;\r
-    int yy0;\r
-    int yy1;\r
-    int yy2;\r
-    int dow;\r
-\r
-    value = 0;\r
-    leapyear = isleap(year);\r
-    switch (rulep->r_type)\r
-    {\r
-    case JULIAN_DAY:\r
-        /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap\r
-         * years.\r
-         * In non-leap years, or if the day number is 59 or less, just\r
-         * add SECS_PER_DAY times the day number-1 to the time of\r
-         * January 1, midnight, to get the day. */\r
-        value = janfirst + (rulep->r_day - 1)*SECS_PER_DAY;\r
-        if (leapyear  &&  rulep->r_day >= 60)\r
-            value += SECS_PER_DAY;\r
-        break;\r
-    case DAY_OF_YEAR:\r
-        /* n - day of year.\r
-         * Just add SECS_PER_DAY times the day number to the time of\r
-         * January 1, midnight, to get the day. */\r
-        value = janfirst + rulep->r_day * SECS_PER_DAY;\r
-        break;\r
-    case MONTH_NTH_DAY_OF_WEEK:\r
-        /* Mm.n.d - nth "dth day" of month m. */\r
-        value = janfirst;\r
-        for (i = 0;  i < rulep->r_mon - 1;  i++)\r
-            value += mon_lengths[leapyear][i]*SECS_PER_DAY;\r
-\r
-        /* Use Zeller's Congruence to get day-of-week of first day of month. */\r
-        m1 = (rulep->r_mon + 9)%12 + 1;\r
-        yy0 = (rulep->r_mon <= 2)  ?  (year - 1)  :  year;\r
-        yy1 = yy0/100;\r
-        yy2 = yy0%100;\r
-        dow = ((26*m1 - 2)/10 + 1 + yy2 + yy2/4 + yy1/4 - 2*yy1)%7;\r
-        if (dow < 0)\r
-            dow += DAYS_PER_WEEK;\r
-\r
-        /* "dow" is the day-of-week of the first day of the month. Get\r
-         * the day-of-month (zero-origin) of the first "dow" day of the\r
-         * month. */\r
-        d = rulep->r_day - dow;\r
-        if (d < 0)\r
-            d += DAYS_PER_WEEK;\r
-        for (i = 1;  i < rulep->r_week;  i++)\r
-        {\r
-            if (d + DAYS_PER_WEEK >= mon_lengths[leapyear][rulep->r_mon - 1])\r
-                break;\r
-            d += DAYS_PER_WEEK;\r
-        }\r
-\r
-        /* "d" is the day-of-month (zero-origin) of the day we want. */\r
-        value += d*SECS_PER_DAY;\r
-        break;\r
-    }\r
-\r
-    /* "value" is the Epoch-relative time of 00:00:00 UTC on the day in\r
-     * question. To get the Epoch-relative time of the specified local\r
-     * time on that day, add the transition time and the current offset\r
-     * from UTC. */\r
-    return value + rulep->r_time + offset;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-/* Given a POSIX section 8-style TZ string, fill in the rule tables as\r
-   appropriate. */\r
-static int tzparse(const char *name, struct tz_state_s * const sp, const int lastditch)\r
-{\r
-    const char *stdname;\r
-    const char *dstname;\r
-    size_t stdlen;\r
-    size_t dstlen;\r
-    long int stdoffset;\r
-    long int dstoffset;\r
-    long int theirstdoffset;\r
-    long int theirdstoffset;\r
-    long int theiroffset;\r
-    unsigned char *typep;\r
-    char *cp;\r
-    int load_result;\r
-    int isdst;\r
-    int i;\r
-    int j;\r
-    int year;\r
-    struct tz_rule_s start;\r
-    struct tz_rule_s end;\r
-    time_t *atp;\r
-    time_t janfirst;\r
-    time_t starttime;\r
-    time_t endtime;\r
-\r
-    dstname = NULL;\r
-    stdname = name;\r
-    if (lastditch)\r
-    {\r
-        stdlen = strlen(name);      /* Length of standard zone name */\r
-        name += stdlen;\r
-        if (stdlen >= sizeof(sp->chars))\r
-            stdlen = sizeof(sp->chars) - 1;\r
-        stdoffset = 0;\r
-    }\r
-    else\r
-    {\r
-        name = get_tzname(name);\r
-        stdlen = name - stdname;\r
-        if (stdlen < 3)\r
-            return -1;\r
-        if (*name == '\0')\r
-            return -1;\r
-        name = get_offset(name, &stdoffset);\r
-        if (name == NULL)\r
-            return -1;\r
-    }\r
-    load_result = -1;\r
-    if (load_result != 0)\r
-        sp->leapcnt = 0;            /* So, we're off a little */\r
-    if (*name != '\0')\r
-    {\r
-        dstname = name;\r
-        name = get_tzname(name);\r
-        dstlen = name - dstname;    /* Length of DST zone name */\r
-        if (dstlen < 3)\r
-            return -1;\r
-        if (*name != '\0'  &&  *name != ','  &&  *name != ';')\r
-        {\r
-            if ((name = get_offset(name, &dstoffset)) == NULL)\r
-                return -1;\r
-        }\r
-        else\r
-        {\r
-            dstoffset = stdoffset - SECS_PER_HOUR;\r
-        }\r
-        if (*name == '\0'  &&  load_result != 0)\r
-            name = TZ_DEF_RULE_STRING;\r
-        if (*name == ','  ||  *name == ';')\r
-        {\r
-            if ((name = get_rule(name + 1, &start)) == NULL)\r
-                return -1;\r
-            if (*name++ != ',')\r
-                return -1;\r
-            if ((name = get_rule(name, &end)) == NULL)\r
-                return -1;\r
-            if (*name != '\0')\r
-                return -1;\r
-            sp->typecnt = 2;        /* Standard time and DST */\r
-            /* Two transitions per year, from EPOCH_YEAR to 2037. */\r
-            sp->timecnt = 2*(2037 - EPOCH_YEAR + 1);\r
-            if (sp->timecnt > TZ_MAX_TIMES)\r
-                return -1;\r
-            sp->ttis[0].gmtoff = -dstoffset;\r
-            sp->ttis[0].isdst = 1;\r
-            sp->ttis[0].abbrind = stdlen + 1;\r
-            sp->ttis[1].gmtoff = -stdoffset;\r
-            sp->ttis[1].isdst = 0;\r
-            sp->ttis[1].abbrind = 0;\r
-            atp = sp->ats;\r
-            typep = sp->types;\r
-            janfirst = 0;\r
-            for (year = EPOCH_YEAR;  year <= 2037;  year++)\r
-            {\r
-                starttime = trans_time(janfirst, year, &start, stdoffset);\r
-                endtime = trans_time(janfirst, year, &end, dstoffset);\r
-                if (starttime > endtime)\r
-                {\r
-                    *atp++ = endtime;\r
-                    *typep++ = 1;    /* DST ends */\r
-                    *atp++ = starttime;\r
-                    *typep++ = 0;    /* DST begins */\r
-                }\r
-                else\r
-                {\r
-                    *atp++ = starttime;\r
-                    *typep++ = 0;    /* DST begins */\r
-                    *atp++ = endtime;\r
-                    *typep++ = 1;    /* DST ends */\r
-                }\r
-                janfirst += year_lengths[isleap(year)]*SECS_PER_DAY;\r
-            }\r
-        }\r
-        else\r
-        {\r
-            if (*name != '\0')\r
-                return -1;\r
-            /* Initial values of theirstdoffset and theirdstoffset. */\r
-            theirstdoffset = 0;\r
-            for (i = 0;  i < sp->timecnt;  i++)\r
-            {\r
-                j = sp->types[i];\r
-                if (!sp->ttis[j].isdst)\r
-                {\r
-                    theirstdoffset = -sp->ttis[j].gmtoff;\r
-                    break;\r
-                }\r
-            }\r
-            theirdstoffset = 0;\r
-            for (i = 0;  i < sp->timecnt;  i++)\r
-            {\r
-                j = sp->types[i];\r
-                if (sp->ttis[j].isdst)\r
-                {\r
-                    theirdstoffset = -sp->ttis[j].gmtoff;\r
-                    break;\r
-                }\r
-            }\r
-            /* Initially we're assumed to be in standard time. */\r
-            isdst = FALSE;\r
-            theiroffset = theirstdoffset;\r
-            /* Now juggle transition times and types tracking offsets as you do. */\r
-            for (i = 0;  i < sp->timecnt;  i++)\r
-            {\r
-                j = sp->types[i];\r
-                sp->types[i] = sp->ttis[j].isdst;\r
-                if (sp->ttis[j].ttisgmt)\r
-                {\r
-                    /* No adjustment to transition time */\r
-                }\r
-                else\r
-                {\r
-                    /* If summer time is in effect, and the\r
-                     * transition time was not specified as\r
-                     * standard time, add the summer time\r
-                     * offset to the transition time;\r
-                     * otherwise, add the standard time\r
-                     * offset to the transition time. */\r
-                    /* Transitions from DST to DDST\r
-                     * will effectively disappear since\r
-                     * POSIX provides for only one DST\r
-                     * offset. */\r
-                    if (isdst  &&  !sp->ttis[j].ttisstd)\r
-                        sp->ats[i] += (dstoffset - theirdstoffset);\r
-                    else\r
-                        sp->ats[i] += (stdoffset - theirstdoffset);\r
-                }\r
-                theiroffset = -sp->ttis[j].gmtoff;\r
-                if (sp->ttis[j].isdst)\r
-                    theirdstoffset = theiroffset;\r
-                else\r
-                    theirstdoffset = theiroffset;\r
-            }\r
-            /* Finally, fill in ttis. ttisstd and ttisgmt need not be handled. */\r
-            sp->ttis[0].gmtoff = -stdoffset;\r
-            sp->ttis[0].isdst = FALSE;\r
-            sp->ttis[0].abbrind = 0;\r
-            sp->ttis[1].gmtoff = -dstoffset;\r
-            sp->ttis[1].isdst = TRUE;\r
-            sp->ttis[1].abbrind = stdlen + 1;\r
-            sp->typecnt = 2;\r
-        }\r
-    }\r
-    else\r
-    {\r
-        dstlen = 0;\r
-        sp->typecnt = 1;        /* Only standard time */\r
-        sp->timecnt = 0;\r
-        sp->ttis[0].gmtoff = -stdoffset;\r
-        sp->ttis[0].isdst = 0;\r
-        sp->ttis[0].abbrind = 0;\r
-    }\r
-    sp->charcnt = stdlen + 1;\r
-    if (dstlen != 0)\r
-        sp->charcnt += dstlen + 1;\r
-    if ((size_t) sp->charcnt > sizeof(sp->chars))\r
-        return -1;\r
-    cp = sp->chars;\r
-    strncpy(cp, stdname, stdlen);\r
-    cp += stdlen;\r
-    *cp++ = '\0';\r
-    if (dstlen != 0)\r
-    {\r
-        strncpy(cp, dstname, dstlen);\r
-        cp[dstlen] = '\0';\r
-    }\r
-    return 0;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-static void tz_set(tz_t *tz, const char *tzstring)\r
-{\r
-    const char *name = "";\r
-    struct tz_state_s *lclptr = &tz->state;\r
-\r
-    if (tzstring)\r
-        name = tzstring;\r
-\r
-    /* See if we are already set OK */\r
-    if (tz->lcl_is_set > 0  &&  strcmp(tz->lcl_tzname, name) == 0)\r
-        return;\r
-    tz->lcl_is_set = strlen(name) < sizeof(tz->lcl_tzname);\r
-    if (tz->lcl_is_set)\r
-        strcpy(tz->lcl_tzname, name);\r
-\r
-    if (name[0] == '\0')\r
-    {\r
-        /* User wants it fast rather than right, so, we're off a little. */\r
-        lclptr->leapcnt = 0;\r
-        lclptr->timecnt = 0;\r
-        lclptr->typecnt = 0;\r
-        lclptr->ttis[0].isdst = 0;\r
-        lclptr->ttis[0].gmtoff = 0;\r
-        lclptr->ttis[0].abbrind = 0;\r
-        strcpy(lclptr->chars, gmt);\r
-    }\r
-    else if (name[0] == ':'  ||  tzparse(name, lclptr, FALSE) != 0)\r
-    {\r
-        tzparse(gmt, lclptr, TRUE);\r
-    }\r
-    set_tzname(tz);\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-SPAN_DECLARE(int) tz_localtime(tz_t *tz, struct tm *tmp, time_t t)\r
-{\r
-    struct tz_state_s *sp;\r
-    const struct tz_ttinfo_s *ttisp;\r
-    int i;\r
-\r
-    sp = &tz->state;\r
-\r
-    if (sp->timecnt == 0  ||  t < sp->ats[0])\r
-    {\r
-        i = 0;\r
-        while (sp->ttis[i].isdst)\r
-        {\r
-            if (++i >= sp->typecnt)\r
-            {\r
-                i = 0;\r
-                break;\r
-            }\r
-        }\r
-    }\r
-    else\r
-    {\r
-        for (i = 1;  i < sp->timecnt;  i++)\r
-        {\r
-            if (t < sp->ats[i])\r
-                break;\r
-        }\r
-        i = (int) sp->types[i - 1];\r
-    }\r
-    ttisp = &sp->ttis[i];\r
-    time_sub(&t, ttisp->gmtoff, sp, tmp);\r
-    tmp->tm_isdst = ttisp->isdst;\r
-    tz->tzname[tmp->tm_isdst] = &sp->chars[ttisp->abbrind];\r
-    return 0;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-SPAN_DECLARE(const char *) tz_tzname(tz_t *tz, int isdst)\r
-{\r
-    return tz->tzname[(!isdst)  ?  0  :  1];\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-SPAN_DECLARE(tz_t *) tz_init(tz_t *tz, const char *tzstring)\r
-{\r
-    if (tz == NULL)\r
-    {\r
-        if ((tz = (tz_t *) malloc(sizeof(*tz))) == NULL)\r
-            return NULL;\r
-    }\r
-    memset(tz, 0, sizeof(*tz));\r
-    tz->tzname[0] =\r
-    tz->tzname[1] = wildabbr;\r
-    tz_set(tz, tzstring);\r
-    return tz;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-SPAN_DECLARE(int) tz_release(tz_t *tz)\r
-{\r
-    return 0;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-\r
-SPAN_DECLARE(int) tz_free(tz_t *tz)\r
-{\r
-    if (tz)\r
-        free(tz);\r
-    return 0;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-/*- End of file ------------------------------------------------------------*/\r
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * timezone.c - Timezone handling for time interpretation
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2010 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+/* Timezone processing might not seem like a DSP activity, but getting the headers
+   right on FAXes demands it. We need to handle multiple time zones within a process,
+   for FAXes related to different parts of the globe, so the system timezone handling
+   is not adequate. */
+
+/* This timezone handling is derived from public domain software by Arthur David Olson
+   <arthur_david_olson@nih.gov> which you may download from ftp://elsie.nci.nih.gov/pub
+   at the time of writing. */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/timezone.h"
+
+#include "spandsp/private/timezone.h"
+
+#if !defined(FALSE)
+#define FALSE    0
+#endif
+
+#if !defined(TRUE)
+#define TRUE    (!FALSE)
+#endif
+
+#define SECS_PER_MIN            60
+#define MINS_PER_HOUR           60
+#define HOURS_PER_DAY           24
+#define DAYS_PER_WEEK           7
+#define DAYS_PER_NON_LEAP_YEAR  365
+#define DAYS_PER_LEAP_YEAR      366
+#define SECS_PER_HOUR           (SECS_PER_MIN*MINS_PER_HOUR)
+#define SECS_PER_DAY            ((long int) SECS_PER_HOUR*HOURS_PER_DAY)
+#define MONTHS_PER_YEAR         12
+
+#define TM_YEAR_BASE            1900
+
+#define EPOCH_YEAR              1970
+#define EPOCH_WDAY              TM_THURSDAY
+
+#define isleap(y)               (((y)%4) == 0  &&  (((y)%100) != 0  ||  ((y)%400) == 0))
+
+#define isleap_sum(a, b)        isleap((a)%400 + (b)%400)
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c)             ((unsigned int) (c) - '0' <= 9)
+
+#define TZ_DEF_RULE_STRING      ",M4.1.0,M10.5.0"
+
+#define JULIAN_DAY              0       /* Jn - Julian day */
+#define DAY_OF_YEAR             1       /* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK   2       /* Mm.n.d - month, week, day of week */
+
+static const char wildabbr[] = "   ";
+
+static const char gmt[] = "GMT";
+
+struct tz_rule_s
+{
+    int r_type;                         /* Type of rule--see below */
+    int r_day;                          /* Day number of rule */
+    int r_week;                         /* Week number of rule */
+    int r_mon;                          /* Month number of rule */
+    long int r_time;                    /* Transition time of rule */
+};
+
+static const int mon_lengths[2][MONTHS_PER_YEAR] =
+{
+    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+static const int year_lengths[2] =
+{
+    DAYS_PER_NON_LEAP_YEAR,
+    DAYS_PER_LEAP_YEAR
+};
+
+static int increment_overflow(int *number, int delta)
+{
+    int number0;
+
+    number0 = *number;
+    *number += delta;
+    return (*number < number0) != (delta < 0);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void set_tzname(tz_t *tz)
+{
+    struct tz_state_s *sp;
+    const struct tz_ttinfo_s *ttisp;
+    int i;
+
+    sp = &tz->state;
+    tz->tzname[0] = wildabbr;
+    tz->tzname[1] = wildabbr;
+    for (i = 0;  i < sp->typecnt;  i++)
+    {
+        ttisp = &sp->ttis[i];
+        tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];
+    }
+    for (i = 0;  i < sp->timecnt;  i++)
+    {
+        ttisp = &sp->ttis[sp->types[i]];
+        tz->tzname[ttisp->isdst] = &sp->chars[ttisp->abbrind];
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Return the number of leap years through the end of the given year
+   where, to make the math easy, the answer for year zero is defined as zero. */
+static int leaps_thru_end_of(const int y)
+{
+    return (y >= 0)  ?  (y/4 - y/100 + y/400)  :  -(leaps_thru_end_of(-(y + 1)) + 1);
+}
+/*- End of function --------------------------------------------------------*/
+
+static struct tm *time_sub(const time_t * const timep, const long int offset, const struct tz_state_s * const sp, struct tm * const tmp)
+{
+    const struct tz_lsinfo_s *lp;
+    time_t tdays;
+    const int *ip;
+    int32_t corr;
+    int32_t seconds;
+    int32_t rem;
+    int idays;
+    int y;
+    int hit;
+    int i;
+
+    corr = 0;
+    hit = 0;
+    i = sp->leapcnt;
+    while (--i >= 0)
+    {
+        lp = &sp->lsis[i];
+        if (*timep >= lp->trans)
+        {
+            if (*timep == lp->trans)
+            {
+                hit = ((i == 0  &&  lp->corr > 0)  ||  lp->corr > sp->lsis[i - 1].corr);
+                if (hit)
+                {
+                    while (i > 0
+                           &&
+                           sp->lsis[i].trans == sp->lsis[i - 1].trans + 1
+                           &&
+                           sp->lsis[i].corr == sp->lsis[i - 1].corr + 1)
+                    {
+                        hit++;
+                        --i;
+                    }
+                }
+            }
+            corr = lp->corr;
+            break;
+        }
+    }
+    y = EPOCH_YEAR;
+    tdays = *timep/SECS_PER_DAY;
+    rem = *timep - tdays*SECS_PER_DAY;
+    while (tdays < 0  ||  tdays >= year_lengths[isleap(y)])
+    {
+        int newy;
+        time_t tdelta;
+        int idelta;
+        int leapdays;
+
+        tdelta = tdays / DAYS_PER_LEAP_YEAR;
+        idelta = tdelta;
+        if (tdelta - idelta >= 1  ||  idelta - tdelta >= 1)
+            return NULL;
+        if (idelta == 0)
+            idelta = (tdays < 0)  ?  -1  :  1;
+        newy = y;
+        if (increment_overflow(&newy, idelta))
+            return NULL;
+        leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1);
+        tdays -= ((time_t) newy - y)*DAYS_PER_NON_LEAP_YEAR;
+        tdays -= leapdays;
+        y = newy;
+    }
+    seconds = tdays*SECS_PER_DAY;
+    tdays = seconds/SECS_PER_DAY;
+    rem += seconds - tdays*SECS_PER_DAY;
+    /* Given the range, we can now fearlessly cast... */
+    idays = tdays;
+    rem += (offset - corr);
+    while (rem < 0)
+    {
+        rem += SECS_PER_DAY;
+        idays--;
+    }
+    while (rem >= SECS_PER_DAY)
+    {
+        rem -= SECS_PER_DAY;
+        idays++;
+    }
+    while (idays < 0)
+    {
+        if (increment_overflow(&y, -1))
+            return NULL;
+        idays += year_lengths[isleap(y)];
+    }
+    while (idays >= year_lengths[isleap(y)])
+    {
+        idays -= year_lengths[isleap(y)];
+        if (increment_overflow(&y, 1))
+            return NULL;
+    }
+    tmp->tm_year = y;
+    if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+        return NULL;
+    tmp->tm_yday = idays;
+    /* The "extra" mods below avoid overflow problems. */
+    tmp->tm_wday = EPOCH_WDAY
+                 + ((y - EPOCH_YEAR) % DAYS_PER_WEEK)*(DAYS_PER_NON_LEAP_YEAR % DAYS_PER_WEEK)
+                 + leaps_thru_end_of(y - 1)
+                 - leaps_thru_end_of(EPOCH_YEAR - 1)
+                 + idays;
+    tmp->tm_wday %= DAYS_PER_WEEK;
+    if (tmp->tm_wday < 0)
+        tmp->tm_wday += DAYS_PER_WEEK;
+    tmp->tm_hour = (int) (rem/SECS_PER_HOUR);
+    rem %= SECS_PER_HOUR;
+    tmp->tm_min = (int) (rem/SECS_PER_MIN);
+    /* A positive leap second requires a special
+     * representation. This uses "... ??:59:60" et seq. */
+    tmp->tm_sec = (int) (rem%SECS_PER_MIN) + hit;
+    ip = mon_lengths[isleap(y)];
+    for (tmp->tm_mon = 0;  idays >= ip[tmp->tm_mon];  (tmp->tm_mon)++)
+        idays -= ip[tmp->tm_mon];
+    tmp->tm_mday = (int) (idays + 1);
+    tmp->tm_isdst = 0;
+    return tmp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, scan until a character that is not
+ * a valid character in a zone name is found. Return a pointer to that
+ * character. */
+static const char *get_tzname(const char *strp)
+{
+    char c;
+
+    while ((c = *strp) != '\0'  &&  !is_digit(c)  &&  c != ','  &&  c != '-'  &&  c != '+')
+        strp++;
+    return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, extract a number from that string.
+ * Check that the number is within a specified range; if it is not, return
+ * NULL.
+ * Otherwise, return a pointer to the first character not part of the number. */
+static const char *get_num(const char *strp, int * const nump, const int min, const int max)
+{
+    char c;
+    int num;
+
+    if (strp == NULL  ||  !is_digit(c = *strp))
+        return NULL;
+    num = 0;
+    do
+    {
+        num = num*10 + (c - '0');
+        if (num > max)
+            return NULL;    /* Illegal value */
+        c = *++strp;
+    }
+    while (is_digit(c));
+    if (num < min)
+        return NULL;        /* Illegal value */
+    *nump = num;
+    return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, extract a number of seconds,
+ * in hh[:mm[:ss]] form, from the string.
+ * If any error occurs, return NULL.
+ * Otherwise, return a pointer to the first character not part of the number
+ * of seconds. */
+static const char *get_secs(const char *strp, long int * const secsp)
+{
+    int num;
+
+    /* HOURS_PER_DAY*DAYS_PER_WEEK - 1 allows quasi-Posix rules like
+     * "M10.4.6/26", which does not conform to Posix,
+     * but which specifies the equivalent of
+     * "02:00 on the first Sunday on or after 23 Oct". */
+    strp = get_num(strp, &num, 0, HOURS_PER_DAY*DAYS_PER_WEEK - 1);
+    if (strp == NULL)
+        return NULL;
+    *secsp = num*(long int) SECS_PER_HOUR;
+    if (*strp == ':')
+    {
+        strp = get_num(strp + 1, &num, 0, MINS_PER_HOUR - 1);
+        if (strp == NULL)
+            return NULL;
+        *secsp += num*SECS_PER_MIN;
+        if (*strp == ':')
+        {
+            /* SECS_PER_MIN allows for leap seconds. */
+            strp = get_num(strp + 1, &num, 0, SECS_PER_MIN);
+            if (strp == NULL)
+                return NULL;
+            *secsp += num;
+        }
+    }
+    return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, extract an offset, in
+ * [+-]hh[:mm[:ss]] form, from the string.
+ * If any error occurs, return NULL.
+ * Otherwise, return a pointer to the first character not part of the time. */
+static const char *get_offset(const char *strp, long int * const offsetp)
+{
+    int neg = 0;
+
+    if (*strp == '-')
+    {
+        neg = 1;
+        strp++;
+    }
+    else if (*strp == '+')
+    {
+        strp++;
+    }
+    strp = get_secs(strp, offsetp);
+    if (strp == NULL)
+        return NULL;        /* Illegal time */
+    if (neg)
+        *offsetp = -*offsetp;
+    return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a pointer into a time zone string, extract a rule in the form
+ * date[/time]. See POSIX section 8 for the format of "date" and "time".
+ * If a valid rule is not found, return NULL.
+ * Otherwise, return a pointer to the first character not part of the rule. */
+static const char *get_rule(const char *strp, struct tz_rule_s * const rulep)
+{
+    if (*strp == 'J')
+    {
+        /* Julian day. */
+        rulep->r_type = JULIAN_DAY;
+        strp = get_num(strp + 1, &rulep->r_day, 1, DAYS_PER_NON_LEAP_YEAR);
+    }
+    else if (*strp == 'M')
+    {
+        /* Month, week, day. */
+        rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+        strp = get_num(strp + 1, &rulep->r_mon, 1, MONTHS_PER_YEAR);
+        if (strp == NULL  ||  *strp++ != '.')
+            return NULL;
+        strp = get_num(strp, &rulep->r_week, 1, 5);
+        if (strp == NULL  ||  *strp++ != '.')
+            return NULL;
+        strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_WEEK - 1);
+    }
+    else if (is_digit(*strp))
+    {
+        /* Day of the year. */
+        rulep->r_type = DAY_OF_YEAR;
+        strp = get_num(strp, &rulep->r_day, 0, DAYS_PER_LEAP_YEAR - 1);
+    }
+    else
+    {
+        /* Invalid format */
+        return NULL;
+    }
+    if (strp == NULL)
+        return NULL;
+    if (*strp == '/')
+    {
+        /* Time specified. */
+        strp = get_secs(strp + 1, &rulep->r_time);
+    }
+    else
+    {
+        /* Default = 2:00:00 */
+        rulep->r_time = 2*SECS_PER_HOUR;
+    }
+    return strp;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
+ * year, a rule, and the offset from UTC at the time that rule takes effect,
+ * calculate the Epoch-relative time that rule takes effect. */
+static time_t trans_time(const time_t janfirst, const int year, const struct tz_rule_s * const rulep, const long int offset)
+{
+    int leapyear;
+    time_t value;
+    int i;
+    int d;
+    int m1;
+    int yy0;
+    int yy1;
+    int yy2;
+    int dow;
+
+    value = 0;
+    leapyear = isleap(year);
+    switch (rulep->r_type)
+    {
+    case JULIAN_DAY:
+        /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+         * years.
+         * In non-leap years, or if the day number is 59 or less, just
+         * add SECS_PER_DAY times the day number-1 to the time of
+         * January 1, midnight, to get the day. */
+        value = janfirst + (rulep->r_day - 1)*SECS_PER_DAY;
+        if (leapyear  &&  rulep->r_day >= 60)
+            value += SECS_PER_DAY;
+        break;
+    case DAY_OF_YEAR:
+        /* n - day of year.
+         * Just add SECS_PER_DAY times the day number to the time of
+         * January 1, midnight, to get the day. */
+        value = janfirst + rulep->r_day * SECS_PER_DAY;
+        break;
+    case MONTH_NTH_DAY_OF_WEEK:
+        /* Mm.n.d - nth "dth day" of month m. */
+        value = janfirst;
+        for (i = 0;  i < rulep->r_mon - 1;  i++)
+            value += mon_lengths[leapyear][i]*SECS_PER_DAY;
+
+        /* Use Zeller's Congruence to get day-of-week of first day of month. */
+        m1 = (rulep->r_mon + 9)%12 + 1;
+        yy0 = (rulep->r_mon <= 2)  ?  (year - 1)  :  year;
+        yy1 = yy0/100;
+        yy2 = yy0%100;
+        dow = ((26*m1 - 2)/10 + 1 + yy2 + yy2/4 + yy1/4 - 2*yy1)%7;
+        if (dow < 0)
+            dow += DAYS_PER_WEEK;
+
+        /* "dow" is the day-of-week of the first day of the month. Get
+         * the day-of-month (zero-origin) of the first "dow" day of the
+         * month. */
+        d = rulep->r_day - dow;
+        if (d < 0)
+            d += DAYS_PER_WEEK;
+        for (i = 1;  i < rulep->r_week;  i++)
+        {
+            if (d + DAYS_PER_WEEK >= mon_lengths[leapyear][rulep->r_mon - 1])
+                break;
+            d += DAYS_PER_WEEK;
+        }
+
+        /* "d" is the day-of-month (zero-origin) of the day we want. */
+        value += d*SECS_PER_DAY;
+        break;
+    }
+
+    /* "value" is the Epoch-relative time of 00:00:00 UTC on the day in
+     * question. To get the Epoch-relative time of the specified local
+     * time on that day, add the transition time and the current offset
+     * from UTC. */
+    return value + rulep->r_time + offset;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* Given a POSIX section 8-style TZ string, fill in the rule tables as
+   appropriate. */
+static int tzparse(const char *name, struct tz_state_s * const sp, const int lastditch)
+{
+    const char *stdname;
+    const char *dstname;
+    size_t stdlen;
+    size_t dstlen;
+    long int stdoffset;
+    long int dstoffset;
+    long int theirstdoffset;
+    long int theirdstoffset;
+    long int theiroffset;
+    unsigned char *typep;
+    char *cp;
+    int load_result;
+    int isdst;
+    int i;
+    int j;
+    int year;
+    struct tz_rule_s start;
+    struct tz_rule_s end;
+    time_t *atp;
+    time_t janfirst;
+    time_t starttime;
+    time_t endtime;
+
+    dstname = NULL;
+    stdname = name;
+    if (lastditch)
+    {
+        stdlen = strlen(name);      /* Length of standard zone name */
+        name += stdlen;
+        if (stdlen >= sizeof(sp->chars))
+            stdlen = sizeof(sp->chars) - 1;
+        stdoffset = 0;
+    }
+    else
+    {
+        name = get_tzname(name);
+        stdlen = name - stdname;
+        if (stdlen < 3)
+            return -1;
+        if (*name == '\0')
+            return -1;
+        name = get_offset(name, &stdoffset);
+        if (name == NULL)
+            return -1;
+    }
+    load_result = -1;
+    if (load_result != 0)
+        sp->leapcnt = 0;            /* So, we're off a little */
+    if (*name != '\0')
+    {
+        dstname = name;
+        name = get_tzname(name);
+        dstlen = name - dstname;    /* Length of DST zone name */
+        if (dstlen < 3)
+            return -1;
+        if (*name != '\0'  &&  *name != ','  &&  *name != ';')
+        {
+            if ((name = get_offset(name, &dstoffset)) == NULL)
+                return -1;
+        }
+        else
+        {
+            dstoffset = stdoffset - SECS_PER_HOUR;
+        }
+        if (*name == '\0'  &&  load_result != 0)
+            name = TZ_DEF_RULE_STRING;
+        if (*name == ','  ||  *name == ';')
+        {
+            if ((name = get_rule(name + 1, &start)) == NULL)
+                return -1;
+            if (*name++ != ',')
+                return -1;
+            if ((name = get_rule(name, &end)) == NULL)
+                return -1;
+            if (*name != '\0')
+                return -1;
+            sp->typecnt = 2;        /* Standard time and DST */
+            /* Two transitions per year, from EPOCH_YEAR to 2037. */
+            sp->timecnt = 2*(2037 - EPOCH_YEAR + 1);
+            if (sp->timecnt > TZ_MAX_TIMES)
+                return -1;
+            sp->ttis[0].gmtoff = -dstoffset;
+            sp->ttis[0].isdst = 1;
+            sp->ttis[0].abbrind = stdlen + 1;
+            sp->ttis[1].gmtoff = -stdoffset;
+            sp->ttis[1].isdst = 0;
+            sp->ttis[1].abbrind = 0;
+            atp = sp->ats;
+            typep = sp->types;
+            janfirst = 0;
+            for (year = EPOCH_YEAR;  year <= 2037;  year++)
+            {
+                starttime = trans_time(janfirst, year, &start, stdoffset);
+                endtime = trans_time(janfirst, year, &end, dstoffset);
+                if (starttime > endtime)
+                {
+                    *atp++ = endtime;
+                    *typep++ = 1;    /* DST ends */
+                    *atp++ = starttime;
+                    *typep++ = 0;    /* DST begins */
+                }
+                else
+                {
+                    *atp++ = starttime;
+                    *typep++ = 0;    /* DST begins */
+                    *atp++ = endtime;
+                    *typep++ = 1;    /* DST ends */
+                }
+                janfirst += year_lengths[isleap(year)]*SECS_PER_DAY;
+            }
+        }
+        else
+        {
+            if (*name != '\0')
+                return -1;
+            /* Initial values of theirstdoffset and theirdstoffset. */
+            theirstdoffset = 0;
+            for (i = 0;  i < sp->timecnt;  i++)
+            {
+                j = sp->types[i];
+                if (!sp->ttis[j].isdst)
+                {
+                    theirstdoffset = -sp->ttis[j].gmtoff;
+                    break;
+                }
+            }
+            theirdstoffset = 0;
+            for (i = 0;  i < sp->timecnt;  i++)
+            {
+                j = sp->types[i];
+                if (sp->ttis[j].isdst)
+                {
+                    theirdstoffset = -sp->ttis[j].gmtoff;
+                    break;
+                }
+            }
+            /* Initially we're assumed to be in standard time. */
+            isdst = FALSE;
+            theiroffset = theirstdoffset;
+            /* Now juggle transition times and types tracking offsets as you do. */
+            for (i = 0;  i < sp->timecnt;  i++)
+            {
+                j = sp->types[i];
+                sp->types[i] = sp->ttis[j].isdst;
+                if (sp->ttis[j].ttisgmt)
+                {
+                    /* No adjustment to transition time */
+                }
+                else
+                {
+                    /* If summer time is in effect, and the
+                     * transition time was not specified as
+                     * standard time, add the summer time
+                     * offset to the transition time;
+                     * otherwise, add the standard time
+                     * offset to the transition time. */
+                    /* Transitions from DST to DDST
+                     * will effectively disappear since
+                     * POSIX provides for only one DST
+                     * offset. */
+                    if (isdst  &&  !sp->ttis[j].ttisstd)
+                        sp->ats[i] += (dstoffset - theirdstoffset);
+                    else
+                        sp->ats[i] += (stdoffset - theirstdoffset);
+                }
+                theiroffset = -sp->ttis[j].gmtoff;
+                if (sp->ttis[j].isdst)
+                    theirdstoffset = theiroffset;
+                else
+                    theirstdoffset = theiroffset;
+            }
+            /* Finally, fill in ttis. ttisstd and ttisgmt need not be handled. */
+            sp->ttis[0].gmtoff = -stdoffset;
+            sp->ttis[0].isdst = FALSE;
+            sp->ttis[0].abbrind = 0;
+            sp->ttis[1].gmtoff = -dstoffset;
+            sp->ttis[1].isdst = TRUE;
+            sp->ttis[1].abbrind = stdlen + 1;
+            sp->typecnt = 2;
+        }
+    }
+    else
+    {
+        dstlen = 0;
+        sp->typecnt = 1;        /* Only standard time */
+        sp->timecnt = 0;
+        sp->ttis[0].gmtoff = -stdoffset;
+        sp->ttis[0].isdst = 0;
+        sp->ttis[0].abbrind = 0;
+    }
+    sp->charcnt = stdlen + 1;
+    if (dstlen != 0)
+        sp->charcnt += dstlen + 1;
+    if ((size_t) sp->charcnt > sizeof(sp->chars))
+        return -1;
+    cp = sp->chars;
+    strncpy(cp, stdname, stdlen);
+    cp += stdlen;
+    *cp++ = '\0';
+    if (dstlen != 0)
+    {
+        strncpy(cp, dstname, dstlen);
+        cp[dstlen] = '\0';
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void tz_set(tz_t *tz, const char *tzstring)
+{
+    const char *name = "";
+    struct tz_state_s *lclptr = &tz->state;
+
+    if (tzstring)
+        name = tzstring;
+
+    /* See if we are already set OK */
+    if (tz->lcl_is_set > 0  &&  strcmp(tz->lcl_tzname, name) == 0)
+        return;
+    tz->lcl_is_set = strlen(name) < sizeof(tz->lcl_tzname);
+    if (tz->lcl_is_set)
+        strcpy(tz->lcl_tzname, name);
+
+    if (name[0] == '\0')
+    {
+        /* User wants it fast rather than right, so, we're off a little. */
+        lclptr->leapcnt = 0;
+        lclptr->timecnt = 0;
+        lclptr->typecnt = 0;
+        lclptr->ttis[0].isdst = 0;
+        lclptr->ttis[0].gmtoff = 0;
+        lclptr->ttis[0].abbrind = 0;
+        strcpy(lclptr->chars, gmt);
+    }
+    else if (name[0] == ':'  ||  tzparse(name, lclptr, FALSE) != 0)
+    {
+        tzparse(gmt, lclptr, TRUE);
+    }
+    set_tzname(tz);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) tz_localtime(tz_t *tz, struct tm *tmp, time_t t)
+{
+    struct tz_state_s *sp;
+    const struct tz_ttinfo_s *ttisp;
+    int i;
+
+    sp = &tz->state;
+
+    if (sp->timecnt == 0  ||  t < sp->ats[0])
+    {
+        i = 0;
+        while (sp->ttis[i].isdst)
+        {
+            if (++i >= sp->typecnt)
+            {
+                i = 0;
+                break;
+            }
+        }
+    }
+    else
+    {
+        for (i = 1;  i < sp->timecnt;  i++)
+        {
+            if (t < sp->ats[i])
+                break;
+        }
+        i = (int) sp->types[i - 1];
+    }
+    ttisp = &sp->ttis[i];
+    time_sub(&t, ttisp->gmtoff, sp, tmp);
+    tmp->tm_isdst = ttisp->isdst;
+    tz->tzname[tmp->tm_isdst] = &sp->chars[ttisp->abbrind];
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(const char *) tz_tzname(tz_t *tz, int isdst)
+{
+    return tz->tzname[(!isdst)  ?  0  :  1];
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(tz_t *) tz_init(tz_t *tz, const char *tzstring)
+{
+    if (tz == NULL)
+    {
+        if ((tz = (tz_t *) malloc(sizeof(*tz))) == NULL)
+            return NULL;
+    }
+    memset(tz, 0, sizeof(*tz));
+    tz->tzname[0] =
+    tz->tzname[1] = wildabbr;
+    tz_set(tz, tzstring);
+    return tz;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) tz_release(tz_t *tz)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) tz_free(tz_t *tz)
+{
+    if (tz)
+        free(tz);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/