-/*\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 ------------------------------------------------------------*/