Subject: | Inconsistent parsedate() handling of invalid date / times during DST |
When using parsedate() on invalid date time during DST the expected
behavior is not working as it does in other circumstances. This problem
only presents itself during the spring ahead event.
Example:
Show quoted text
> TZ=US/Eastern perl -e 'use Time::ParseDate; print parsedate("2011-03-
13 02:30:00")."\n";'
1299997800
Show quoted text> TZ=US/Eastern perl -e 'use Time::CTime; print strftime("%Y-%m-%d
%H:%M:%S", localtime(1299997800))."\n";'
2011-03-13 01:30:00
The reason I am stating this is inconsistent with parsedate's behavior
is based upon what parsedate does with other invalid date times.
Example: February 31st
Show quoted text> TZ=US/Eastern perl -e 'use Time::ParseDate; print parsedate("2011-02-
31 00:00:00")."\n";'
1299128400
Show quoted text> TZ=US/Eastern perl -e 'use Time::CTime; print strftime("%Y-%m-%d
%H:%M:%S", localtime(1299128400))."\n";'
2011-03-03 00:00:00
Example: 30:00:00
Show quoted text> TZ=US/Eastern perl -e 'use Time::ParseDate; print parsedate("2011-01-
01 30:00:00")."\n";'
1293966000
Show quoted text> TZ=US/Eastern perl -e 'use Time::CTime; print strftime("%Y-%m-%d
%H:%M:%S", localtime(1293966000))."\n";'
2011-01-02 06:00:00
During DST spring forward event, parsedate() of invalid datetime results
in a unix timestamp that is in the past instead of in the future. I
have attached a patch so that parsedate's handling of this DST event
becomes consistent with the rest of the parsedate's handling of invalid
date times.
Subject: | parsedate.diff |
diff --git a/lib/Time/ParseDate.pm b/lib/Time/ParseDate.pm
index c46b732..fb2693a 100644
--- a/lib/Time/ParseDate.pm
+++ b/lib/Time/ParseDate.pm
@@ -371,7 +371,7 @@ sub parsedate
unless ($options{GMT}) {
if ($options{ZONE}) {
$tzadj = tz_offset($options{ZONE}, $secs) || 0;
- $tzadj = tz_offset($options{ZONE}, $secs-$tzadj);
+ my $tzadj2 = tz_offset($options{ZONE}, $secs-$tzadj);
unless (defined($tzadj)) {
return (undef, "could not convert '$options{ZONE}' to time offset")
if wantarray();
@@ -380,14 +380,42 @@ sub parsedate
print "adjusting secs for $options{ZONE}: $tzadj\n" if $debug;
$secs -= $tzadj;
} else {
- $tzadj = tz_local_offset($secs);
+ my $tzadj = tz_local_offset($secs);
print "adjusting secs for local offset: $tzadj\n" if $debug;
#
# Just in case we are very close to a time
# change...
#
- $tzadj = tz_local_offset($secs-$tzadj);
- $secs -= $tzadj;
+ my $tzadj2 = tz_local_offset($secs-$tzadj);
+
+ #
+ # To provide consistent handling of invalid
+ # times, this forces the time forward when
+ # provided a date / time within the
+ # perceived spring forward event of DST
+ # (example: from EST to EDT parsedate('02:30:00')
+ # does not exist but turns to '01:30:00',
+ # instead of going to 03:30:00, similar to
+ # the behavior of parsedate('2011-02-31') which
+ # would be 2011-03-03).
+ #
+ my $use_original_offset = 0;
+ if ($tzadj2 > $tzadj) {
+ for (my $i = $secs - $tzadj; $i >= $secs - $tzadj2; $i--) {
+ my $t_tzadj = tz_local_offset($i);
+ ## it changed in here some where.
+ if ($t_tzadj == $tzadj) {
+ $use_original_offset = 1;
+ last;
+ }
+ }
+ }
+
+ if ($use_original_offset) {
+ $secs -= $tzadj;
+ } else {
+ $secs -= $tzadj2;
+ }
}
}
}
diff --git a/t/datetime.t b/t/datetime.t
index 416ebc8..568dc58 100755
--- a/t/datetime.t
+++ b/t/datetime.t
@@ -310,6 +310,8 @@ BEGIN {
1012550400, ['2/1/02', NOW => 1011252345, FUZZY => 1, PREFER_FUTURE => 1],
1011247200, ['6am', GMT => 1, NOW => 1011252345],
1256435700, ['2009-10-25 02:55:00', ZONE => 'MEZ'],
+ 1300001400, ['2011-03-13 02:30:00', ZONE => 'EST'],
+ 1301189400, ['2011-03-27 03:30:00', ZONE => 'EET'],
);
%tztests = (
@@ -494,3 +496,26 @@ while (@sdt) {
}
$c++;
}
+
+# handling of spring foward events, invalid times.
+
+$ENV{TZ} = 'US/Eastern';
+tzset;
+if (parsedate('2011-03-13 02:30:00') == 1300001400) {
+ $c++;
+ print "ok $c # US/Eastern spring ahead event, invalid time\n";
+} else {
+ $c++;
+ print "not ok $c # US/Eastern spring ahead event, invalid time\n";
+}
+
+$ENV{TZ} = 'Europe/Athens';
+tzset;
+if (parsedate('2011-03-27 03:30:00') == 1301189400) {
+ $c++;
+ print "ok $c # Europe/Athens spring ahead event, invalid time\n";
+} else {
+ $c++;
+ print "not ok $c # Europe/Athens spring ahead event, invalid time\n";
+}
+