[Fwd: DateTime::Duration comparisions]
This was originally to Dave directly. A second mail with a tentative patch that somewhat "fixes" the problem discussed below follows in a second mail (that I also sent to Dave directly and will forward separately). Please comment but CC me, as I don't follow the list! -max Original Message Subject: DateTime::Duration comparisions Date: Tue, 11 Nov 2003 14:49:04 +0100 From: Max Maischein <[EMAIL PROTECTED]> To: [EMAIL PROTECTED] Hello Dave, I just dabbled a bit with DateTime, comparisions and DateTime::Durations, and I found some things that I don't understand or did not find in the documentation: The delta_* methods are not really usefull, since in 90% of the time, I just want to know if one duration is longer than another - having to do several comparisions just to find that out is tedious. I saw in the CHANGES file that you added some other delta_* methods, but didn't find them in the documentation, so I don't know about these, and was too lazy to delve into the source for this :-) I wrote a test file that shows some ugly bugs when dealing with my naive comparision, most notably that ($dt-$dt)->is_zero() is false. I don't know why you implemented is_zero() that way, so I can't propose any good change. My approach would be to make is_zero() not only check the sign but also check whether all values are zero. I wrote a simple DateTime::Duration comparision and hacked it into DateTime::Duration, but my tests fail and I don't understand enough of the DateTime mechanics to fix them. I saw some references to the DateTime::Duration problems on the mailing list, but I saw that you considered removing the comparision operators completely from DateTime::Duration - I think this would be bad, but I only ended up looking at DateTime::Duration, as I could not find an easy way to look at two DateTime objects and find out which one was later, as comparision for those also isn't implemented (or, once again, I'm too stupid to read the docs properly). Can you please take a look at my comparision patch, the included tests (especially the is_zero() test is bothering me), and tell me where I am wrong :-) Thanks for writing DateTime, -max --- DateTime.pm sub _cmp_datetime { my ($left,$right,$reverse) = @_; ($left,$right) = ($right,$left) if $reverse; my $diff = $right - $left; warn sprintf "Left is %s days and %s minutes.\n", $left->delta_days, $left->delta_minutes; warn sprintf "Right is %s days and %s minutes.\n", $right->delta_days, $right->delta_minutes; warn "positive" if $diff->is_positive; warn "negative" if $diff->is_negative; return 0 if $diff->is_zero; return 1 if $diff->is_positive; return -1 if $diff->is_negative; die "This should never happen"; }; --- The test file --- #!/usr/bin/perl -w use strict; use Test::More tests => 7; use DateTime; use Data::Dumper; my $one_hour = DateTime::Duration->new( hours => 1 ); my $one_day_plus_something = DateTime->from_epoch(epoch => 24*3600+19*60) - DateTime->from_epoch( epoch => 0); ok( $one_day_plus_something > $one_hour, "One day plus something takes longer than one hour" ) or dump_duration( $one_day_plus_something, $one_hour); is( $one_hour, $one_hour, "One hour equals itself" ) or dump_duration($one_hour); ok( $one_hour == $one_hour, "One hour equals itself (via ==)" ) or dump_duration($one_hour); is( $one_day_plus_something, $one_day_plus_something, "One day plus something equals itself" ) or dump_duration($one_day_plus_something); ok( ($one_day_plus_something - $one_day_plus_something)->is_zero, "A difference subtracted from itself is zero" ) or dump_duration($one_day_plus_something - $one_day_plus_something); ok( $one_day_plus_something == $one_day_plus_something, "One day plus something equals itself (via ==)" ) or dump_duration($one_day_plus_something); ok( $one_hour < $one_day_plus_something, "One hour is shorter than one day plus something" ); sub dump_duration { diag Dumper($_) for @_; };
Re: [Fwd: DateTime::Duration comparisions]
On Tue, 11 Nov 2003, Max Maischein wrote: > I saw in the CHANGES file that you added some other delta_* methods, but > didn't find them in the documentation, so I don't know about these, and > was too lazy to delve into the source for this :-) These are for DateTime objects. I'm now regretting the fact that both classes have delta_* methods. Sigh ... > I wrote a test file that shows some ugly bugs when dealing with my naive > comparision, most notably that ($dt-$dt)->is_zero() is false. I don't > know why you implemented is_zero() that way, so I can't propose any good > change. My approach would be to make is_zero() not only check the sign > but also check whether all values are zero. Yeah, the is_* bits for durations are all screwed up. I'm considering removing them, because I'm not sure we can fix them and still do interesting thing with durations. > DateTime::Duration problems on the mailing list, but I saw that you > considered removing the comparision operators completely from > DateTime::Duration - I think this would be bad, but I only ended up DateTime::Duration has never had comparison methods or overloading, so this would be difficult to remove ;) > looking at DateTime::Duration, as I could not find an easy way to look > at two DateTime objects and find out which one was later, as comparision > for those also isn't implemented (or, once again, I'm too stupid to read > the docs properly). Now I'm really confused. DateTime.pm objects have _always_ had a compare method, plus they overload comparison operators. -dave /*=== House Absolute Consulting www.houseabsolute.com ===*/
Re: [Fwd: DateTime::Duration comparisions]
(I am too stupid - I forgot to CC the list) Original Message Subject: Re: [Fwd: DateTime::Duration comparisions] Date: Thu, 13 Nov 2003 10:16:25 +0100 From: Max Maischein <[EMAIL PROTECTED]> To: Dave Rolsky <[EMAIL PROTECTED]> References: <[EMAIL PROTECTED]> <[EMAIL PROTECTED]> Dave Rolsky wrote: I wrote a test file that shows some ugly bugs when dealing with my naive comparision, most notably that ($dt-$dt)->is_zero() is false. I don't know why you implemented is_zero() that way, so I can't propose any good change. My approach would be to make is_zero() not only check the sign but also check whether all values are zero. Yeah, the is_* bits for durations are all screwed up. I'm considering removing them, because I'm not sure we can fix them and still do interesting thing with durations. At least the is_zero thing can easily be fixed. For the other is_* methods, only my ugly approximation or some other threshold-based approach can be made. I now understand some of the problems you face, but I still think that a ::Duration should DWIM as far as possible. looking at DateTime::Duration, as I could not find an easy way to look at two DateTime objects and find out which one was later, as comparision for those also isn't implemented (or, once again, I'm too stupid to read the docs properly). Now I'm really confused. DateTime.pm objects have _always_ had a compare method, plus they overload comparison operators. Somehow I must have been really stupid that day, as now, DateTime comparision works ... perl -MDateTime -le "$a=DateTime->from_epoch(epoch=>0);$b=DateTime->now(); print $a>$b?'a>b':''; print $a<$b?'a I think my problem arose because I tried to check whether the distance between two points in time was larger than 30 minutes, which meant that I had to compare two DateTime::Duration objects, which isn't/wasn't possible. More on this in the next mail :-) -max
Re: [Fwd: DateTime::Duration comparisions]
I guess this boils down to four choices: 1) give an arbitrary answer 2) give a reasonable answer that may depend on the current time (add both durations to the current time and compare the resulting times) 3) give an answer if it is correct for all times, throw an exception otherwise 4) always throw an exception The current behavior is (1). I still favor (2), because it never throws an exception. The problem with (3) is that if you write code that compares arbitrary durations, it will work most of the time (e.g. during testing) but will occasionally throw an exception (e.g. once you're in the wild). It also seems more complicated to implement and document, though maybe not by much. Jon - Original Message - From: "Doug Treder" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Cc: "Jonathan Swartz" <[EMAIL PROTECTED]>; "Dave Rolsky" <[EMAIL PROTECTED]>; "Max Maischein" <[EMAIL PROTECTED]>; "datetime @ perl . org" <[EMAIL PROTECTED]> Sent: Friday, November 14, 2003 4:49 PM Subject: Re: [Fwd: DateTime::Duration comparisions] > The "fuzzy logic" that was being described earlier on the list is more > attractive. There are definitely edge cases where two durations cannot > be compared and should throw exception. But most of the spectrum is > actually comparable, depending on the units being compared: 30 seconds > is always less than 31 seconds, 1 day is always less than 1 month. In > fact just taking the worst case for every unit still leaves most of the > spectrum comparable. You can determine a span of "worst fuzziness" for > each unit (months are fuzzy for at least 3 days, possibly 4) and throw > exceptions if the duration comes within that range. > > -D > > > [EMAIL PROTECTED] wrote: > > >Jonathan Swartz <[EMAIL PROTECTED]> wrote: > > > > > >>Yes, that is technically true. However, it > >> > >> > >is -so- intuitive and > > > > > >>tempting to > >>compare two durations (because other > >> > >> > >mathemetical operators work, and > > > > > >>because you can compare two datetimes), > >> > >> > >that IMO it would be > > > > > >>appropriate to > >>specifically overload those operators to > >> > >> > >throw an error. Or to have > > > > > >>it > >>compare them anchored at the current time. > >> > >> > >Anything but letting it > > > > > >>compare > >>the reference values. > >> > >> > > > >I think Jon has a very astute point here. I > >used to just compare durations without > >thinking. It was only when I started getting > >strange errors that I looked through the > >module and realised there was no comparison > >overloads. > > > >If you look at 'closest' in the old > >Event::Easter you'll see this. Unfortunately > >the tests provided returned the right thing > >when comparing the references. Of course this > >was just a fluke and only allowed the tests > >to pass. > > > >I would prefer to see these overloads die, > >preferably painfully, so that people realise > >they can't overload duration comparissons. > > > >However, at the same time, I'd like to > >implement $dtdur->compare($dtdur2, $basedt) > >which would return the normal -1, 0 or 1 from > >$basedt. If $basedt is not set, then > >DateTime->now is used. > > > >This would mean we could die with > >"DateTime::Duration cannot overload > >comparison operators. For more information > >see perldoc DateTime::Duration. To compare > >durations see the compare method in the same > >document". > > > >Cheers! > >Rick > > > > > > > > > > > >
Re: [Fwd: DateTime::Duration comparisions]
On Fri, 14 Nov 2003, Jonathan Swartz wrote: > I guess this boils down to four choices: > > 1) give an arbitrary answer > 2) give a reasonable answer that may depend on the current time (add both > durations to the current time and compare the resulting times) > 3) give an answer if it is correct for all times, throw an exception > otherwise > 4) always throw an exception > > The current behavior is (1). I still favor (2), because it never throws an > exception. The problem with (3) is that if you write code that compares > arbitrary durations, it will work most of the time (e.g. during testing) but > will occasionally throw an exception (e.g. once you're in the wild). It also > seems more complicated to implement and document, though maybe not by much. I agree. Working quietly most of the time and occasionally throwing an exception that will be hard to explain ("Duration exceeds fuzzy comparison limits") is not a good behavior. I don't mind the idea of adding a compare() class method that accepts a base datetime and uses DateTime->now, per Rick's suggestion. Overloading comparison to explicitly die also seems reasonable. It's annoying the first time it happens, but as I said in my "Industrial Strength Perl" presentation, it's better to die before you've screwed up all your data than afterwards. -dave /*=== House Absolute Consulting www.houseabsolute.com ===*/
Re: [Fwd: DateTime::Duration comparisions]
On Fri, 14 Nov 2003, Dave Rolsky wrote: > I don't mind the idea of adding a compare() class method that accepts a > base datetime and uses DateTime->now, per Rick's suggestion. I proposed something just short of this *months* ago and nobody even responded to my RFC. DT::Q could be either used by this 'compare' class or be extended to include comparison operators. Two DT::Q objects could be compared deterministically. -J -- > From [EMAIL PROTECTED] Fri Nov 14 17:40:01 2003 > Date: Sun, 17 Aug 2003 16:02:09 -1000 (HST) > From: Joshua Hoblitt <[EMAIL PROTECTED]> > To: DateTime <[EMAIL PROTECTED]> > Subject: [RFC] DateTime::Quantitative was Re: Subtraction Broken? > > I'm not too sure about the name. I wanted to make sure it couldn't be confused with > DateTime::Duration and still be meaningful. > > Is this the best approach? I can't see this type of functionality being cleanly > implemented in DT::Duration. > > -J > > -- > DateTime::Quantitative - returns duration values normalized to a fixed point in time > > ->new( datetime => $dt, duration => $dtd ) > > Like DT::Duration it would provide: > > ->years; > ->months; > ->weeks; > ->days; > ->hours; > ->minutes; > ->seconds; > ->nanoseconds; > ->sign; > ->is_positive > ->is_zero > ->is_negative > > In addition: > > ->clone > > ->datetime > ->duration > > returns the component object > > If DT::Q is mutable (might not be a good idea) it would also include: > > ->set_datetime( object => $dt ) > ->set_duration( object => $dtd ) > > updates the component object > > No math operators would be overloaded but perhaps stringification would be allowed. >
Re: [Fwd: DateTime::Duration comparisions]
Doug Treder wrote: > The "fuzzy logic" that was being described earlier on the list is more > attractive. There are definitely edge cases where two durations cannot > be compared and should throw exception. But most of the spectrum is > actually comparable, depending on the units being compared: 30 seconds > is always less than 31 seconds, 1 day is always less than 1 month. In > fact just taking the worst case for every unit still leaves most of the > spectrum comparable. You can determine a span of "worst fuzziness" for > each unit (months are fuzzy for at least 3 days, possibly 4) and throw > exceptions if the duration comes within that range. I really, really, really don't like this. If I get two durations I want to be able to compare them or not compare them. I don't want to have to handle the 'fuzzy' bits. Here's how you'd have to handle 'might compare': my $cmp = eval{ ($dtdurr1 < $dtdurr2) }; if ($@) { $cmp = DateTime->now()->add_durration( $dt_durr1 ) < DateTime->now()->add_durration( $dt_durr2 ); $cmp_from_now++; } if ($cmp) { print "dtdurr2 is the larger"; print ($cmp_from_now) ? " using now() as a base" : " for any base"; } __END__ However, to compare with compare() is much easier: if ($dtdurr1->compare($dtdurr2) == -1) { print "dtdurr2 is the larger using now() as a base" } And, to make comparing easier, we could add is_less_than and is_greater_than and is_equal_to: sub is_greater_than { (return shift->compare( @_ ) == 1) } sub is_less_than{ (return shift->compare( @_ ) == -1) } sub is_equal_to { (return shift->compare( @_ ) == 0) } Of course, you could always write DateTime::Utils::DurationFuzzyCompare which could either take a Duration for its new() parameter, or could be Class::ClassDecorator on top of DateTime::Duration. Cheers! Rick
Re: [Fwd: DateTime::Duration comparisions]
Dave Rolsky wrote: I guess this boils down to four choices: 1) give an arbitrary answer 2) give a reasonable answer that may depend on the current time (add both durations to the current time and compare the resulting times) 3) give an answer if it is correct for all times, throw an exception otherwise 4) always throw an exception The current behavior is (1). I still favor (2), because it never throws an exception. The problem with (3) is that if you write code that compares arbitrary durations, it will work most of the time (e.g. during testing) but will occasionally throw an exception (e.g. once you're in the wild). It also seems more complicated to implement and document, though maybe not by much. I agree. Working quietly most of the time and occasionally throwing an exception that will be hard to explain ("Duration exceeds fuzzy comparison limits") is not a good behavior. I agree, while it would be explainable in hindsight, it's not good to have scripts blow up because they hit boundaries, even if the programmers should rethink his algorithm... I don't mind the idea of adding a compare() class method that accepts a base datetime and uses DateTime->now, per Rick's suggestion. I wouldn't want to use DateTime->now(), but a global DateTime object that is initialized at script startup - that way you always get consistent comparisions throughout the run of the script (much like -M). Overloading comparison to explicitly die also seems reasonable. It's annoying the first time it happens, but as I said in my "Industrial Strength Perl" presentation, it's better to die before you've screwed up all your data than afterwards. Overloading comparision to die might be sensible if it were clear that the situations where comparision is meaningless were happening often, but I'm not convinced - in most cases, there is a sensible way of comparing two DT::Ds even without a reference point in time, and with a reference point in time the dubious situations become even fewer - so I'm in favour of (2), as this solves the problem I am interested in (and what good are durations if you can't look at two and decide which one is longer?) -max PS: I didn't know that this was some hornets nest I stirred up again - it was just a problem I wanted to solve for myself ...
Re: [Fwd: DateTime::Duration comparisions]
On Sat, 15 Nov 2003, Max Maischein wrote: > PS: I didn't know that this was some hornets nest I stirred up again - > it was just a problem I wanted to solve for myself ... Almost every facet of DateTime stuff is a hornet's nest. It seems simple at first, but the deeper you get into it, the weirder it becomes. -dave /*=== House Absolute Consulting www.houseabsolute.com ===*/