提交 a5a6d6a6 authored 作者: cypromis's avatar cypromis

got rid of crlf windows endings on timezone.c

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