Skip to content
项目
群组
代码片段
帮助
正在加载...
登录
切换导航
F
freeswitch
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
张华
freeswitch
Commits
a5a6d6a6
提交
a5a6d6a6
authored
7月 24, 2010
作者:
cypromis
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
got rid of crlf windows endings on timezone.c
上级
17a0f628
隐藏空白字符变更
内嵌
并排
正在显示
1 个修改的文件
包含
822 行增加
和
822 行删除
+822
-822
timezone.c
libs/spandsp/src/timezone.c
+822
-822
没有找到文件。
libs/spandsp/src/timezone.c
浏览文件 @
a5a6d6a6
/*
* 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 ------------------------------------------------------------*/
/*
* 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 ------------------------------------------------------------*/
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论