Subject: | Time::Piece object and undef compare as equal using <=> |
When a Time::Piece object is compared to an undefined value using the
<=> operator (or the other derived operators such as ==) it compares as
equal. The cmp operator (and derivatives) do not have this problem, nor
do comparisons to a null string or zero.
Example code:
use Time::Piece;
$t = gmtime;
print '$t <=> undef = ' . ($t <=> undef) . "\n";
print '$t cmp undef = ' . ($t cmp undef) . "\n";
print '$t <=> "" = ' . ($t <=> '') . " \n";
print '$t cmp "" = ' . ($t cmp '') . " \n";
print '$t <=> 0 = ' . ($t <=> '') . " \n";
print '$t cmp 0 = ' . ($t cmp '') . " \n";
Results:
$t <=> undef = 0
$t cmp undef = 1
$t <=> "" = 1
$t cmp "" = 1
$t <=> 0 = 1
$t cmp 0 = 1
It showed up in a test along the lines of
if ($t == get_a_time()) { do_something() }
where 'get_a_time' returned undef to indicate that it couldn't get the
time requested (a last runtime or whatever).
The bug appears to be in the get_epochs function called by compare:
sub get_epochs {
my ($lhs, $rhs, $reverse) = @_;
if (!UNIVERSAL::isa($rhs, 'Time::Piece')) {
$rhs = $lhs->new($rhs);
}
if ($reverse) {
return $rhs->epoch, $lhs->epoch;
}
return $lhs->epoch, $rhs->epoch;
}
When $rhs is not itself a Time::Piece object, 'get_epochs' tries to
convert it to one using the 'new' constructor. Unfortunately, when
'new' is called on a Time::Piece object and passed undef it acts as a
copy constructor.
Using Time::Piece->new($rhs) instead doesn't completely solve the
problem, as for an undef $rhs 'new' returns an object set to the
current time -- so if $lhs is itself the current time, it still
compares equal. Adding a specific test works though, something like:
sub get_epochs {
my ($lhs, $rhs, $reverse) = @_;
return ($lhs->epoch, -1) if not defined($rhs);
... etc
The problem was checked in Perl versions 5.10.1 on Ubuntu, Time::Piece
versions 1.16 and 1.20, and 5.10.0 on Windows XP, Time::Piece version
1.20 (both ActiveState builds), but appears not to be OS-specific.