I never had any luck finding anything to give me the future DST changes for
a timezone so I worked out how to get it on my own, but recently I upgraded
from something like DateTime 0.36 to 0.74 and something really strange is
happening.

In my code I set the date to the start of the year and then use an
undocumented internal timezone call _span_for_datetime to get the next
change,  then I pull utc_rd_as_seconds one subtracted from the other allows
me to add the proper seconds to move forward to the next change, store it,
then roll and repeat until I have what I want. Now however the second
change in 2012 is coming up one second short, even though the offsets are
the same, and it seems to be other timezones as well, not just New York.
Asia/Gaza for instance.

Using the old code I get "2012-03-11T03:00:00 2012-11-04T01:00:00" for New
York,
but the new DateTime gives me "2012-03-11T03:00:00 2012-11-04T01:59:59",
one second before the actual change.

Is this a bug in my code or something weird in DT. Of course I'm using
various calls that are unsupported externally so I shouldn't be surprised
when things go south, but the numbers are the same between one and the
other so maybe I'm doing something wrong, wouldn't surprise me. I even
tried converting to utc before doing my math and then back as the perldoc
suggests with no joy.

A slightly simplified version of my code follows... I've found a workaround
but would like to understand the problem.

use DateTime;
# pass year timezone
print "@{get_dst_changes($ARGV[0],$ARGV[1])}\n";

sub get_dst_changes {
    my $year = shift;
    my $tz   = shift;
    my ( $DT, $DT_end );

    eval { $DT = DateTime->new( year => $year, time_zone => $tz ); };
    return if $@;
    $DT_end = $DT->clone();
    $DT_end->add( years => 1 );

    my $i = 0;
    my @out;
    my $DT2;
    while ( $i++ < 100 ) {    #in case we start looping for some reason
        my $span = $DT->{tz}->_span_for_datetime( 'utc', $DT );
        my $seconds = $DT->utc_rd_as_seconds();

        if ( $span->[UTC_END] =~ /\+/ ) {
            last;
        }
        my $change = $span->[UTC_END] - $seconds;

        # Find the offset and add it. Now we have a DateTime object AT DST
change
        $DT->add( seconds => $change );

        $DT2 = $DT->clone();
        $DT->add( seconds => 1 ); # One second past allows us to search for
the next DST change
        last unless $DT < $DT_end;

        push @out, $DT2;
    }
    \@out;
}

Reply via email to