Filename | /home/s1/perl5/perlbrew/perls/perl-5.22.1/lib/site_perl/5.22.1/DateTime/TimeZone.pm |
Statements | Executed 34056 statements in 81.7ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
3002 | 3 | 2 | 33.1ms | 57.6ms | new | DateTime::TimeZone::
13000 | 1 | 1 | 7.14ms | 7.14ms | is_floating | DateTime::TimeZone::
1 | 1 | 1 | 2.59ms | 2.71ms | BEGIN@8 | DateTime::TimeZone::
3002 | 1 | 1 | 1.59ms | 1.59ms | CORE:match (opcode) | DateTime::TimeZone::
1 | 1 | 1 | 1.28ms | 5.63ms | BEGIN@10 | DateTime::TimeZone::
1 | 1 | 1 | 1.12ms | 1.24ms | BEGIN@12 | DateTime::TimeZone::
1 | 1 | 1 | 520µs | 3.42ms | BEGIN@9 | DateTime::TimeZone::
1 | 1 | 1 | 22µs | 22µs | BEGIN@3 | DateTime::TimeZone::
1 | 1 | 1 | 22µs | 95µs | BEGIN@15 | DateTime::TimeZone::
1 | 1 | 1 | 12µs | 60µs | BEGIN@28 | DateTime::TimeZone::
1 | 1 | 1 | 11µs | 11µs | BEGIN@11 | DateTime::TimeZone::
1 | 1 | 1 | 11µs | 14µs | BEGIN@5 | DateTime::TimeZone::
1 | 1 | 1 | 11µs | 53µs | BEGIN@16 | DateTime::TimeZone::
1 | 1 | 1 | 11µs | 67µs | BEGIN@18 | DateTime::TimeZone::
1 | 1 | 1 | 10µs | 10µs | BEGIN@13 | DateTime::TimeZone::
1 | 1 | 1 | 10µs | 55µs | BEGIN@19 | DateTime::TimeZone::
1 | 1 | 1 | 10µs | 72µs | BEGIN@27 | DateTime::TimeZone::
1 | 1 | 1 | 10µs | 28µs | BEGIN@14 | DateTime::TimeZone::
1 | 1 | 1 | 9µs | 55µs | BEGIN@22 | DateTime::TimeZone::
1 | 1 | 1 | 9µs | 16µs | BEGIN@6 | DateTime::TimeZone::
1 | 1 | 1 | 9µs | 50µs | BEGIN@23 | DateTime::TimeZone::
1 | 1 | 1 | 9µs | 50µs | BEGIN@24 | DateTime::TimeZone::
1 | 1 | 1 | 8µs | 47µs | BEGIN@25 | DateTime::TimeZone::
1 | 1 | 1 | 8µs | 49µs | BEGIN@26 | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | STORABLE_freeze | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | STORABLE_thaw | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | __ANON__[:407] | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | __ANON__[:446] | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | __ANON__[:481] | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | __ANON__[:82] | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | __ANON__[:85] | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | _generate_next_span | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | _generate_spans_until_match | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | _init | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | _keys_for_type | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | _span_as_array | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | _span_for_datetime | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | _spans_binary_search | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | all_names | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | categories | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | category | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | countries | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | has_dst_changes | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | is_dst_for_datetime | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | is_olson | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | is_utc | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | is_valid_name | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | links | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | max_span | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | name | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | names_in_category | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | names_in_country | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | offset_as_seconds | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | offset_as_string | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | offset_for_datetime | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | offset_for_local_datetime | DateTime::TimeZone::
0 | 0 | 0 | 0s | 0s | short_name_for_datetime | DateTime::TimeZone::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package DateTime::TimeZone; | ||||
2 | 1 | 1µs | $DateTime::TimeZone::VERSION = '1.98'; | ||
3 | 2 | 54µs | 1 | 22µs | # spent 22µs within DateTime::TimeZone::BEGIN@3 which was called:
# once (22µs+0s) by DateTime::BEGIN@15 at line 3 # spent 22µs making 1 call to DateTime::TimeZone::BEGIN@3 |
4 | |||||
5 | 2 | 30µs | 2 | 17µs | # spent 14µs (11+3) within DateTime::TimeZone::BEGIN@5 which was called:
# once (11µs+3µs) by DateTime::BEGIN@15 at line 5 # spent 14µs making 1 call to DateTime::TimeZone::BEGIN@5
# spent 3µs making 1 call to strict::import |
6 | 2 | 29µs | 2 | 22µs | # spent 16µs (9+6) within DateTime::TimeZone::BEGIN@6 which was called:
# once (9µs+6µs) by DateTime::BEGIN@15 at line 6 # spent 16µs making 1 call to DateTime::TimeZone::BEGIN@6
# spent 6µs making 1 call to warnings::import |
7 | |||||
8 | 2 | 202µs | 1 | 2.71ms | # spent 2.71ms (2.59+113µs) within DateTime::TimeZone::BEGIN@8 which was called:
# once (2.59ms+113µs) by DateTime::BEGIN@15 at line 8 # spent 2.71ms making 1 call to DateTime::TimeZone::BEGIN@8 |
9 | 2 | 290µs | 1 | 3.42ms | # spent 3.42ms (520µs+2.90) within DateTime::TimeZone::BEGIN@9 which was called:
# once (520µs+2.90ms) by DateTime::BEGIN@15 at line 9 # spent 3.42ms making 1 call to DateTime::TimeZone::BEGIN@9 |
10 | 2 | 300µs | 1 | 5.63ms | # spent 5.63ms (1.28+4.34) within DateTime::TimeZone::BEGIN@10 which was called:
# once (1.28ms+4.34ms) by DateTime::BEGIN@15 at line 10 # spent 5.63ms making 1 call to DateTime::TimeZone::BEGIN@10 |
11 | 2 | 37µs | 1 | 11µs | # spent 11µs within DateTime::TimeZone::BEGIN@11 which was called:
# once (11µs+0s) by DateTime::BEGIN@15 at line 11 # spent 11µs making 1 call to DateTime::TimeZone::BEGIN@11 |
12 | 2 | 275µs | 1 | 1.24ms | # spent 1.24ms (1.12+114µs) within DateTime::TimeZone::BEGIN@12 which was called:
# once (1.12ms+114µs) by DateTime::BEGIN@15 at line 12 # spent 1.24ms making 1 call to DateTime::TimeZone::BEGIN@12 |
13 | 2 | 38µs | 1 | 10µs | # spent 10µs within DateTime::TimeZone::BEGIN@13 which was called:
# once (10µs+0s) by DateTime::BEGIN@15 at line 13 # spent 10µs making 1 call to DateTime::TimeZone::BEGIN@13 |
14 | 2 | 44µs | 2 | 46µs | # spent 28µs (10+18) within DateTime::TimeZone::BEGIN@14 which was called:
# once (10µs+18µs) by DateTime::BEGIN@15 at line 14 # spent 28µs making 1 call to DateTime::TimeZone::BEGIN@14
# spent 18µs making 1 call to Module::Runtime::import |
15 | 3 | 64µs | 3 | 168µs | # spent 95µs (22+73) within DateTime::TimeZone::BEGIN@15 which was called:
# once (22µs+73µs) by DateTime::BEGIN@15 at line 15 # spent 95µs making 1 call to DateTime::TimeZone::BEGIN@15
# spent 59µs making 1 call to Exporter::import
# spent 14µs making 1 call to UNIVERSAL::VERSION |
16 | 2 | 57µs | 2 | 95µs | # spent 53µs (11+42) within DateTime::TimeZone::BEGIN@16 which was called:
# once (11µs+42µs) by DateTime::BEGIN@15 at line 16 # spent 53µs making 1 call to DateTime::TimeZone::BEGIN@16
# spent 42µs making 1 call to Exporter::import |
17 | |||||
18 | 2 | 48µs | 2 | 124µs | # spent 67µs (11+57) within DateTime::TimeZone::BEGIN@18 which was called:
# once (11µs+57µs) by DateTime::BEGIN@15 at line 18 # spent 67µs making 1 call to DateTime::TimeZone::BEGIN@18
# spent 57µs making 1 call to constant::import |
19 | 2 | 38µs | 2 | 100µs | # spent 55µs (10+45) within DateTime::TimeZone::BEGIN@19 which was called:
# once (10µs+45µs) by DateTime::BEGIN@15 at line 19 # spent 55µs making 1 call to DateTime::TimeZone::BEGIN@19
# spent 45µs making 1 call to constant::import |
20 | |||||
21 | # the offsets for each span element | ||||
22 | 2 | 36µs | 2 | 101µs | # spent 55µs (9+46) within DateTime::TimeZone::BEGIN@22 which was called:
# once (9µs+46µs) by DateTime::BEGIN@15 at line 22 # spent 55µs making 1 call to DateTime::TimeZone::BEGIN@22
# spent 46µs making 1 call to constant::import |
23 | 2 | 34µs | 2 | 91µs | # spent 50µs (9+41) within DateTime::TimeZone::BEGIN@23 which was called:
# once (9µs+41µs) by DateTime::BEGIN@15 at line 23 # spent 50µs making 1 call to DateTime::TimeZone::BEGIN@23
# spent 41µs making 1 call to constant::import |
24 | 2 | 33µs | 2 | 91µs | # spent 50µs (9+41) within DateTime::TimeZone::BEGIN@24 which was called:
# once (9µs+41µs) by DateTime::BEGIN@15 at line 24 # spent 50µs making 1 call to DateTime::TimeZone::BEGIN@24
# spent 41µs making 1 call to constant::import |
25 | 2 | 33µs | 2 | 86µs | # spent 47µs (8+39) within DateTime::TimeZone::BEGIN@25 which was called:
# once (8µs+39µs) by DateTime::BEGIN@15 at line 25 # spent 47µs making 1 call to DateTime::TimeZone::BEGIN@25
# spent 39µs making 1 call to constant::import |
26 | 2 | 52µs | 2 | 89µs | # spent 49µs (8+40) within DateTime::TimeZone::BEGIN@26 which was called:
# once (8µs+40µs) by DateTime::BEGIN@15 at line 26 # spent 49µs making 1 call to DateTime::TimeZone::BEGIN@26
# spent 40µs making 1 call to constant::import |
27 | 2 | 40µs | 2 | 134µs | # spent 72µs (10+62) within DateTime::TimeZone::BEGIN@27 which was called:
# once (10µs+62µs) by DateTime::BEGIN@15 at line 27 # spent 72µs making 1 call to DateTime::TimeZone::BEGIN@27
# spent 62µs making 1 call to constant::import |
28 | 2 | 3.78ms | 2 | 108µs | # spent 60µs (12+48) within DateTime::TimeZone::BEGIN@28 which was called:
# once (12µs+48µs) by DateTime::BEGIN@15 at line 28 # spent 60µs making 1 call to DateTime::TimeZone::BEGIN@28
# spent 48µs making 1 call to constant::import |
29 | |||||
30 | 1 | 23µs | my %SpecialName = map { $_ => 1 } | ||
31 | qw( EST MST HST CET EET MET WET EST5EDT CST6CDT MST7MDT PST8PDT ); | ||||
32 | |||||
33 | # spent 57.6ms (33.1+24.5) within DateTime::TimeZone::new which was called 3002 times, avg 19µs/call:
# 3000 times (33.0ms+24.5ms) by DateTime::_new at line 235 of DateTime.pm, avg 19µs/call
# once (46µs+52µs) by DateTime::Format::Alami::parse_datetime at line 78 of DateTime/Infinite.pm
# once (23µs+19µs) by DateTime::Format::Alami::parse_datetime at line 103 of DateTime/Infinite.pm | ||||
34 | 3002 | 740µs | my $class = shift; | ||
35 | 3002 | 31.7ms | 3002 | 17.7ms | my %p = validate( # spent 17.7ms making 3002 calls to Params::Validate::XS::validate, avg 6µs/call |
36 | @_, | ||||
37 | { name => { type => SCALAR } }, | ||||
38 | ); | ||||
39 | |||||
40 | 3002 | 2.75ms | if ( exists $DateTime::TimeZone::Catalog::LINKS{ $p{name} } ) { | ||
41 | $p{name} = $DateTime::TimeZone::Catalog::LINKS{ $p{name} }; | ||||
42 | } | ||||
43 | elsif ( exists $DateTime::TimeZone::Catalog::LINKS{ uc $p{name} } ) { | ||||
44 | $p{name} = $DateTime::TimeZone::Catalog::LINKS{ uc $p{name} }; | ||||
45 | } | ||||
46 | |||||
47 | 3002 | 6.74ms | 3002 | 1.59ms | unless ( $p{name} =~ m{/} # spent 1.59ms making 3002 calls to DateTime::TimeZone::CORE:match, avg 528ns/call |
48 | || $SpecialName{ $p{name} } ) { | ||||
49 | 3002 | 802µs | 2 | 22µs | if ( $p{name} eq 'floating' ) { # spent 22µs making 2 calls to Class::Singleton::instance, avg 11µs/call |
50 | return DateTime::TimeZone::Floating->instance; | ||||
51 | } | ||||
52 | |||||
53 | 3000 | 317µs | if ( $p{name} eq 'local' ) { | ||
54 | return DateTime::TimeZone::Local->TimeZone(); | ||||
55 | } | ||||
56 | |||||
57 | 3000 | 13.0ms | 3000 | 5.22ms | if ( $p{name} eq 'UTC' || $p{name} eq 'Z' ) { # spent 5.22ms making 3000 calls to Class::Singleton::instance, avg 2µs/call |
58 | return DateTime::TimeZone::UTC->instance; | ||||
59 | } | ||||
60 | |||||
61 | return DateTime::TimeZone::OffsetOnly->new( offset => $p{name} ); | ||||
62 | } | ||||
63 | |||||
64 | my $subclass = $p{name}; | ||||
65 | $subclass =~ s{/}{::}g; | ||||
66 | $subclass =~ s/-(\d)/_Minus$1/; | ||||
67 | $subclass =~ s/\+/_Plus/; | ||||
68 | $subclass =~ s/-/_/g; | ||||
69 | |||||
70 | my $real_class = "DateTime::TimeZone::$subclass"; | ||||
71 | |||||
72 | die "The timezone '$p{name}' is an invalid name.\n" | ||||
73 | unless $real_class =~ /^\w+(::\w+)*$/; | ||||
74 | |||||
75 | unless ( $real_class->can('instance') ) { | ||||
76 | ($real_class) = $real_class =~ m{\A([a-zA-Z0-9_]+(?:::[a-zA-Z0-9_]+)*)\z}; | ||||
77 | |||||
78 | my $e; | ||||
79 | try { | ||||
80 | local $SIG{__DIE__}; | ||||
81 | require_module($real_class); | ||||
82 | } | ||||
83 | catch { | ||||
84 | $e = $_; | ||||
85 | }; | ||||
86 | |||||
87 | if ($e) { | ||||
88 | my $regex = join '.', split /::/, $real_class; | ||||
89 | $regex .= '\\.pm'; | ||||
90 | |||||
91 | if ( $e =~ /^Can't locate $regex/i ) { | ||||
92 | die | ||||
93 | "The timezone '$p{name}' could not be loaded, or is an invalid name.\n"; | ||||
94 | } | ||||
95 | else { | ||||
96 | die $e; | ||||
97 | } | ||||
98 | } | ||||
99 | } | ||||
100 | |||||
101 | my $zone = $real_class->instance( name => $p{name}, is_olson => 1 ); | ||||
102 | |||||
103 | if ( $zone->is_olson() ) { | ||||
104 | my $object_version | ||||
105 | = $zone->can('olson_version') | ||||
106 | ? $zone->olson_version() | ||||
107 | : 'unknown'; | ||||
108 | my $catalog_version = DateTime::TimeZone::Catalog->OlsonVersion(); | ||||
109 | |||||
110 | if ( $object_version ne $catalog_version ) { | ||||
111 | warn | ||||
112 | "Loaded $real_class, which is from a different version ($object_version) of the Olson database than this installation of DateTime::TimeZone ($catalog_version).\n"; | ||||
113 | } | ||||
114 | } | ||||
115 | |||||
116 | return $zone; | ||||
117 | } | ||||
118 | |||||
119 | sub _init { | ||||
120 | my $class = shift; | ||||
121 | my %p = validate( | ||||
122 | @_, { | ||||
123 | name => { type => SCALAR }, | ||||
124 | spans => { type => ARRAYREF }, | ||||
125 | is_olson => { type => BOOLEAN, default => 0 }, | ||||
126 | }, | ||||
127 | ); | ||||
128 | |||||
129 | my $self = bless { | ||||
130 | name => $p{name}, | ||||
131 | spans => $p{spans}, | ||||
132 | is_olson => $p{is_olson}, | ||||
133 | }, $class; | ||||
134 | |||||
135 | foreach my $k (qw( last_offset last_observance rules max_year )) { | ||||
136 | my $m = "_$k"; | ||||
137 | $self->{$k} = $self->$m() if $self->can($m); | ||||
138 | } | ||||
139 | |||||
140 | return $self; | ||||
141 | } | ||||
142 | |||||
143 | sub is_olson { $_[0]->{is_olson} } | ||||
144 | |||||
145 | sub is_dst_for_datetime { | ||||
146 | my $self = shift; | ||||
147 | |||||
148 | my $span = $self->_span_for_datetime( 'utc', $_[0] ); | ||||
149 | |||||
150 | return $span->[IS_DST]; | ||||
151 | } | ||||
152 | |||||
153 | sub offset_for_datetime { | ||||
154 | my $self = shift; | ||||
155 | |||||
156 | my $span = $self->_span_for_datetime( 'utc', $_[0] ); | ||||
157 | |||||
158 | return $span->[OFFSET]; | ||||
159 | } | ||||
160 | |||||
161 | sub offset_for_local_datetime { | ||||
162 | my $self = shift; | ||||
163 | |||||
164 | my $span = $self->_span_for_datetime( 'local', $_[0] ); | ||||
165 | |||||
166 | return $span->[OFFSET]; | ||||
167 | } | ||||
168 | |||||
169 | sub short_name_for_datetime { | ||||
170 | my $self = shift; | ||||
171 | |||||
172 | my $span = $self->_span_for_datetime( 'utc', $_[0] ); | ||||
173 | |||||
174 | return $span->[SHORT_NAME]; | ||||
175 | } | ||||
176 | |||||
177 | sub _span_for_datetime { | ||||
178 | my $self = shift; | ||||
179 | my $type = shift; | ||||
180 | my $dt = shift; | ||||
181 | |||||
182 | my $method = $type . '_rd_as_seconds'; | ||||
183 | |||||
184 | my $end = $type eq 'utc' ? UTC_END : LOCAL_END; | ||||
185 | |||||
186 | my $span; | ||||
187 | my $seconds = $dt->$method(); | ||||
188 | if ( $seconds < $self->max_span->[$end] ) { | ||||
189 | $span = $self->_spans_binary_search( $type, $seconds ); | ||||
190 | } | ||||
191 | else { | ||||
192 | my $until_year = $dt->utc_year + 1; | ||||
193 | $span = $self->_generate_spans_until_match( $until_year, $seconds, | ||||
194 | $type ); | ||||
195 | } | ||||
196 | |||||
197 | # This means someone gave a local time that doesn't exist | ||||
198 | # (like during a transition into savings time) | ||||
199 | unless ( defined $span ) { | ||||
200 | my $err = 'Invalid local time for date'; | ||||
201 | $err .= ' ' . $dt->iso8601 if $type eq 'utc'; | ||||
202 | $err .= ' in time zone: ' . $self->name; | ||||
203 | $err .= "\n"; | ||||
204 | |||||
205 | die $err; | ||||
206 | } | ||||
207 | |||||
208 | return $span; | ||||
209 | } | ||||
210 | |||||
211 | sub _spans_binary_search { | ||||
212 | my $self = shift; | ||||
213 | my ( $type, $seconds ) = @_; | ||||
214 | |||||
215 | my ( $start, $end ) = _keys_for_type($type); | ||||
216 | |||||
217 | my $min = 0; | ||||
218 | my $max = scalar @{ $self->{spans} } + 1; | ||||
219 | my $i = int( $max / 2 ); | ||||
220 | |||||
221 | # special case for when there are only 2 spans | ||||
222 | $i++ if $max % 2 && $max != 3; | ||||
223 | |||||
224 | $i = 0 if @{ $self->{spans} } == 1; | ||||
225 | |||||
226 | while (1) { | ||||
227 | my $current = $self->{spans}[$i]; | ||||
228 | |||||
229 | if ( $seconds < $current->[$start] ) { | ||||
230 | $max = $i; | ||||
231 | my $c = int( ( $i - $min ) / 2 ); | ||||
232 | $c ||= 1; | ||||
233 | |||||
234 | $i -= $c; | ||||
235 | |||||
236 | return if $i < $min; | ||||
237 | } | ||||
238 | elsif ( $seconds >= $current->[$end] ) { | ||||
239 | $min = $i; | ||||
240 | my $c = int( ( $max - $i ) / 2 ); | ||||
241 | $c ||= 1; | ||||
242 | |||||
243 | $i += $c; | ||||
244 | |||||
245 | return if $i >= $max; | ||||
246 | } | ||||
247 | else { | ||||
248 | |||||
249 | # Special case for overlapping ranges because of DST and | ||||
250 | # other weirdness (like Alaska's change when bought from | ||||
251 | # Russia by the US). Always prefer latest span. | ||||
252 | if ( $current->[IS_DST] && $type eq 'local' ) { | ||||
253 | |||||
254 | # Asia/Dhaka in 2009j goes into DST without any known | ||||
255 | # end-of-DST date (wtf, Bangladesh). | ||||
256 | return $current if $current->[UTC_END] == INFINITY; | ||||
257 | |||||
258 | my $next = $self->{spans}[ $i + 1 ]; | ||||
259 | |||||
260 | # Sometimes we will get here and the span we're | ||||
261 | # looking at is the last that's been generated so far. | ||||
262 | # We need to try to generate one more or else we run | ||||
263 | # out. | ||||
264 | $next ||= $self->_generate_next_span; | ||||
265 | |||||
266 | die "No next span in $self->{max_year}" unless defined $next; | ||||
267 | |||||
268 | if ( ( !$next->[IS_DST] ) | ||||
269 | && $next->[$start] <= $seconds | ||||
270 | && $seconds <= $next->[$end] ) { | ||||
271 | return $next; | ||||
272 | } | ||||
273 | } | ||||
274 | |||||
275 | return $current; | ||||
276 | } | ||||
277 | } | ||||
278 | } | ||||
279 | |||||
280 | sub _generate_next_span { | ||||
281 | my $self = shift; | ||||
282 | |||||
283 | my $last_idx = $#{ $self->{spans} }; | ||||
284 | |||||
285 | my $max_span = $self->max_span; | ||||
286 | |||||
287 | # Kind of a hack, but AFAIK there are no zones where it takes | ||||
288 | # _more_ than a year for a _future_ time zone change to occur, so | ||||
289 | # by looking two years out we can ensure that we will find at | ||||
290 | # least one more span. Of course, I will no doubt be proved wrong | ||||
291 | # and this will cause errors. | ||||
292 | $self->_generate_spans_until_match( $self->{max_year} + 2, | ||||
293 | $max_span->[UTC_END] + ( 366 * 86400 ), 'utc' ); | ||||
294 | |||||
295 | return $self->{spans}[ $last_idx + 1 ]; | ||||
296 | } | ||||
297 | |||||
298 | sub _generate_spans_until_match { | ||||
299 | my $self = shift; | ||||
300 | my $generate_until_year = shift; | ||||
301 | my $seconds = shift; | ||||
302 | my $type = shift; | ||||
303 | |||||
304 | my @changes; | ||||
305 | my @rules = @{ $self->_rules }; | ||||
306 | foreach my $year ( $self->{max_year} .. $generate_until_year ) { | ||||
307 | for ( my $x = 0; $x < @rules; $x++ ) { | ||||
308 | my $last_offset_from_std; | ||||
309 | |||||
310 | if ( @rules == 2 ) { | ||||
311 | $last_offset_from_std | ||||
312 | = $x | ||||
313 | ? $rules[0]->offset_from_std | ||||
314 | : $rules[1]->offset_from_std; | ||||
315 | } | ||||
316 | elsif ( @rules == 1 ) { | ||||
317 | $last_offset_from_std = $rules[0]->offset_from_std; | ||||
318 | } | ||||
319 | else { | ||||
320 | my $count = scalar @rules; | ||||
321 | die | ||||
322 | "Cannot generate future changes for zone with $count infinite rules\n"; | ||||
323 | } | ||||
324 | |||||
325 | my $rule = $rules[$x]; | ||||
326 | |||||
327 | my $next = $rule->utc_start_datetime_for_year( $year, | ||||
328 | $self->{last_offset}, $last_offset_from_std ); | ||||
329 | |||||
330 | # don't bother with changes we've seen already | ||||
331 | next if $next->utc_rd_as_seconds < $self->max_span->[UTC_END]; | ||||
332 | |||||
333 | push @changes, | ||||
334 | DateTime::TimeZone::OlsonDB::Change->new( | ||||
335 | type => 'rule', | ||||
336 | utc_start_datetime => $next, | ||||
337 | local_start_datetime => $next + DateTime::Duration->new( | ||||
338 | seconds => $self->{last_observance}->total_offset | ||||
339 | + $rule->offset_from_std | ||||
340 | ), | ||||
341 | short_name => $self->{last_observance} | ||||
342 | ->formatted_short_name( $rule->letter ), | ||||
343 | observance => $self->{last_observance}, | ||||
344 | rule => $rule, | ||||
345 | ); | ||||
346 | } | ||||
347 | } | ||||
348 | |||||
349 | $self->{max_year} = $generate_until_year; | ||||
350 | |||||
351 | my @sorted | ||||
352 | = sort { $a->utc_start_datetime <=> $b->utc_start_datetime } @changes; | ||||
353 | |||||
354 | my ( $start, $end ) = _keys_for_type($type); | ||||
355 | |||||
356 | my $match; | ||||
357 | for ( my $x = 1; $x < @sorted; $x++ ) { | ||||
358 | my $last_total_offset | ||||
359 | = $x == 1 | ||||
360 | ? $self->max_span->[OFFSET] | ||||
361 | : $sorted[ $x - 2 ]->total_offset; | ||||
362 | |||||
363 | my $span = DateTime::TimeZone::OlsonDB::Change::two_changes_as_span( | ||||
364 | @sorted[ $x - 1, $x ], $last_total_offset ); | ||||
365 | |||||
366 | $span = _span_as_array($span); | ||||
367 | |||||
368 | push @{ $self->{spans} }, $span; | ||||
369 | |||||
370 | $match = $span | ||||
371 | if $seconds >= $span->[$start] && $seconds < $span->[$end]; | ||||
372 | } | ||||
373 | |||||
374 | return $match; | ||||
375 | } | ||||
376 | |||||
377 | sub max_span { $_[0]->{spans}[-1] } | ||||
378 | |||||
379 | sub _keys_for_type { | ||||
380 | $_[0] eq 'utc' ? ( UTC_START, UTC_END ) : ( LOCAL_START, LOCAL_END ); | ||||
381 | } | ||||
382 | |||||
383 | sub _span_as_array { | ||||
384 | [ | ||||
385 | @{ $_[0] }{ | ||||
386 | qw( utc_start utc_end local_start local_end offset is_dst short_name ) | ||||
387 | } | ||||
388 | ]; | ||||
389 | } | ||||
390 | |||||
391 | 13000 | 20.1ms | # spent 7.14ms within DateTime::TimeZone::is_floating which was called 13000 times, avg 549ns/call:
# 13000 times (7.14ms+0s) by DateTime::_handle_offset_modifier at line 332 of DateTime.pm, avg 549ns/call | ||
392 | |||||
393 | sub is_utc {0} | ||||
394 | |||||
395 | sub has_dst_changes {0} | ||||
396 | |||||
397 | sub name { $_[0]->{name} } | ||||
398 | sub category { ( split /\//, $_[0]->{name}, 2 )[0] } | ||||
399 | |||||
400 | sub is_valid_name { | ||||
401 | my $class = shift; | ||||
402 | my $name = shift; | ||||
403 | |||||
404 | my $tz = try { | ||||
405 | local $SIG{__DIE__}; | ||||
406 | $class->new( name => $name ); | ||||
407 | }; | ||||
408 | |||||
409 | return $tz && $tz->isa('DateTime::TimeZone') ? 1 : 0; | ||||
410 | } | ||||
411 | |||||
412 | sub STORABLE_freeze { | ||||
413 | my $self = shift; | ||||
414 | |||||
415 | return $self->name; | ||||
416 | } | ||||
417 | |||||
418 | sub STORABLE_thaw { | ||||
419 | my $self = shift; | ||||
420 | my $cloning = shift; | ||||
421 | my $serialized = shift; | ||||
422 | |||||
423 | my $class = ref $self || $self; | ||||
424 | |||||
425 | my $obj; | ||||
426 | if ( $class->isa(__PACKAGE__) ) { | ||||
427 | $obj = __PACKAGE__->new( name => $serialized ); | ||||
428 | } | ||||
429 | else { | ||||
430 | $obj = $class->new( name => $serialized ); | ||||
431 | } | ||||
432 | |||||
433 | %$self = %$obj; | ||||
434 | |||||
435 | return $self; | ||||
436 | } | ||||
437 | |||||
438 | # | ||||
439 | # Functions | ||||
440 | # | ||||
441 | sub offset_as_seconds { | ||||
442 | my $offset = shift; | ||||
443 | $offset = shift if try { | ||||
444 | local $SIG{__DIE__}; | ||||
445 | $offset->isa('DateTime::TimeZone'); | ||||
446 | }; | ||||
447 | |||||
448 | return undef unless defined $offset; | ||||
449 | |||||
450 | return 0 if $offset eq '0'; | ||||
451 | |||||
452 | my ( $sign, $hours, $minutes, $seconds ); | ||||
453 | if ( $offset =~ /^([\+\-])?(\d\d?):(\d\d)(?::(\d\d))?$/ ) { | ||||
454 | ( $sign, $hours, $minutes, $seconds ) = ( $1, $2, $3, $4 ); | ||||
455 | } | ||||
456 | elsif ( $offset =~ /^([\+\-])?(\d\d)(\d\d)(\d\d)?$/ ) { | ||||
457 | ( $sign, $hours, $minutes, $seconds ) = ( $1, $2, $3, $4 ); | ||||
458 | } | ||||
459 | else { | ||||
460 | return undef; | ||||
461 | } | ||||
462 | |||||
463 | $sign = '+' unless defined $sign; | ||||
464 | return undef unless $hours >= 0 && $hours <= 99; | ||||
465 | return undef unless $minutes >= 0 && $minutes <= 59; | ||||
466 | return undef | ||||
467 | unless !defined($seconds) || ( $seconds >= 0 && $seconds <= 59 ); | ||||
468 | |||||
469 | my $total = $hours * 3600 + $minutes * 60; | ||||
470 | $total += $seconds if $seconds; | ||||
471 | $total *= -1 if $sign eq '-'; | ||||
472 | |||||
473 | return $total; | ||||
474 | } | ||||
475 | |||||
476 | sub offset_as_string { | ||||
477 | my $offset = shift; | ||||
478 | $offset = shift if try { | ||||
479 | local $SIG{__DIE__}; | ||||
480 | $offset->isa('DateTime::TimeZone'); | ||||
481 | }; | ||||
482 | |||||
483 | return undef unless defined $offset; | ||||
484 | return undef unless $offset >= -359999 && $offset <= 359999; | ||||
485 | |||||
486 | my $sign = $offset < 0 ? '-' : '+'; | ||||
487 | |||||
488 | $offset = abs($offset); | ||||
489 | |||||
490 | my $hours = int( $offset / 3600 ); | ||||
491 | $offset %= 3600; | ||||
492 | my $mins = int( $offset / 60 ); | ||||
493 | $offset %= 60; | ||||
494 | my $secs = int($offset); | ||||
495 | |||||
496 | return ( | ||||
497 | $secs | ||||
498 | ? sprintf( '%s%02d%02d%02d', $sign, $hours, $mins, $secs ) | ||||
499 | : sprintf( '%s%02d%02d', $sign, $hours, $mins ) | ||||
500 | ); | ||||
501 | } | ||||
502 | |||||
503 | # These methods all operate on data contained in the DateTime/TimeZone/Catalog.pm file. | ||||
504 | |||||
505 | sub all_names { | ||||
506 | return | ||||
507 | wantarray | ||||
508 | ? @DateTime::TimeZone::Catalog::ALL | ||||
509 | : [@DateTime::TimeZone::Catalog::ALL]; | ||||
510 | } | ||||
511 | |||||
512 | sub categories { | ||||
513 | return wantarray | ||||
514 | ? @DateTime::TimeZone::Catalog::CATEGORY_NAMES | ||||
515 | : [@DateTime::TimeZone::Catalog::CATEGORY_NAMES]; | ||||
516 | } | ||||
517 | |||||
518 | sub links { | ||||
519 | return | ||||
520 | wantarray | ||||
521 | ? %DateTime::TimeZone::Catalog::LINKS | ||||
522 | : {%DateTime::TimeZone::Catalog::LINKS}; | ||||
523 | } | ||||
524 | |||||
525 | sub names_in_category { | ||||
526 | shift if $_[0]->isa('DateTime::TimeZone'); | ||||
527 | return unless exists $DateTime::TimeZone::Catalog::CATEGORIES{ $_[0] }; | ||||
528 | |||||
529 | return wantarray | ||||
530 | ? @{ $DateTime::TimeZone::Catalog::CATEGORIES{ $_[0] } } | ||||
531 | : $DateTime::TimeZone::Catalog::CATEGORIES{ $_[0] }; | ||||
532 | } | ||||
533 | |||||
534 | sub countries { | ||||
535 | wantarray | ||||
536 | ? ( sort keys %DateTime::TimeZone::Catalog::ZONES_BY_COUNTRY ) | ||||
537 | : [ sort keys %DateTime::TimeZone::Catalog::ZONES_BY_COUNTRY ]; | ||||
538 | } | ||||
539 | |||||
540 | sub names_in_country { | ||||
541 | shift if $_[0]->isa('DateTime::TimeZone'); | ||||
542 | |||||
543 | return | ||||
544 | unless | ||||
545 | exists $DateTime::TimeZone::Catalog::ZONES_BY_COUNTRY{ lc $_[0] }; | ||||
546 | |||||
547 | return | ||||
548 | wantarray | ||||
549 | ? @{ $DateTime::TimeZone::Catalog::ZONES_BY_COUNTRY{ lc $_[0] } } | ||||
550 | : $DateTime::TimeZone::Catalog::ZONES_BY_COUNTRY{ lc $_[0] }; | ||||
551 | } | ||||
552 | |||||
553 | 1 | 17µs | 1; | ||
554 | |||||
555 | # ABSTRACT: Time zone object base class and factory | ||||
556 | |||||
557 | __END__ | ||||
# spent 1.59ms within DateTime::TimeZone::CORE:match which was called 3002 times, avg 528ns/call:
# 3002 times (1.59ms+0s) by DateTime::TimeZone::new at line 47, avg 528ns/call |