Commit | Line | Data |
3fea05b9 |
1 | package DateTime::TimeZone::OlsonDB::Zone; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | |
6 | use DateTime::TimeZone; |
7 | use DateTime::TimeZone::OlsonDB; |
8 | use DateTime::TimeZone::OlsonDB::Change; |
9 | use DateTime::TimeZone::OlsonDB::Observance; |
10 | |
11 | use List::Util qw( first ); |
12 | use Params::Validate qw( validate SCALAR ARRAYREF ); |
13 | |
14 | sub new |
15 | { |
16 | my $class = shift; |
17 | my %p = validate( @_, { name => { type => SCALAR }, |
18 | observances => { type => ARRAYREF }, |
19 | olson_db => 1, |
20 | } |
21 | ); |
22 | |
23 | my $self = { name => $p{name}, |
24 | observances => $p{observances}, |
25 | changes => [], |
26 | infinite_rules => {}, |
27 | }; |
28 | |
29 | return bless $self, $class; |
30 | } |
31 | |
32 | sub name { $_[0]->{name} } |
33 | |
34 | sub expand_observances |
35 | { |
36 | my $self = shift; |
37 | my $odb = shift; |
38 | my $max_year = shift; |
39 | |
40 | my $prev_until; |
41 | for ( my $x = 0; $x < @{ $self->{observances} }; $x++ ) |
42 | { |
43 | my %p = %{ $self->{observances}[$x] }; |
44 | |
45 | my $rules_name = delete $p{rules}; |
46 | |
47 | my $last_offset_from_std = |
48 | $self->last_change ? $self->last_change->offset_from_std : 0; |
49 | my $last_offset_from_utc = |
50 | $self->last_change ? $self->last_change->offset_from_utc : 0; |
51 | |
52 | my $obs = |
53 | DateTime::TimeZone::OlsonDB::Observance->new |
54 | ( %p, |
55 | utc_start_datetime => $prev_until, |
56 | rules => [ $odb->rules_by_name($rules_name) ], |
57 | last_offset_from_utc => $last_offset_from_utc, |
58 | last_offset_from_std => $last_offset_from_std, |
59 | ); |
60 | |
61 | my $rule = $obs->first_rule; |
62 | my $letter = $rule ? $rule->letter : ''; |
63 | |
64 | my $change = |
65 | DateTime::TimeZone::OlsonDB::Change->new |
66 | ( type => 'observance', |
67 | utc_start_datetime => $obs->utc_start_datetime, |
68 | local_start_datetime => $obs->local_start_datetime, |
69 | short_name => sprintf( $obs->format, $letter ), |
70 | observance => $obs, |
71 | $rule ? ( rule => $rule ) : (), |
72 | ); |
73 | |
74 | if ($DateTime::TimeZone::OlsonDB::DEBUG) |
75 | { |
76 | print "Adding observance change ...\n"; |
77 | |
78 | $change->_debug_output; |
79 | } |
80 | |
81 | $self->add_change($change); |
82 | |
83 | if ( $obs->rules ) |
84 | { |
85 | $obs->expand_from_rules( $self, $max_year ); |
86 | } |
87 | |
88 | $prev_until = |
89 | $obs->until( $self->last_change ? $self->last_change->offset_from_std : 0 ); |
90 | |
91 | # last observance |
92 | if ( $x == $#{ $self->{observances} } ) |
93 | { |
94 | foreach my $rule ( $obs->rules ) |
95 | { |
96 | if ( $rule->is_infinite ) |
97 | { |
98 | $self->add_infinite_rule($rule); |
99 | } |
100 | } |
101 | } |
102 | } |
103 | } |
104 | |
105 | sub add_change |
106 | { |
107 | my $self = shift; |
108 | my $change = shift; |
109 | |
110 | if ( defined $change->utc_start_datetime ) |
111 | { |
112 | if ( @{ $self->{changes} } |
113 | && $self->{changes}[-1]->utc_start_datetime |
114 | && $self->{changes}[-1]->utc_start_datetime == $change->utc_start_datetime |
115 | ) |
116 | { |
117 | if ( $self->{changes}[-1]->rule && $change->observance ) |
118 | { |
119 | print " Ignoring previous rule change, that starts the same time as current observance change\n\n" |
120 | if $DateTime::TimeZone::OlsonDB::DEBUG; |
121 | |
122 | $self->{changes}[-1] = $change; |
123 | |
124 | return; |
125 | } |
126 | |
127 | die "Cannot add two different changes that have the same UTC start datetime!\n"; |
128 | } |
129 | |
130 | my $last_change = $self->last_change; |
131 | |
132 | if ( $last_change->short_name eq $change->short_name |
133 | && $last_change->total_offset == $change->total_offset |
134 | && $last_change->is_dst == $change->is_dst |
135 | && $last_change->observance eq $change->observance |
136 | ) |
137 | { |
138 | my $last_rule = $last_change->rule || ''; |
139 | my $new_rule = $change->rule || ''; |
140 | |
141 | if ( $last_rule eq $new_rule ) |
142 | { |
143 | print "Skipping identical change\n" if $DateTime::TimeZone::OlsonDB::DEBUG; |
144 | |
145 | return; |
146 | } |
147 | } |
148 | |
149 | push @{ $self->{changes} }, $change; |
150 | } |
151 | else |
152 | { |
153 | if ( $self->{earliest} ) |
154 | { |
155 | die "There can only be one earliest time zone change!"; |
156 | } |
157 | else |
158 | { |
159 | $self->{earliest} = $change; |
160 | } |
161 | } |
162 | } |
163 | |
164 | sub add_infinite_rule |
165 | { |
166 | $_[0]->{infinite_rules}{ $_[1] } = $_[1]; |
167 | } |
168 | |
169 | sub last_change { return unless @{ $_[0]->{changes} } || $_[0]->{earliest}; |
170 | return ( @{ $_[0]->{changes} } ? |
171 | $_[0]->{changes}[-1] : |
172 | $_[0]->{earliest} ); } |
173 | |
174 | sub sorted_changes { ( ( defined $_[0]->{earliest} ? $_[0]->{earliest} : () ), |
175 | sort { $a->utc_start_datetime <=> $b->utc_start_datetime } |
176 | @{ $_[0]->{changes} } ) } |
177 | |
178 | sub infinite_rules { values %{ $_[0]->{infinite_rules} } } |
179 | |
180 | 1; |