Profile of DateTime/TimeZone.pm

← Index
NYTProf Performance Profile «line view»
For -e
Run on Thu Jun 30 16:34:56 2016
Reported on Thu Jun 30 16:35:09 2016

Filename | /home/s1/perl5/perlbrew/perls/perl-5.22.1/lib/site_perl/5.22.1/DateTime/TimeZone.pm
Statements | Executed 34056 statements in 93.7ms

Subroutines
Calls | P | F | Exclusive Time | Inclusive Time | Subroutine
--- | --- | --- | --- | --- | ---
3002 | 3 | 2 | 42.9ms | 65.7ms | DateTime::TimeZone::::newDateTime::TimeZone::new
13000 | 1 | 1 | 6.31ms | 6.31ms | DateTime::TimeZone::::is_floatingDateTime::TimeZone::is_floating
1 | 1 | 1 | 2.05ms | 2.15ms | DateTime::TimeZone::::BEGIN@8DateTime::TimeZone::BEGIN@8
3002 | 1 | 1 | 1.28ms | 1.28ms | DateTime::TimeZone::::CORE:matchDateTime::TimeZone::CORE:match (opcode)
1 | 1 | 1 | 863µs | 974µs | DateTime::TimeZone::::BEGIN@10DateTime::TimeZone::BEGIN@10
1 | 1 | 1 | 804µs | 878µs | DateTime::TimeZone::::BEGIN@12DateTime::TimeZone::BEGIN@12
1 | 1 | 1 | 319µs | 2.43ms | DateTime::TimeZone::::BEGIN@9DateTime::TimeZone::BEGIN@9
1 | 1 | 1 | 18µs | 18µs | DateTime::TimeZone::::BEGIN@3DateTime::TimeZone::BEGIN@3
1 | 1 | 1 | 14µs | 64µs | DateTime::TimeZone::::BEGIN@15DateTime::TimeZone::BEGIN@15
1 | 1 | 1 | 9µs | 58µs | DateTime::TimeZone::::BEGIN@18DateTime::TimeZone::BEGIN@18
1 | 1 | 1 | 8µs | 36µs | DateTime::TimeZone::::BEGIN@16DateTime::TimeZone::BEGIN@16
1 | 1 | 1 | 8µs | 10µs | DateTime::TimeZone::::BEGIN@5DateTime::TimeZone::BEGIN@5
1 | 1 | 1 | 8µs | 12µs | DateTime::TimeZone::::BEGIN@6DateTime::TimeZone::BEGIN@6
1 | 1 | 1 | 7µs | 7µs | DateTime::TimeZone::::BEGIN@11DateTime::TimeZone::BEGIN@11
1 | 1 | 1 | 7µs | 37µs | DateTime::TimeZone::::BEGIN@19DateTime::TimeZone::BEGIN@19
1 | 1 | 1 | 7µs | 19µs | DateTime::TimeZone::::BEGIN@14DateTime::TimeZone::BEGIN@14
1 | 1 | 1 | 6µs | 34µs | DateTime::TimeZone::::BEGIN@28DateTime::TimeZone::BEGIN@28
1 | 1 | 1 | 6µs | 34µs | DateTime::TimeZone::::BEGIN@22DateTime::TimeZone::BEGIN@22
1 | 1 | 1 | 6µs | 31µs | DateTime::TimeZone::::BEGIN@27DateTime::TimeZone::BEGIN@27
1 | 1 | 1 | 6µs | 34µs | DateTime::TimeZone::::BEGIN@23DateTime::TimeZone::BEGIN@23
1 | 1 | 1 | 6µs | 32µs | DateTime::TimeZone::::BEGIN@25DateTime::TimeZone::BEGIN@25
1 | 1 | 1 | 6µs | 34µs | DateTime::TimeZone::::BEGIN@24DateTime::TimeZone::BEGIN@24
1 | 1 | 1 | 6µs | 32µs | DateTime::TimeZone::::BEGIN@26DateTime::TimeZone::BEGIN@26
1 | 1 | 1 | 6µs | 6µs | DateTime::TimeZone::::BEGIN@13DateTime::TimeZone::BEGIN@13
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::STORABLE_freezeDateTime::TimeZone::STORABLE_freeze
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::STORABLE_thawDateTime::TimeZone::STORABLE_thaw
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::__ANON__[:407]DateTime::TimeZone::__ANON__[:407]
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::__ANON__[:446]DateTime::TimeZone::__ANON__[:446]
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::__ANON__[:481]DateTime::TimeZone::__ANON__[:481]
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::__ANON__[:82]DateTime::TimeZone::__ANON__[:82]
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::__ANON__[:85]DateTime::TimeZone::__ANON__[:85]
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::_generate_next_spanDateTime::TimeZone::_generate_next_span
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::_generate_spans_until_matchDateTime::TimeZone::_generate_spans_until_match
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::_initDateTime::TimeZone::_init
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::_keys_for_typeDateTime::TimeZone::_keys_for_type
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::_span_as_arrayDateTime::TimeZone::_span_as_array
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::_span_for_datetimeDateTime::TimeZone::_span_for_datetime
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::_spans_binary_searchDateTime::TimeZone::_spans_binary_search
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::all_namesDateTime::TimeZone::all_names
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::categoriesDateTime::TimeZone::categories
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::categoryDateTime::TimeZone::category
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::countriesDateTime::TimeZone::countries
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::has_dst_changesDateTime::TimeZone::has_dst_changes
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::is_dst_for_datetimeDateTime::TimeZone::is_dst_for_datetime
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::is_olsonDateTime::TimeZone::is_olson
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::is_utcDateTime::TimeZone::is_utc
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::is_valid_nameDateTime::TimeZone::is_valid_name
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::linksDateTime::TimeZone::links
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::max_spanDateTime::TimeZone::max_span
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::nameDateTime::TimeZone::name
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::names_in_categoryDateTime::TimeZone::names_in_category
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::names_in_countryDateTime::TimeZone::names_in_country
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::offset_as_secondsDateTime::TimeZone::offset_as_seconds
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::offset_as_stringDateTime::TimeZone::offset_as_string
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::offset_for_datetimeDateTime::TimeZone::offset_for_datetime
0 | 0 | 0 | 0s | 0s | DateTime::TimeZone::::offset_for_local_datetimeDateTime::TimeZone::offset_for_local_datetime
0 | 0 | 0 | 0s | 0s | DateTime::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
--- | --- | --- | --- | --- | ---
1 |  |  |  |  | package DateTime::TimeZone;
2 | 1 | 600ns |  |  | $DateTime::TimeZone::VERSION = '1.98';
3 | 2 | 42µs | 1 | 18µs | # spent 18µs within DateTime::TimeZone::BEGIN@3 which was called: # once (18µs+0s) by DateTime::BEGIN@15 at line 3 use 5.006; # spent 18µs making 1 call to DateTime::TimeZone::BEGIN@3
4 |  |  |  |  |
5 | 2 | 23µs | 2 | 12µs | # spent 10µs (8+2) within DateTime::TimeZone::BEGIN@5 which was called: # once (8µs+2µs) by DateTime::BEGIN@15 at line 5 use strict; # spent 10µs making 1 call to DateTime::TimeZone::BEGIN@5 # spent 2µs making 1 call to strict::import
6 | 2 | 23µs | 2 | 17µs | # spent 12µs (8+5) within DateTime::TimeZone::BEGIN@6 which was called: # once (8µs+5µs) by DateTime::BEGIN@15 at line 6 use warnings; # spent 12µs making 1 call to DateTime::TimeZone::BEGIN@6 # spent 5µs making 1 call to warnings::import
7 |  |  |  |  |
8 | 2 | 219µs | 1 | 2.15ms | # spent 2.15ms (2.05+96µs) within DateTime::TimeZone::BEGIN@8 which was called: # once (2.05ms+96µs) by DateTime::BEGIN@15 at line 8 use DateTime::TimeZone::Catalog; # spent 2.15ms making 1 call to DateTime::TimeZone::BEGIN@8
9 | 2 | 184µs | 1 | 2.43ms | # spent 2.43ms (319µs+2.11) within DateTime::TimeZone::BEGIN@9 which was called: # once (319µs+2.11ms) by DateTime::BEGIN@15 at line 9 use DateTime::TimeZone::Floating; # spent 2.43ms making 1 call to DateTime::TimeZone::BEGIN@9
10 | 2 | 142µs | 1 | 974µs | # spent 974µs (863+111) within DateTime::TimeZone::BEGIN@10 which was called: # once (863µs+111µs) by DateTime::BEGIN@15 at line 10 use DateTime::TimeZone::Local; # spent 974µs making 1 call to DateTime::TimeZone::BEGIN@10
11 | 2 | 25µs | 1 | 7µs | # spent 7µs within DateTime::TimeZone::BEGIN@11 which was called: # once (7µs+0s) by DateTime::BEGIN@15 at line 11 use DateTime::TimeZone::OffsetOnly; # spent 7µs making 1 call to DateTime::TimeZone::BEGIN@11
12 | 2 | 212µs | 1 | 878µs | # spent 878µs (804+74) within DateTime::TimeZone::BEGIN@12 which was called: # once (804µs+74µs) by DateTime::BEGIN@15 at line 12 use DateTime::TimeZone::OlsonDB::Change; # spent 878µs making 1 call to DateTime::TimeZone::BEGIN@12
13 | 2 | 25µs | 1 | 6µs | # spent 6µs within DateTime::TimeZone::BEGIN@13 which was called: # once (6µs+0s) by DateTime::BEGIN@15 at line 13 use DateTime::TimeZone::UTC; # spent 6µs making 1 call to DateTime::TimeZone::BEGIN@13
14 | 2 | 30µs | 2 | 32µs | # spent 19µs (7+12) within DateTime::TimeZone::BEGIN@14 which was called: # once (7µs+12µs) by DateTime::BEGIN@15 at line 14 use Module::Runtime qw( require_module ); # spent 19µs making 1 call to DateTime::TimeZone::BEGIN@14 # spent 12µs making 1 call to Module::Runtime::import
15 | 3 | 43µs | 3 | 114µs | # spent 64µs (14+50) within DateTime::TimeZone::BEGIN@15 which was called: # once (14µs+50µs) by DateTime::BEGIN@15 at line 15 use Params::Validate 0.72 qw( validate validate_pos SCALAR ARRAYREF BOOLEAN ); # spent 64µs making 1 call to DateTime::TimeZone::BEGIN@15 # spent 40µs making 1 call to Exporter::import # spent 10µs making 1 call to UNIVERSAL::VERSION
16 | 2 | 44µs | 2 | 63µs | # spent 36µs (8+28) within DateTime::TimeZone::BEGIN@16 which was called: # once (8µs+28µs) by DateTime::BEGIN@15 at line 16 use Try::Tiny; # spent 36µs making 1 call to DateTime::TimeZone::BEGIN@16 # spent 28µs making 1 call to Exporter::import
17 |  |  |  |  |
18 | 2 | 35µs | 2 | 107µs | # spent 58µs (9+49) within DateTime::TimeZone::BEGIN@18 which was called: # once (9µs+49µs) by DateTime::BEGIN@15 at line 18 use constant INFINITY => 100**1000; # spent 58µs making 1 call to DateTime::TimeZone::BEGIN@18 # spent 49µs making 1 call to constant::import
19 | 2 | 26µs | 2 | 68µs | # spent 37µs (7+31) within DateTime::TimeZone::BEGIN@19 which was called: # once (7µs+31µs) by DateTime::BEGIN@15 at line 19 use constant NEG_INFINITY => -1 * ( 100**1000 ); # spent 37µs making 1 call to DateTime::TimeZone::BEGIN@19 # spent 30µs making 1 call to constant::import
20 |  |  |  |  |
21 |  |  |  |  | # the offsets for each span element
22 | 2 | 27µs | 2 | 62µs | # spent 34µs (6+28) within DateTime::TimeZone::BEGIN@22 which was called: # once (6µs+28µs) by DateTime::BEGIN@15 at line 22 use constant UTC_START => 0; # spent 34µs making 1 call to DateTime::TimeZone::BEGIN@22 # spent 28µs making 1 call to constant::import
23 | 2 | 24µs | 2 | 61µs | # spent 34µs (6+28) within DateTime::TimeZone::BEGIN@23 which was called: # once (6µs+28µs) by DateTime::BEGIN@15 at line 23 use constant UTC_END => 1; # spent 34µs making 1 call to DateTime::TimeZone::BEGIN@23 # spent 28µs making 1 call to constant::import
24 | 2 | 23µs | 2 | 61µs | # spent 34µs (6+28) within DateTime::TimeZone::BEGIN@24 which was called: # once (6µs+28µs) by DateTime::BEGIN@15 at line 24 use constant LOCAL_START => 2; # spent 34µs making 1 call to DateTime::TimeZone::BEGIN@24 # spent 28µs making 1 call to constant::import
25 | 2 | 22µs | 2 | 58µs | # spent 32µs (6+26) within DateTime::TimeZone::BEGIN@25 which was called: # once (6µs+26µs) by DateTime::BEGIN@15 at line 25 use constant LOCAL_END => 3; # spent 32µs making 1 call to DateTime::TimeZone::BEGIN@25 # spent 26µs making 1 call to constant::import
26 | 2 | 22µs | 2 | 58µs | # spent 32µs (6+26) within DateTime::TimeZone::BEGIN@26 which was called: # once (6µs+26µs) by DateTime::BEGIN@15 at line 26 use constant OFFSET => 4; # spent 32µs making 1 call to DateTime::TimeZone::BEGIN@26 # spent 26µs making 1 call to constant::import
27 | 2 | 23µs | 2 | 57µs | # spent 31µs (6+25) within DateTime::TimeZone::BEGIN@27 which was called: # once (6µs+25µs) by DateTime::BEGIN@15 at line 27 use constant IS_DST => 5; # spent 31µs making 1 call to DateTime::TimeZone::BEGIN@27 # spent 25µs making 1 call to constant::import
28 | 2 | 2.15ms | 2 | 61µs | # spent 34µs (6+27) within DateTime::TimeZone::BEGIN@28 which was called: # once (6µs+27µs) by DateTime::BEGIN@15 at line 28 use constant SHORT_NAME => 6; # spent 34µs making 1 call to DateTime::TimeZone::BEGIN@28 # spent 27µs making 1 call to constant::import
29 |  |  |  |  |
30 | 1 | 8µs |  |  | my %SpecialName = map { $_ => 1 }
31 |  |  |  |  | qw( EST MST HST CET EET MET WET EST5EDT CST6CDT MST7MDT PST8PDT );
32 |  |  |  |  |
33 |  |  |  |  | # spent 65.7ms (42.9+22.8) within DateTime::TimeZone::new which was called 3002 times, avg 22µs/call: # 3000 times (42.9ms+22.8ms) by DateTime::_new at line 235 of DateTime.pm, avg 22µs/call # once (32µs+40µs) by DateTime::Format::Alami::parse_datetime at line 78 of DateTime/Infinite.pm # once (12µs+10µs) by DateTime::Format::Alami::parse_datetime at line 103 of DateTime/Infinite.pm sub new {
34 | 3002 | 667µs |  |  | my $class = shift;
35 | 3002 | 29.9ms | 3002 | 16.5ms | my %p = validate( # spent 16.5ms making 3002 calls to Params::Validate::XS::validate, avg 6µs/call
36 |  |  |  |  | @_,
37 |  |  |  |  | { name => { type => SCALAR } },
38 |  |  |  |  | );
39 |  |  |  |  |
40 | 3002 | 2.29ms |  |  | 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 | 12.3ms | 3002 | 1.28ms | unless ( $p{name} =~ m{/} # spent 1.28ms making 3002 calls to DateTime::TimeZone::CORE:match, avg 427ns/call
48 |  |  |  |  | || $SpecialName{ $p{name} } ) {
49 | 3002 | 732µs | 2 | 14µs | if ( $p{name} eq 'floating' ) { # spent 14µs making 2 calls to Class::Singleton::instance, avg 7µs/call
50 |  |  |  |  | return DateTime::TimeZone::Floating->instance;
51 |  |  |  |  | }
52 |  |  |  |  |
53 | 3000 | 373µs |  |  | if ( $p{name} eq 'local' ) {
54 |  |  |  |  | return DateTime::TimeZone::Local->TimeZone();
55 |  |  |  |  | }
56 |  |  |  |  |
57 | 3000 | 13.5ms | 3000 | 5.00ms | if ( $p{name} eq 'UTC' || $p{name} eq 'Z' ) { # spent 5.00ms 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 | 30.6ms |  |  | # spent 6.31ms within DateTime::TimeZone::is_floating which was called 13000 times, avg 485ns/call: # 13000 times (6.31ms+0s) by DateTime::_handle_offset_modifier at line 332 of DateTime.pm, avg 485ns/call sub is_floating {0}
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 | 5µs |  |  | 1;
554 |  |  |  |  |
555 |  |  |  |  | # ABSTRACT: Time zone object base class and factory
556 |  |  |  |  |
557 |  |  |  |  | __END__
|  |  |  |  | # spent 1.28ms within DateTime::TimeZone::CORE:match which was called 3002 times, avg 427ns/call: # 3002 times (1.28ms+0s) by DateTime::TimeZone::new at line 47, avg 427ns/call sub DateTime::TimeZone::CORE:match; # opcode