Skip Menu |

Preferred bug tracker

Please visit the preferred bug tracker to report your issue.

This queue is for tickets about the DateTime CPAN distribution.

Report information
The Basics
Id: 55453
Status: resolved
Priority: 0/
Queue: DateTime

People
Owner: Nobody in particular
Requestors: cpan [...] punch.net
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: 0.54



Subject: Comparing DateTime Objects to iso8601 strings
Hello, Attached is a patch to allow iso8601 strings to be compared to datetime objects. It also includes tests and documentation patches. Thanks, Tom
Subject: datetime-0.53.overload.patch
diff -Nair DateTime-0.53/lib/DateTime.pm DateTime-0.53.old/lib/DateTime.pm 1767,1768c1767 < < unless --- > return unless 1771,1774c1770 < ) < { < return "$dt1" eq "$dt2"; < } --- > ); 3374,3375c3370 < compare a DateTime.pm compares the string with the output from the < C<iso8601()> method. --- > compare a DateTime.pm always returns false. diff -Nair DateTime-0.53/t/29overload.t DateTime-0.53.old/t/29overload.t 5c5 < use Test::More tests => 14; --- > use Test::More tests => 12; 20,21d19 < ok( $dt eq '2050-01-15T20:10:10', 'stringification overloading comparison with "eq"' ); < ok( $dt ne '2050-01-15T20:10:11', 'stringification overloading comparison with "ne"' );
From: cpan [...] punch.net
Sorry, previous patch was backwards. Attached is update for correctness. Tom
Subject: datetime-0.53.overload.patch
diff -Nair DateTime-0.53.old/lib/DateTime.pm DateTime-0.53/lib/DateTime.pm 1767c1767,1768 < return unless --- > > unless 1770c1771,1774 < ); --- > ) > { > return "$dt1" eq "$dt2"; > } 3370c3374,3375 < compare a DateTime.pm always returns false. --- > compare a DateTime.pm compares the string with the output from the > C<iso8601()> method. diff -Nair DateTime-0.53.old/t/29overload.t DateTime-0.53/t/29overload.t 5c5 < use Test::More tests => 12; --- > use Test::More tests => 14; 19a20,21 > ok( $dt eq '2050-01-15T20:10:10', 'stringification overloading comparison with "eq"' ); > ok( $dt ne '2050-01-15T20:10:11', 'stringification overloading comparison with "ne"' );
Here is an expansion of Tom's patch. It is a comprehensive fix for string comparisons of DateTime vs non-DateTime (including other objects and plain strings). This will make DT objects masquerade properly as date strings and remove a great surprise for the unsuspecting user. * eq, ne, lt, gt and cmp now work between a DT and non-DT. * Docs have been updated to reflect this. * Docs now explain that DT objects do not numify. * Docs recommend sorting mixed DT/non-DT lists of dates using eq. Unlike Tom's patch, I've made the change happen in the overload specific methods rather than in compare(). That keeps the change confined to overloading where it is obviously needed. I don't know if it makes sense to push it down into compare(). Upcoming fixes to Test::More will cause is() to do the equivalent of C<< $have eq $want >> instead of C<< "$have" eq "$want" >>. This will cause havoc with modules that use DateTime and innocently do C<< is( $dt, "1234-12-12T01:23:45" ); >>. See Test::More 0.95_01. Test::More has a tendency to stringify before comparing, but it is considered a misfeature and is slowly going away. I'll hold off releasing Test::More with the eq change until DT is updated, but I recommend this fix be considered urgent. Sorry for the fixage.
Oh yeah, the patch.
Subject: 0001-Comprehensive-fix-for-DT-v-non-DT-string-overloading.patch
From 950dcafb63b0ded068ed8b4a6596ff2f1a62419c Mon Sep 17 00:00:00 2001 From: Michael G. Schwern <schwern@pobox.com> Date: Thu, 11 Mar 2010 10:43:47 -0800 Subject: [PATCH] Comprehensive fix for DT v non-DT string overloading. Make eq, ne and cmp work for non-DateTime objects and plain strings. Change the docs to reflect this fact. Document that DateTime objects do not numify. Document the recommended way to sort mixed DT and nonDT objects. --- lib/DateTime.pm | 39 ++++++++++++++++++++++++++++----------- t/29overload.t | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/lib/DateTime.pm b/lib/DateTime.pm index 48c267b..d4cc5c8 100644 --- a/lib/DateTime.pm +++ b/lib/DateTime.pm @@ -56,7 +56,7 @@ use Params::Validate qw( validate validate_pos SCALAR BOOLEAN HASHREF OBJECT ); # use overload ( 'fallback' => 1, '<=>' => '_compare_overload', - 'cmp' => '_compare_overload', + 'cmp' => '_string_compare_overload', '""' => '_stringify', '-' => '_subtract_overload', '+' => '_add_overload', @@ -1728,6 +1728,19 @@ sub _compare_overload return $_[2] ? - $_[0]->compare( $_[1] ) : $_[0]->compare( $_[1] ); } +sub _string_compare_overload { + my($dt1, $dt2, $flip) = @_; + my $sign = $flip ? -1 : 1; + + # One is a DateTime object, one isn't. Just stringify and compare. + if( !DateTime::Helpers::can( $dt2, 'utc_rd_values' ) ) { + return $sign * ("$dt1" cmp "$dt2"); + } + else { + goto $dt1->can('_compare_overload'); + } +} + sub compare { shift->_compare( @_, 0 ); @@ -1793,10 +1806,10 @@ sub _string_equals_overload { my ( $class, $dt1, $dt2 ) = ref $_[0] ? ( undef, @_ ) : @_; - return unless - ( DateTime::Helpers::can( $dt1, 'utc_rd_values' ) - && DateTime::Helpers::can( $dt2, 'utc_rd_values' ) - ); + # One is a DateTime object, one isn't. Just stringify and compare. + if( !DateTime::Helpers::can( $dt2, 'utc_rd_values' ) ) { + return "$dt1" eq "$dt2"; + } $class ||= ref $dt1; return ! $class->compare( $dt1, $dt2 ); @@ -3401,13 +3414,17 @@ Additionally, the fallback parameter is set to true, so other derivable operators (+=, -=, etc.) will work properly. Do not expect increment (++) or decrement (--) to do anything useful. -If you attempt to sort DateTime objects with non-DateTime.pm objects -or scalars (strings, number, whatever) then an exception will be -thrown. Using the string comparison operators, C<eq> or C<ne>, to -compare a DateTime.pm always returns false. +The string comparison operators, C<eq> or C<ne>, will use the string +value to compare with non-DateTime objects. -The module also overloads stringification to use the C<iso8601()> -method. +DateTime objects do not have a numeric value, using C<==> or C<< <=> +>> to compare a DateTime object with a non-DateTime object will result +in an exception. To safely sort mixed DateTime and non-DateTime +objects, use C<sort { $a cmp $b } @dates>. + +The module also overloads stringification using the object's +formatter, defaulting to C<iso8601()> method. See L<Formatters And +Stringification> for details. =head2 Formatters And Stringification diff --git a/t/29overload.t b/t/29overload.t index 71be162..df04885 100644 --- a/t/29overload.t +++ b/t/29overload.t @@ -2,7 +2,7 @@ use strict; -use Test::More tests => 12; +use Test::More tests => 29; use DateTime; @@ -16,7 +16,50 @@ use DateTime; my $dt = DateTime->new( year => 2050, month => 1, day => 15, hour => 20, minute => 10, second => 10 ); - is( "$dt", '2050-01-15T20:10:10', 'stringification overloading' ); + my $before_string = '2050-01-15T20:10:09'; + my $dt_string = '2050-01-15T20:10:10'; + my $after_string = '2050-01-15T20:10:11'; + + is( "$dt", $dt_string, 'stringification overloading' ); + ok( $dt eq $dt_string, 'eq overloading true' ); + ok( !($dt eq $after_string), 'eq overloading false' ); + ok( $dt ne $after_string, 'ne overloading true' ); + ok( !($dt ne $dt_string), 'ne overloading false' ); + + is( $dt cmp $dt_string, 0, 'cmp overloading' ); + is( $dt cmp $after_string, -1, ' less than' ); + ok( $dt lt $after_string, 'lt overloading' ); + ok( !($dt lt $dt_string), ' not' ); + + { + package Other::Date; + use overload + q[""] => sub { return ${$_[0]}; }, + fallback => 1; + sub new { + my($class, $date) = @_; + return bless \$date, $class + } + } + + my $same = Other::Date->new($dt_string); + my $after = Other::Date->new($after_string); + my $before = Other::Date->new($before_string); + ok $dt eq $same, "DateTime eq non-DateTime overloaded object true"; + ok !($dt eq $after), " eq false"; + ok $dt ne $after, " ne true"; + ok !($dt ne $same), " ne false"; + + is( $dt cmp $same, 0, 'cmp overloading' ); + is( $dt cmp $after, -1, ' lt overloading' ); + ok( $dt lt $after, 'lt overloading' ); + ok( !($dt lt $same), ' not' ); + + is_deeply( + [sort { $a cmp $b } $same, $after, $before, $dt, $dt_string, $after_string, $before_string], + [$before, $before_string, $dt, $same, $dt_string, $after, $after_string], + "eq sort" + ); eval { my $x = $dt + 1 }; like( $@, qr/Cannot add 1 to a DateTime object/, -- 1.7.0