Add built local::lib
[catagits/Gitalist.git] / local-lib5 / lib / perl5 / DateTime / TimeZone / OlsonDB / Zone.pm
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;