| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
=head1 NAME |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
DateTime::TimeZone::Tzfile - tzfile (zoneinfo) timezone files |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use DateTime::TimeZone::Tzfile; |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
$tz = DateTime::TimeZone::Tzfile->new( |
|
10
|
|
|
|
|
|
|
name => "local timezone", |
|
11
|
|
|
|
|
|
|
filename => "/etc/localtime"); |
|
12
|
|
|
|
|
|
|
$tz = DateTime::TimeZone::Tzfile->new("/etc/localtime"); |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
if($tz->is_floating) { ... |
|
15
|
|
|
|
|
|
|
if($tz->is_utc) { ... |
|
16
|
|
|
|
|
|
|
if($tz->is_olson) { ... |
|
17
|
|
|
|
|
|
|
$category = $tz->category; |
|
18
|
|
|
|
|
|
|
$tz_string = $tz->name; |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
if($tz->has_dst_changes) { ... |
|
21
|
|
|
|
|
|
|
if($tz->is_dst_for_datetime($dt)) { ... |
|
22
|
|
|
|
|
|
|
$offset = $tz->offset_for_datetime($dt); |
|
23
|
|
|
|
|
|
|
$abbrev = $tz->short_name_for_datetime($dt); |
|
24
|
|
|
|
|
|
|
$offset = $tz->offset_for_local_datetime($dt); |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
An instance of this class represents a timezone that was encoded in a |
|
29
|
|
|
|
|
|
|
file in the L format. These can express arbitrary patterns |
|
30
|
|
|
|
|
|
|
of offsets from Universal Time, changing over time. Offsets and change |
|
31
|
|
|
|
|
|
|
times are limited to a resolution of one second. |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
This class implements the L interface, so that its |
|
34
|
|
|
|
|
|
|
instances can be used with L objects. |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=cut |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
package DateTime::TimeZone::Tzfile; |
|
39
|
|
|
|
|
|
|
|
|
40
|
5
|
|
|
5
|
|
15818
|
{ use 5.006; } |
|
|
5
|
|
|
|
|
20
|
|
|
|
5
|
|
|
|
|
242
|
|
|
41
|
5
|
|
|
5
|
|
31
|
use warnings; |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
198
|
|
|
42
|
5
|
|
|
5
|
|
30
|
use strict; |
|
|
5
|
|
|
|
|
21
|
|
|
|
5
|
|
|
|
|
183
|
|
|
43
|
|
|
|
|
|
|
|
|
44
|
5
|
|
|
5
|
|
26
|
use Carp qw(croak); |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
377
|
|
|
45
|
5
|
|
|
5
|
|
3199
|
use Date::ISO8601 0.000 qw(present_ymd); |
|
|
5
|
|
|
|
|
15307
|
|
|
|
5
|
|
|
|
|
392
|
|
|
46
|
5
|
|
|
5
|
|
3968
|
use IO::File 1.13; |
|
|
5
|
|
|
|
|
45918
|
|
|
|
5
|
|
|
|
|
950
|
|
|
47
|
5
|
|
|
5
|
|
40
|
use IO::Handle 1.08; |
|
|
5
|
|
|
|
|
91
|
|
|
|
5
|
|
|
|
|
223
|
|
|
48
|
5
|
|
|
5
|
|
5057
|
use Params::Classify 0.000 qw(is_undef is_string is_ref); |
|
|
5
|
|
|
|
|
14664
|
|
|
|
5
|
|
|
|
|
725
|
|
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
our $VERSION = "0.010"; |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my $rdn_epoch_cjdn = 1721425; |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# _fdiv(A, B), _fmod(A, B): divide A by B, flooring remainder |
|
55
|
|
|
|
|
|
|
# |
|
56
|
|
|
|
|
|
|
# B must be a positive Perl integer. A must be a Perl integer. |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub _fdiv($$) { |
|
59
|
14406
|
|
|
14406
|
|
17741
|
my($a, $b) = @_; |
|
60
|
14406
|
100
|
|
|
|
25184
|
if($a < 0) { |
|
61
|
5
|
|
|
5
|
|
34
|
use integer; |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
30
|
|
|
62
|
2507
|
|
|
|
|
5439
|
return -(($b - 1 - $a) / $b); |
|
63
|
|
|
|
|
|
|
} else { |
|
64
|
5
|
|
|
5
|
|
177
|
use integer; |
|
|
5
|
|
|
|
|
543
|
|
|
|
5
|
|
|
|
|
19
|
|
|
65
|
11899
|
|
|
|
|
21226
|
return $a / $b; |
|
66
|
|
|
|
|
|
|
} |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
|
|
69
|
14406
|
|
|
14406
|
|
30833
|
sub _fmod($$) { $_[0] % $_[1] } |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=over |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item DateTime::TimeZone::Tzfile->new(ATTR => VALUE, ...) |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Reads and parses a L format file, then constructs and returns |
|
78
|
|
|
|
|
|
|
a L-compatible timezone object that implements the timezone |
|
79
|
|
|
|
|
|
|
encoded in the file. The following attributes may be given: |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=over |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=item B |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
Name for the timezone object. This will be returned by the C |
|
86
|
|
|
|
|
|
|
method described below, and will be included in certain error messages. |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=item B |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
The string or C that will be returned by the C method |
|
91
|
|
|
|
|
|
|
described below. Default C. |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item B |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
The truth value that will be returned by the C method described |
|
96
|
|
|
|
|
|
|
below. Default false. |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=item B |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Name of the file from which to read the timezone data. The filename |
|
101
|
|
|
|
|
|
|
must be understood by L. |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=item B |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
An L object from which the timezone data can be read. |
|
106
|
|
|
|
|
|
|
This does not need to be a regular seekable file; it is read sequentially. |
|
107
|
|
|
|
|
|
|
After the constructor has finished, the handle can still be used to read |
|
108
|
|
|
|
|
|
|
any data that follows the timezone data. |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=back |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Either a filename or filehandle must be given. If a timezone name is not |
|
113
|
|
|
|
|
|
|
given, then the filename is used instead if supplied; a timezone name |
|
114
|
|
|
|
|
|
|
must be given explicitly if no filename is given. |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=item DateTime::TimeZone::Tzfile->new(FILENAME) |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Simpler way to invoke the above constructor in the usual case. Only the |
|
119
|
|
|
|
|
|
|
filename is given; this will also be used as the timezone name. |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=cut |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub _saferead($$) { |
|
124
|
16532
|
|
|
16532
|
|
21156
|
my($fh, $len) = @_; |
|
125
|
16532
|
|
|
|
|
16711
|
my $data; |
|
126
|
16532
|
|
|
|
|
43388
|
my $rlen = $fh->read($data, $len); |
|
127
|
16532
|
50
|
|
|
|
148331
|
croak "can't read tzfile: $!" unless defined($rlen); |
|
128
|
16532
|
50
|
|
|
|
32436
|
croak "bad tzfile: premature EOF" unless $rlen == $len; |
|
129
|
16532
|
|
|
|
|
47169
|
return $data; |
|
130
|
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
|
|
132
|
9153
|
|
|
9153
|
|
16338
|
sub _read_u32($) { unpack("N", _saferead($_[0], 4)) } |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub _read_s32($) { |
|
135
|
6040
|
|
|
6040
|
|
12308
|
my $uval = _read_u32($_[0]); |
|
136
|
6040
|
100
|
|
|
|
23051
|
return ($uval & 0x80000000) ? ($uval & 0x7fffffff) - 0x80000000 : |
|
137
|
|
|
|
|
|
|
$uval; |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
|
|
140
|
6329
|
|
|
6329
|
|
12554
|
sub _read_u8($) { ord(_saferead($_[0], 1)) } |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
my $unix_epoch_rdn = 719163; |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub _read_tm32($) { |
|
145
|
2866
|
|
|
2866
|
|
5669
|
my $t = _read_s32($_[0]); |
|
146
|
2866
|
|
|
|
|
5136
|
return [ $unix_epoch_rdn + _fdiv($t, 86400), _fmod($t, 86400) ]; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub _read_tm64($) { |
|
150
|
2885
|
|
|
2885
|
|
3787
|
my($fh) = @_; |
|
151
|
2885
|
|
|
|
|
4628
|
my $th = _read_s32($fh); |
|
152
|
2885
|
|
|
|
|
5226
|
my $tl = _read_u32($fh); |
|
153
|
2885
|
|
|
|
|
11122
|
my $dh = _fdiv($th, 86400); |
|
154
|
2885
|
|
|
|
|
4691
|
$th = (_fmod($th, 86400) << 10) | ($tl >> 22); |
|
155
|
2885
|
|
|
|
|
4475
|
my $d2 = _fdiv($th, 86400); |
|
156
|
2885
|
|
|
|
|
4607
|
$th = (_fmod($th, 86400) << 10) | (($tl >> 12) & 0x3ff); |
|
157
|
2885
|
|
|
|
|
4764
|
my $d3 = _fdiv($th, 86400); |
|
158
|
2885
|
|
|
|
|
4955
|
$th = (_fmod($th, 86400) << 12) | ($tl & 0xfff); |
|
159
|
2885
|
|
|
|
|
4450
|
my $d4 = _fdiv($th, 86400); |
|
160
|
2885
|
|
|
|
|
4682
|
$th = _fmod($th, 86400); |
|
161
|
2885
|
|
|
|
|
7423
|
my $d = $dh * 4294967296 + $d2 * 4194304 + (($d3 << 12) + $d4); |
|
162
|
2885
|
|
|
|
|
10464
|
return [ $unix_epoch_rdn + $d, $th ]; |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
my $factory_abbr = "Local time zone must be set--see zic manual page"; |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub new { |
|
168
|
43
|
|
|
43
|
1
|
25050
|
my $class = shift; |
|
169
|
43
|
100
|
|
|
|
181
|
unshift @_, "filename" if @_ == 1; |
|
170
|
43
|
|
|
|
|
306
|
my $self = bless({}, $class); |
|
171
|
43
|
|
|
|
|
69
|
my($filename, $fh); |
|
172
|
43
|
|
|
|
|
132
|
while(@_) { |
|
173
|
59
|
|
|
|
|
98
|
my $attr = shift; |
|
174
|
59
|
|
|
|
|
268
|
my $value = shift; |
|
175
|
59
|
100
|
|
|
|
655
|
if($attr eq "name") { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
176
|
13
|
100
|
|
|
|
190
|
croak "timezone name specified redundantly" |
|
177
|
|
|
|
|
|
|
if exists $self->{name}; |
|
178
|
12
|
100
|
|
|
|
768
|
croak "timezone name must be a string" |
|
179
|
|
|
|
|
|
|
unless is_string($value); |
|
180
|
8
|
|
|
|
|
34
|
$self->{name} = $value; |
|
181
|
|
|
|
|
|
|
} elsif($attr eq "category") { |
|
182
|
7
|
100
|
|
|
|
174
|
croak "category value specified redundantly" |
|
183
|
|
|
|
|
|
|
if exists $self->{category}; |
|
184
|
6
|
100
|
100
|
|
|
471
|
croak "category value must be a string or undef" |
|
185
|
|
|
|
|
|
|
unless is_undef($value) || is_string($value); |
|
186
|
3
|
|
|
|
|
195
|
$self->{category} = $value; |
|
187
|
|
|
|
|
|
|
} elsif($attr eq "is_olson") { |
|
188
|
4
|
100
|
|
|
|
338
|
croak "is_olson flag specified redundantly" |
|
189
|
|
|
|
|
|
|
if exists $self->{is_olson}; |
|
190
|
3
|
|
|
|
|
14
|
$self->{is_olson} = !!$value; |
|
191
|
|
|
|
|
|
|
} elsif($attr eq "filename") { |
|
192
|
27
|
100
|
100
|
|
|
535
|
croak "filename specified redundantly" |
|
193
|
|
|
|
|
|
|
if defined($filename) || defined($fh); |
|
194
|
25
|
100
|
|
|
|
614
|
croak "filename must be a string" |
|
195
|
|
|
|
|
|
|
unless is_string($value); |
|
196
|
21
|
|
|
|
|
248
|
$filename = $value; |
|
197
|
|
|
|
|
|
|
} elsif($attr eq "filehandle") { |
|
198
|
7
|
100
|
100
|
|
|
345
|
croak "filehandle specified redundantly" |
|
199
|
|
|
|
|
|
|
if defined($filename) || defined($fh); |
|
200
|
5
|
|
|
|
|
17
|
$fh = $value; |
|
201
|
|
|
|
|
|
|
} else { |
|
202
|
1
|
|
|
|
|
150
|
croak "unrecognised attribute `$attr'"; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
} |
|
205
|
24
|
100
|
100
|
|
|
419
|
croak "file not specified" unless defined($filename) || defined($fh); |
|
206
|
22
|
100
|
|
|
|
109
|
unless(exists $self->{name}) { |
|
207
|
16
|
100
|
|
|
|
199
|
croak "timezone name not specified" unless defined $filename; |
|
208
|
15
|
|
|
|
|
41
|
$self->{name} = $filename; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
21
|
100
|
|
|
|
255
|
unless(exists $self->{category}) { |
|
211
|
19
|
|
|
|
|
46
|
$self->{category} = undef; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
21
|
100
|
|
|
|
61
|
unless(exists $self->{is_olson}) { |
|
214
|
19
|
|
|
|
|
56
|
$self->{is_olson} = !!0; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
21
|
100
|
|
|
|
59
|
if(defined $filename) { |
|
217
|
19
|
100
|
66
|
|
|
464
|
($fh = IO::File->new($filename, "r")) && $fh->binmode |
|
218
|
|
|
|
|
|
|
or croak "can't read $filename: $!"; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
20
|
100
|
|
|
|
4351
|
croak "bad tzfile: wrong magic number" |
|
221
|
|
|
|
|
|
|
unless _saferead($fh, 4) eq "TZif"; |
|
222
|
19
|
|
|
|
|
69
|
my $fmtversion = _saferead($fh, 1); |
|
223
|
19
|
50
|
|
|
|
189
|
croak "bad tzfile: malformed version number" |
|
224
|
|
|
|
|
|
|
unless $fmtversion =~ /\A[2-9\0]\z/; |
|
225
|
19
|
|
|
|
|
44
|
_saferead($fh, 15); |
|
226
|
114
|
|
|
|
|
309
|
my($ttisgmtcnt, $ttisstdcnt, $leapcnt, $timecnt, $typecnt, $charcnt) = |
|
227
|
19
|
|
|
|
|
52
|
map { _read_u32($fh) } 1 .. 6; |
|
228
|
19
|
50
|
|
|
|
68
|
croak "bad tzfile: no local time types" if $typecnt == 0; |
|
229
|
19
|
|
|
|
|
230
|
my @trn_times = map { _read_tm32($fh) } 1 .. $timecnt; |
|
|
2866
|
|
|
|
|
18438
|
|
|
230
|
19
|
|
|
|
|
335
|
my @obs_types = map { _read_u8($fh) } 1 .. $timecnt; |
|
|
2866
|
|
|
|
|
4268
|
|
|
231
|
136
|
|
|
|
|
242
|
my @types = map { |
|
232
|
19
|
|
|
|
|
203
|
[ _read_s32($fh), !!_read_u8($fh), _read_u8($fh) ] |
|
233
|
|
|
|
|
|
|
} 1 .. $typecnt; |
|
234
|
19
|
|
|
|
|
67
|
my $chars = _saferead($fh, $charcnt); |
|
235
|
19
|
|
|
|
|
86
|
for(my $i = $leapcnt; $i--; ) { _saferead($fh, 8); } |
|
|
0
|
|
|
|
|
0
|
|
|
236
|
19
|
|
|
|
|
67
|
for(my $i = $ttisstdcnt; $i--; ) { _saferead($fh, 1); } |
|
|
136
|
|
|
|
|
205
|
|
|
237
|
19
|
|
|
|
|
78
|
for(my $i = $ttisgmtcnt; $i--; ) { _saferead($fh, 1); } |
|
|
136
|
|
|
|
|
230
|
|
|
238
|
19
|
|
|
|
|
31
|
my $late_rule; |
|
239
|
19
|
50
|
|
|
|
73
|
if($fmtversion ge "2") { |
|
240
|
19
|
50
|
|
|
|
49
|
croak "bad tzfile: wrong magic number" |
|
241
|
|
|
|
|
|
|
unless _saferead($fh, 4) eq "TZif"; |
|
242
|
19
|
|
|
|
|
51
|
_saferead($fh, 16); |
|
243
|
114
|
|
|
|
|
202
|
($ttisgmtcnt, $ttisstdcnt, $leapcnt, |
|
244
|
|
|
|
|
|
|
$timecnt, $typecnt, $charcnt) = |
|
245
|
19
|
|
|
|
|
47
|
map { _read_u32($fh) } 1 .. 6; |
|
246
|
19
|
50
|
|
|
|
71
|
croak "bad tzfile: no local time types" if $typecnt == 0; |
|
247
|
19
|
|
|
|
|
629
|
@trn_times = map { _read_tm64($fh) } 1 .. $timecnt; |
|
|
2885
|
|
|
|
|
4774
|
|
|
248
|
19
|
|
|
|
|
561
|
@obs_types = map { _read_u8($fh) } 1 .. $timecnt; |
|
|
2885
|
|
|
|
|
7444
|
|
|
249
|
153
|
|
|
|
|
329
|
@types = map { |
|
250
|
19
|
|
|
|
|
287
|
[ _read_s32($fh), !!_read_u8($fh), _read_u8($fh) ] |
|
251
|
|
|
|
|
|
|
} 1 .. $typecnt; |
|
252
|
19
|
|
|
|
|
63
|
$chars = _saferead($fh, $charcnt); |
|
253
|
19
|
|
|
|
|
400
|
for(my $i = $leapcnt; $i--; ) { _saferead($fh, 12); } |
|
|
0
|
|
|
|
|
0
|
|
|
254
|
19
|
|
|
|
|
79
|
for(my $i = $ttisstdcnt; $i--; ) { _saferead($fh, 1); } |
|
|
153
|
|
|
|
|
228
|
|
|
255
|
19
|
|
|
|
|
66
|
for(my $i = $ttisgmtcnt; $i--; ) { _saferead($fh, 1); } |
|
|
153
|
|
|
|
|
239
|
|
|
256
|
19
|
50
|
|
|
|
42
|
croak "bad tzfile: missing newline" |
|
257
|
|
|
|
|
|
|
unless _saferead($fh, 1) eq "\x0a"; |
|
258
|
19
|
|
|
|
|
141
|
$late_rule = ""; |
|
259
|
19
|
|
|
|
|
30
|
while(1) { |
|
260
|
319
|
|
|
|
|
742
|
my $c = _saferead($fh, 1); |
|
261
|
319
|
100
|
|
|
|
675
|
last if $c eq "\x0a"; |
|
262
|
300
|
|
|
|
|
351
|
$late_rule .= $c; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
} |
|
265
|
19
|
|
|
|
|
34
|
$fh = undef; |
|
266
|
19
|
|
|
|
|
6585
|
for(my $i = @trn_times - 1; $i-- > 0; ) { |
|
267
|
2866
|
50
|
33
|
|
|
10680
|
unless(($trn_times[$i]->[0] <=> $trn_times[$i+1]->[0] || |
|
268
|
|
|
|
|
|
|
$trn_times[$i]->[1] <=> $trn_times[$i+1]->[1]) == -1) { |
|
269
|
0
|
|
|
|
|
0
|
croak "bad tzfile: unsorted change times"; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
} |
|
272
|
19
|
|
|
|
|
36
|
my $first_std_type_index; |
|
273
|
|
|
|
|
|
|
my %offsets; |
|
274
|
19
|
|
|
|
|
82
|
for(my $i = 0; $i != $typecnt; $i++) { |
|
275
|
153
|
|
|
|
|
248
|
my $abbrind = $types[$i]->[2]; |
|
276
|
153
|
50
|
|
|
|
296
|
croak "bad tzfile: invalid abbreviation index" |
|
277
|
|
|
|
|
|
|
if $abbrind > $charcnt; |
|
278
|
153
|
|
|
|
|
464
|
pos($chars) = $abbrind; |
|
279
|
153
|
|
|
|
|
484
|
$chars =~ /\G([^\0]*)/g; |
|
280
|
153
|
|
|
|
|
384
|
$types[$i]->[2] = $1; |
|
281
|
153
|
50
|
66
|
|
|
370
|
$first_std_type_index = $i |
|
282
|
|
|
|
|
|
|
if !defined($first_std_type_index) && !$types[$i]->[1]; |
|
283
|
153
|
100
|
|
|
|
371
|
$self->{has_dst} = 1 if $types[$i]->[1]; |
|
284
|
153
|
100
|
66
|
|
|
1270
|
if($types[$i]->[0] == 0 && !$types[$i]->[1] && |
|
|
|
|
100
|
|
|
|
|
|
285
|
|
|
|
|
|
|
$types[$i]->[2] eq "zzz") { |
|
286
|
|
|
|
|
|
|
# "zzz" means the zone is not defined at this time, |
|
287
|
|
|
|
|
|
|
# due for example to the location being uninhabited |
|
288
|
6
|
|
|
|
|
30
|
$types[$i] = "zone disuse"; |
|
289
|
|
|
|
|
|
|
} else { |
|
290
|
147
|
|
|
|
|
644
|
$offsets{$types[$i]->[0]} = undef; |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
} |
|
293
|
19
|
50
|
|
|
|
159
|
unshift @obs_types, |
|
294
|
|
|
|
|
|
|
defined($first_std_type_index) ? $first_std_type_index : 0; |
|
295
|
19
|
|
|
|
|
58
|
foreach my $obs_type (@obs_types) { |
|
296
|
2904
|
50
|
|
|
|
5128
|
croak "bad tzfile: invalid local time type index" |
|
297
|
|
|
|
|
|
|
if $obs_type >= $typecnt; |
|
298
|
2904
|
|
|
|
|
3320
|
$obs_type = $types[$obs_type]; |
|
299
|
|
|
|
|
|
|
} |
|
300
|
19
|
0
|
33
|
|
|
192
|
if(defined($late_rule) && $late_rule eq "<$factory_abbr>0" && |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
301
|
|
|
|
|
|
|
defined($obs_types[-1]) && $obs_types[-1]->[0] == 0 && |
|
302
|
|
|
|
|
|
|
!$obs_types[-1]->[1] && |
|
303
|
|
|
|
|
|
|
$obs_types[-1]->[2] eq $factory_abbr) { |
|
304
|
|
|
|
|
|
|
# This bizarre timezone abbreviation is used in the Factory |
|
305
|
|
|
|
|
|
|
# timezone in the Olson database. It's not valid in a |
|
306
|
|
|
|
|
|
|
# SysV-style TZ value, because it contains spaces, but zic |
|
307
|
|
|
|
|
|
|
# puts it into one anyway because the file format demands |
|
308
|
|
|
|
|
|
|
# it. DT:TZ:SystemV would object, so as a special |
|
309
|
|
|
|
|
|
|
# exception we ignore the TZ value in this case. |
|
310
|
0
|
|
|
|
|
0
|
$late_rule = undef; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
19
|
50
|
|
|
|
55
|
if(defined $late_rule) { |
|
313
|
19
|
100
|
|
|
|
12089
|
if($late_rule eq "") { |
|
|
|
100
|
|
|
|
|
|
|
314
|
2
|
|
|
|
|
99
|
$obs_types[-1] = "missing data"; |
|
315
|
|
|
|
|
|
|
} elsif($late_rule =~ |
|
316
|
|
|
|
|
|
|
/\A(?:zzz|)[-+]?00?(?::00(?::00)?)?\z/) { |
|
317
|
2
|
|
|
|
|
6
|
$obs_types[-1] = "zone disuse"; |
|
318
|
|
|
|
|
|
|
} else { |
|
319
|
15
|
|
|
|
|
11556
|
require DateTime::TimeZone::SystemV; |
|
320
|
15
|
|
|
|
|
24227
|
DateTime::TimeZone::SystemV->VERSION("0.009"); |
|
321
|
15
|
100
|
|
|
|
175
|
$obs_types[-1] = |
|
322
|
|
|
|
|
|
|
DateTime::TimeZone::SystemV->new( |
|
323
|
|
|
|
|
|
|
system => $fmtversion ge "3" ? |
|
324
|
|
|
|
|
|
|
"tzfile3" : "posix", |
|
325
|
|
|
|
|
|
|
recipe => $late_rule); |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
} |
|
328
|
19
|
|
|
|
|
3838
|
$self->{trn_times} = \@trn_times; |
|
329
|
19
|
|
|
|
|
63
|
$self->{obs_types} = \@obs_types; |
|
330
|
19
|
|
|
|
|
174
|
$self->{offsets} = [ sort { $a <=> $b } keys %offsets ]; |
|
|
85
|
|
|
|
|
221
|
|
|
331
|
19
|
|
|
|
|
424
|
return $self; |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
sub _present_rdn_sod($$) { |
|
335
|
60
|
|
|
60
|
|
84
|
my($rdn, $sod) = @_; |
|
336
|
60
|
|
|
|
|
222
|
return sprintf("%sT%02d:%02d:%02d", |
|
337
|
|
|
|
|
|
|
present_ymd($rdn + $rdn_epoch_cjdn), |
|
338
|
|
|
|
|
|
|
int($sod/3600), int($sod/60)%60, $sod%60); |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=back |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=head1 METHODS |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
These methods are all part of the L interface. |
|
346
|
|
|
|
|
|
|
See that class for the general meaning of these methods; the documentation |
|
347
|
|
|
|
|
|
|
below only comments on the specific behaviour of this class. |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=head2 Identification |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=over |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=item $tz->is_floating |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Returns false. |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=cut |
|
358
|
|
|
|
|
|
|
|
|
359
|
3
|
|
|
3
|
1
|
1066
|
sub is_floating { 0 } |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=item $tz->is_utc |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Returns false. |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=cut |
|
366
|
|
|
|
|
|
|
|
|
367
|
3
|
|
|
3
|
1
|
12
|
sub is_utc { 0 } |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=item $tz->is_olson |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
Returns the truth value that was provided to the constructor for this |
|
372
|
|
|
|
|
|
|
purpose, default false. This nominally indicates whether the timezone |
|
373
|
|
|
|
|
|
|
data is from the Olson database. The files interpreted by this class |
|
374
|
|
|
|
|
|
|
are very likely to be from the Olson database, but there is no explicit |
|
375
|
|
|
|
|
|
|
indicator for this in the file, so this information must be supplied to |
|
376
|
|
|
|
|
|
|
the constructor if required. |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=cut |
|
379
|
|
|
|
|
|
|
|
|
380
|
3
|
|
|
3
|
1
|
19
|
sub is_olson { $_[0]->{is_olson} } |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=item $tz->category |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Returns the value that was provided to the constructor for this purpose, |
|
385
|
|
|
|
|
|
|
default C. This is intended to indicate the general region |
|
386
|
|
|
|
|
|
|
(continent or ocean) in which a geographical timezone is used, when |
|
387
|
|
|
|
|
|
|
the timezone is named according to the hierarchical scheme of the Olson |
|
388
|
|
|
|
|
|
|
timezone database. |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=cut |
|
391
|
|
|
|
|
|
|
|
|
392
|
3
|
|
|
3
|
1
|
18
|
sub category { $_[0]->{category} } |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=item $tz->name |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
Returns the timezone name. Usually this is the filename that was supplied |
|
397
|
|
|
|
|
|
|
to the constructor, but it can be overridden by the constructor's B |
|
398
|
|
|
|
|
|
|
attribute. |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=cut |
|
401
|
|
|
|
|
|
|
|
|
402
|
8
|
|
|
8
|
1
|
4552
|
sub name { $_[0]->{name} } |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=back |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=head2 Offsets |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=over |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=item $tz->has_dst_changes |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
Returns a truth value indicating whether any of the observances in the file |
|
413
|
|
|
|
|
|
|
are marked as DST. These DST flags are potentially arbitrary, and don't |
|
414
|
|
|
|
|
|
|
affect any of the zone's behaviour. |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
=cut |
|
417
|
|
|
|
|
|
|
|
|
418
|
3
|
|
|
3
|
1
|
12
|
sub has_dst_changes { $_[0]->{has_dst} } |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
# |
|
421
|
|
|
|
|
|
|
# observance lookup |
|
422
|
|
|
|
|
|
|
# |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
sub _type_for_rdn_sod { |
|
425
|
1986
|
|
|
1986
|
|
4145
|
my($self, $utc_rdn, $utc_sod) = @_; |
|
426
|
1986
|
|
|
|
|
2618
|
my $lo = 0; |
|
427
|
1986
|
|
|
|
|
2036
|
my $hi = @{$self->{trn_times}}; |
|
|
1986
|
|
|
|
|
3907
|
|
|
428
|
1986
|
|
|
|
|
4922
|
while($lo != $hi) { |
|
429
|
5
|
|
|
5
|
|
12872
|
my $try = do { use integer; ($lo + $hi) / 2 }; |
|
|
5
|
|
|
|
|
12
|
|
|
|
5
|
|
|
|
|
21
|
|
|
|
14395
|
|
|
|
|
14078
|
|
|
|
14395
|
|
|
|
|
18568
|
|
|
430
|
14395
|
100
|
100
|
|
|
73631
|
if(($utc_rdn <=> $self->{trn_times}->[$try]->[0] || |
|
431
|
|
|
|
|
|
|
$utc_sod <=> $self->{trn_times}->[$try]->[1]) == -1) { |
|
432
|
6871
|
|
|
|
|
13926
|
$hi = $try; |
|
433
|
|
|
|
|
|
|
} else { |
|
434
|
7524
|
|
|
|
|
16272
|
$lo = $try + 1; |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
} |
|
437
|
1986
|
|
|
|
|
4863
|
return $self->{obs_types}->[$lo]; |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
sub _type_for_datetime { |
|
441
|
1752
|
|
|
1752
|
|
20175
|
my($self, $dt) = @_; |
|
442
|
1752
|
|
|
|
|
5146
|
my($utc_rdn, $utc_sod) = $dt->utc_rd_values; |
|
443
|
1752
|
100
|
|
|
|
10097
|
$utc_sod = 86399 if $utc_sod >= 86400; |
|
444
|
1752
|
|
|
|
|
3815
|
my $type = $self->_type_for_rdn_sod($utc_rdn, $utc_sod); |
|
445
|
1752
|
100
|
|
|
|
4507
|
if(is_string($type)) { |
|
446
|
33
|
|
|
|
|
51
|
croak "time @{[_present_rdn_sod($utc_rdn, $utc_sod)]}Z ". |
|
|
33
|
|
|
|
|
70
|
|
|
447
|
|
|
|
|
|
|
"is not represented ". |
|
448
|
33
|
|
|
|
|
7823
|
"in the @{[$self->{name}]} timezone ". |
|
449
|
|
|
|
|
|
|
"due to $type"; |
|
450
|
|
|
|
|
|
|
} |
|
451
|
1719
|
|
|
|
|
4600
|
return $type; |
|
452
|
|
|
|
|
|
|
} |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=item $tz->offset_for_datetime(DT) |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
I must be a L-compatible object (specifically, it must |
|
457
|
|
|
|
|
|
|
implement the C method). Returns the offset from UT that |
|
458
|
|
|
|
|
|
|
is in effect at the instant represented by I, in seconds. |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=cut |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
sub offset_for_datetime { |
|
463
|
584
|
|
|
584
|
1
|
8122
|
my($self, $dt) = @_; |
|
464
|
584
|
|
|
|
|
1319
|
my $type = $self->_type_for_datetime($dt); |
|
465
|
573
|
100
|
|
|
|
3721
|
return is_ref($type, "ARRAY") ? $type->[0] : |
|
466
|
|
|
|
|
|
|
$type->offset_for_datetime($dt); |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
=item $tz->is_dst_for_datetime(DT) |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
I must be a L-compatible object (specifically, it must |
|
472
|
|
|
|
|
|
|
implement the C method). Returns a truth value indicating |
|
473
|
|
|
|
|
|
|
whether the timezone's observance at the instant represented by I |
|
474
|
|
|
|
|
|
|
is marked as DST. This DST flag is potentially arbitrary, and doesn't |
|
475
|
|
|
|
|
|
|
affect anything else. |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=cut |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
sub is_dst_for_datetime { |
|
480
|
584
|
|
|
584
|
1
|
369138
|
my($self, $dt) = @_; |
|
481
|
584
|
|
|
|
|
1423
|
my $type = $self->_type_for_datetime($dt); |
|
482
|
573
|
100
|
|
|
|
3905
|
return is_ref($type, "ARRAY") ? $type->[1] : |
|
483
|
|
|
|
|
|
|
$type->is_dst_for_datetime($dt); |
|
484
|
|
|
|
|
|
|
} |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=item $tz->short_name_for_datetime(DT) |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
I must be a L-compatible object (specifically, it must |
|
489
|
|
|
|
|
|
|
implement the C method). Returns the abbreviation |
|
490
|
|
|
|
|
|
|
used to label the time scale at the instant represented by I. |
|
491
|
|
|
|
|
|
|
This abbreviation is potentially arbitrary, and does not uniquely identify |
|
492
|
|
|
|
|
|
|
either the timezone or the offset. |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=cut |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
sub short_name_for_datetime { |
|
497
|
584
|
|
|
584
|
1
|
6945
|
my($self, $dt) = @_; |
|
498
|
584
|
|
|
|
|
1236
|
my $type = $self->_type_for_datetime($dt); |
|
499
|
573
|
100
|
|
|
|
8116
|
return is_ref($type, "ARRAY") ? $type->[2] : |
|
500
|
|
|
|
|
|
|
$type->short_name_for_datetime($dt); |
|
501
|
|
|
|
|
|
|
} |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
=item $tz->offset_for_local_datetime(DT) |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
I must be a L-compatible object (specifically, it |
|
506
|
|
|
|
|
|
|
must implement the C method). Takes the local |
|
507
|
|
|
|
|
|
|
time represented by I (regardless of what absolute time it also |
|
508
|
|
|
|
|
|
|
represents), and interprets that as a local time in the timezone of the |
|
509
|
|
|
|
|
|
|
timezone object (not the timezone used in I). Returns the offset |
|
510
|
|
|
|
|
|
|
from UT that is in effect at that local time, in seconds. |
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
If the local time given is ambiguous due to a nearby offset change, |
|
513
|
|
|
|
|
|
|
the numerically lowest offset (usually the standard one) is returned |
|
514
|
|
|
|
|
|
|
with no warning of the situation. (Equivalently: the latest possible |
|
515
|
|
|
|
|
|
|
absolute time is indicated.) If the local time given does not exist |
|
516
|
|
|
|
|
|
|
due to a nearby offset change, the method Cs saying so. |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=cut |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
sub _local_to_utc_rdn_sod($$$) { |
|
521
|
234
|
|
|
234
|
|
342
|
my($rdn, $sod, $offset) = @_; |
|
522
|
234
|
|
|
|
|
389
|
$sod -= $offset; |
|
523
|
234
|
|
|
|
|
2027
|
while($sod < 0) { |
|
524
|
15
|
|
|
|
|
23
|
$rdn--; |
|
525
|
15
|
|
|
|
|
194
|
$sod += 86400; |
|
526
|
|
|
|
|
|
|
} |
|
527
|
234
|
|
|
|
|
1433
|
while($sod >= 86400) { |
|
528
|
32
|
|
|
|
|
37
|
$rdn++; |
|
529
|
32
|
|
|
|
|
71
|
$sod -= 86400; |
|
530
|
|
|
|
|
|
|
} |
|
531
|
234
|
|
|
|
|
1020
|
return ($rdn, $sod); |
|
532
|
|
|
|
|
|
|
} |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
sub offset_for_local_datetime { |
|
535
|
85
|
|
|
85
|
1
|
12799
|
my($self, $dt) = @_; |
|
536
|
85
|
|
|
|
|
305
|
my($lcl_rdn, $lcl_sod) = $dt->local_rd_values; |
|
537
|
85
|
50
|
|
|
|
643
|
$lcl_sod = 86399 if $lcl_sod >= 86400; |
|
538
|
85
|
|
|
|
|
106
|
my %seen_error; |
|
539
|
85
|
|
|
|
|
302
|
foreach my $offset (@{$self->{offsets}}) { |
|
|
85
|
|
|
|
|
332
|
|
|
540
|
234
|
|
|
|
|
789
|
my($utc_rdn, $utc_sod) = |
|
541
|
|
|
|
|
|
|
_local_to_utc_rdn_sod($lcl_rdn, $lcl_sod, $offset); |
|
542
|
234
|
|
|
|
|
1328
|
my $ttype = $self->_type_for_rdn_sod($utc_rdn, $utc_sod); |
|
543
|
234
|
100
|
|
|
|
720
|
if(is_string($ttype)) { |
|
544
|
31
|
|
|
|
|
60
|
$seen_error{$ttype} = undef; |
|
545
|
31
|
|
|
|
|
296
|
next; |
|
546
|
|
|
|
|
|
|
} |
|
547
|
|
|
|
|
|
|
my $local_offset = is_ref($ttype, "ARRAY") ? $ttype->[0] : |
|
548
|
203
|
100
|
|
|
|
944
|
eval { local $SIG{__DIE__}; |
|
|
22
|
|
|
|
|
67
|
|
|
549
|
22
|
|
|
|
|
63
|
$ttype->offset_for_local_datetime($dt); |
|
550
|
|
|
|
|
|
|
}; |
|
551
|
203
|
100
|
100
|
|
|
55936
|
return $offset |
|
552
|
|
|
|
|
|
|
if defined($local_offset) && $local_offset == $offset; |
|
553
|
|
|
|
|
|
|
} |
|
554
|
27
|
|
|
|
|
61
|
my $error; |
|
555
|
27
|
|
|
|
|
50
|
foreach("zone disuse", "missing data") { |
|
556
|
45
|
100
|
|
|
|
129
|
if(exists $seen_error{$_}) { |
|
557
|
11
|
|
|
|
|
15
|
$error = $_; |
|
558
|
11
|
|
|
|
|
20
|
last; |
|
559
|
|
|
|
|
|
|
} |
|
560
|
|
|
|
|
|
|
} |
|
561
|
27
|
|
100
|
|
|
119
|
$error ||= "offset change"; |
|
562
|
27
|
|
|
|
|
43
|
croak "local time @{[_present_rdn_sod($lcl_rdn, $lcl_sod)]} ". |
|
|
27
|
|
|
|
|
156
|
|
|
563
|
27
|
|
|
|
|
10493
|
"does not exist in the @{[$self->{name}]} timezone ". |
|
564
|
|
|
|
|
|
|
"due to $error"; |
|
565
|
|
|
|
|
|
|
} |
|
566
|
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=back |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
L, |
|
572
|
|
|
|
|
|
|
L, |
|
573
|
|
|
|
|
|
|
L, |
|
574
|
|
|
|
|
|
|
L, |
|
575
|
|
|
|
|
|
|
L, |
|
576
|
|
|
|
|
|
|
L |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=head1 AUTHOR |
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
Andrew Main (Zefram) |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
Copyright (C) 2007, 2009, 2010, 2011, 2012, 2013 |
|
585
|
|
|
|
|
|
|
Andrew Main (Zefram) |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
=head1 LICENSE |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or modify it |
|
590
|
|
|
|
|
|
|
under the same terms as Perl itself. |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=cut |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
1; |