← Index
NYTProf Performance Profile   « line view »
For /usr/local/libexec/sympa/task_manager-debug.pl
  Run on Tue Jun 1 22:32:51 2021
Reported on Tue Jun 1 22:35:06 2021

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