Skip to content

Commit

Permalink
offset_for_local_datetime: Add a 'ignore_missing_spans' argument
Browse files Browse the repository at this point in the history
If this is true and the local time for the given $dt object does
not exist in the time zone (due to DST changes for example), the previous
span will be returned.
  • Loading branch information
wolfsage committed Apr 25, 2018
1 parent 48a08b9 commit aac937e
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 5 deletions.
23 changes: 18 additions & 5 deletions lib/DateTime/TimeZone.pm
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ sub offset_for_datetime {
sub offset_for_local_datetime {
my $self = shift;

my $span = $self->_span_for_datetime( 'local', $_[0] );
my $span = $self->_span_for_datetime( 'local', $_[0], $_[1] );

return $span->[OFFSET];
}
Expand All @@ -210,6 +210,7 @@ sub _span_for_datetime {
my $self = shift;
my $type = shift;
my $dt = shift;
my $ignore_missing_spans = shift;

my $method = $type . '_rd_as_seconds';

Expand All @@ -218,7 +219,7 @@ sub _span_for_datetime {
my $span;
my $seconds = $dt->$method();
if ( $seconds < $self->max_span->[$end] ) {
$span = $self->_spans_binary_search( $type, $seconds );
$span = $self->_spans_binary_search( $type, $seconds, $ignore_missing_spans );
}
else {
my $until_year = $dt->utc_year + 1;
Expand All @@ -244,7 +245,7 @@ sub _span_for_datetime {

sub _spans_binary_search {
my $self = shift;
my ( $type, $seconds ) = @_;
my ( $type, $seconds, $ignore_missing_spans ) = @_;

my ( $start, $end ) = _keys_for_type($type);

Expand Down Expand Up @@ -276,7 +277,15 @@ sub _spans_binary_search {

$i += $c;

return if $i >= $max;
if ($i >= $max) {
# No span found for this time zone? If the user has asked,
# return the previous span so the offset to utc is higher,
# effectively moving the time forward whatever the difference
# in the two spans is (typically 1 hour for DST).
return $self->{spans}[ $i - 1 ] if $ignore_missing_spans;

return;
}
}
else {

Expand Down Expand Up @@ -687,14 +696,18 @@ for the given datetime. This takes into account historical time zone
information, as well as Daylight Saving Time. The offset is
determined by looking at the object's UTC Rata Die days and seconds.
=head2 $tz->offset_for_local_datetime( $dt )
=head2 $tz->offset_for_local_datetime( $dt, [ $ignore_missing_spans ] )
Given a C<DateTime> object, this method returns the offset in seconds
for the given datetime. Unlike the previous method, this method uses
the local time's Rata Die days and seconds. This should only be done
when the corresponding UTC time is not yet known, because local times
can be ambiguous due to Daylight Saving Time rules.
If C<$ignore_missing_spans> is true and the local time for C<$dt> does not
exist in the time zone (due to DST changes for example), the next span
up will be returned.
=head2 $tz->is_dst_for_datetime( $dt )
Given a C<DateTime> object, this method returns true if the DateTime is
Expand Down
43 changes: 43 additions & 0 deletions t/23ignore-missing-spans.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use strict;
use warnings;

use lib 't/lib';
use T::RequireDateTime;

use Test::More;
use Test::Fatal;

use DateTime::TimeZone;
use Try::Tiny;

my $tz = DateTime::TimeZone->new( name => 'America/Denver' );

my $dt = DateTime->new(
year => 2018,
month => 3,
day => 11,
hour => 2,
minute => 0,
second => 0,
time_zone => 'UTC',
);

{
my $error;

try {
my $offset = $tz->offset_for_local_datetime($dt);
} catch {
$error = $_;
};

like($error, qr/invalid local time/i, 'got correct error');
}

{
my $offset = $tz->offset_for_local_datetime($dt, 1);
is($offset, -25200, 'got -7 offset (even though we should be -6)');
}

done_testing();

0 comments on commit aac937e

Please sign in to comment.