package DateTime::TimeZone::OlsonDB::Zone;
use strict;
use warnings;
use DateTime::TimeZone;
use DateTime::TimeZone::OlsonDB;
use DateTime::TimeZone::OlsonDB::Change;
use DateTime::TimeZone::OlsonDB::Observance;
use List::Util qw( first );
use Params::Validate qw( validate SCALAR ARRAYREF );
sub new
{
my $class = shift;
my %p = validate( @_, { name => { type => SCALAR },
observances => { type => ARRAYREF },
olson_db => 1,
}
);
my $self = { name => $p{name},
observances => $p{observances},
changes => [],
infinite_rules => {},
};
return bless $self, $class;
}
sub name { $_[0]->{name} }
sub expand_observances
{
my $self = shift;
my $odb = shift;
my $max_year = shift;
my $prev_until;
for ( my $x = 0; $x < @{ $self->{observances} }; $x++ )
{
my %p = %{ $self->{observances}[$x] };
my $rules_name = delete $p{rules};
my $last_offset_from_std =
$self->last_change ? $self->last_change->offset_from_std : 0;
my $last_offset_from_utc =
$self->last_change ? $self->last_change->offset_from_utc : 0;
my $obs =
DateTime::TimeZone::OlsonDB::Observance->new
( %p,
utc_start_datetime => $prev_until,
rules => [ $odb->rules_by_name($rules_name) ],
last_offset_from_utc => $last_offset_from_utc,
last_offset_from_std => $last_offset_from_std,
);
my $rule = $obs->first_rule;
my $letter = $rule ? $rule->letter : '';
my $change =
DateTime::TimeZone::OlsonDB::Change->new
( type => 'observance',
utc_start_datetime => $obs->utc_start_datetime,
local_start_datetime => $obs->local_start_datetime,
short_name => sprintf( $obs->format, $letter ),
observance => $obs,
$rule ? ( rule => $rule ) : (),
);
if ($DateTime::TimeZone::OlsonDB::DEBUG)
{
print "Adding observance change ...\n";
$change->_debug_output;
}
$self->add_change($change);
if ( $obs->rules )
{
$obs->expand_from_rules( $self, $max_year );
}
$prev_until =
$obs->until( $self->last_change ? $self->last_change->offset_from_std : 0 );
if ( $x == $ {
foreach my $rule ( $obs->rules )
{
if ( $rule->is_infinite )
{
$self->add_infinite_rule($rule);
}
}
}
}
}
sub add_change
{
my $self = shift;
my $change = shift;
if ( defined $change->utc_start_datetime )
{
if ( @{ $self->{changes} }
&& $self->{changes}[-1]->utc_start_datetime
&& $self->{changes}[-1]->utc_start_datetime == $change->utc_start_datetime
)
{
if ( $self->{changes}[-1]->rule && $change->observance )
{
print " Ignoring previous rule change, that starts the same time as current observance change\n\n"
if $DateTime::TimeZone::OlsonDB::DEBUG;
$self->{changes}[-1] = $change;
return;
}
die "Cannot add two different changes that have the same UTC start datetime!\n";
}
my $last_change = $self->last_change;
if ( $last_change->short_name eq $change->short_name
&& $last_change->total_offset == $change->total_offset
&& $last_change->is_dst == $change->is_dst
&& $last_change->observance eq $change->observance
)
{
my $last_rule = $last_change->rule || '';
my $new_rule = $change->rule || '';
if ( $last_rule eq $new_rule )
{
print "Skipping identical change\n" if $DateTime::TimeZone::OlsonDB::DEBUG;
return;
}
}
push @{ $self->{changes} }, $change;
}
else
{
if ( $self->{earliest} )
{
die "There can only be one earliest time zone change!";
}
else
{
$self->{earliest} = $change;
}
}
}
sub add_infinite_rule
{
$_[0]->{infinite_rules}{ $_[1] } = $_[1];
}
sub last_change { return unless @{ $_[0]->{changes} } || $_[0]->{earliest};
return ( @{ $_[0]->{changes} } ?
$_[0]->{changes}[-1] :
$_[0]->{earliest} ); }
sub sorted_changes { ( ( defined $_[0]->{earliest} ? $_[0]->{earliest} : () ),
sort { $a->utc_start_datetime <=> $b->utc_start_datetime }
@{ $_[0]->{changes} } ) }
sub infinite_rules { values %{ $_[0]->{infinite_rules} } }
1;