in reply to Unexplained Behavior in Date::Calc

I was waiting to see what other reponses turned up. But I strongly disagree that the results shown for Delta_YMDHMS are reasonable. This is like subtracting 1.483289 - 2.12314 and Perl returning to you "-1 + 0.3 + 0.06 + 0.0001 + 0.00004 + 0.000009". Gee, if I wanted to just pair-wise subtract at each position, I'd do that myself.

"How much time passed between these two date-times?" "Exactly 1 year less 11 months less 28 days less 22 hours less 58 minutes plus 17 seconds."

A "difference" of dates should consist of either all positive values or all negative values.

What's next? Should it be okay for a "date" module to return "19103/12/32" for the first of next year because the documentation doesn't state that it won't?

<Update> Unfortunately, my solution is incorrect. The Delta_YMDHMS is wonkier than I thought. The paragraph I quote below could explain the problem if the result was -1 year plus 8 months plus 14 hours etc. but the result was actually 1 year minus 8 months etc. So the dates were given in chronological order and Delta_YMDHMS is just wonky.

But much of Date::Calc is at least a bit wonky. When I say that I'll probably never use it, I don't mean to imply that the module is not useful. It has been heavilly used and so appears to get complex date calculations correct (even if the interface isn't one I'd praise). It was more out of surprise after having heard it recommended so many times.

It also reflects that there are other modules to choose from and much of what Date::Calc does I have no problem rolling my own solutions for. I use localtime and Time::Local -- even though the latter appears to me to have missed part of the point of C defining mktime(), that you can do date arithmatic with it because it wraps overflow and underflow ("If structure members are outside their legal interval, they will be normalized").

Yes, Date::Calc isn't limited to the "Unix epoch", but I have yet to need date calculations outside of that...

Oh, and the Compare_Vectors() function below would still be a useful addition to Date::Calc. (: </Update>


Checking the documentation myself I see that you cut off quoting it just as it is about to say:

Arguments are expected to be in chronological order to yield a (usually) positive result.
which explains the problem.

If the algorithm works wonky when the dates are in the wrong order, then that is easy to fix. For this simple fix, I need the quick-and-easy way to compare to two date-times so I checked the Date::Calc docs and was horrified. So I'll also add a sane "compare" function for date-times. Here is what Date::Calc wants you to write every time you want to compare two date-times:

use Date::Calc qw( Add_Delta_DHMS Date_to_Days ); @date1 = (2002,8,31,23,59,1); @date2 = (2002,9,1,11,30,59); @d1 = ( Date_to_Days(@date1[0..2]), ($date1[3]*60+$date1[4])*60+$dat +e1[5] ); @d2 = ( Date_to_Days(@date2[0..2]), ($date2[3]*60+$date2[4])*60+$dat +e2[5] ); @diff = ( $d1[0]-$d2[0], $d1[1]-$d2[1] ); if ($diff[0] > 0 and $diff[1] < 0) { $diff[0]--; $diff[1] += 86400; } if ($diff[0] < 0 and $diff[1] > 0) { $diff[0]++; $diff[1] -= 86400; } if ($diff[0] > 0 || $diff[0] == 0 && $diff[1] >= 0) { ... }
My word!! No wonder they haven't fixed Delta_YMDHMS!

Here is a fixed Delta_YMDHMS which is how Date::Calc should be patched to work for the sake of sanity (untested):

sub Compare_Vectors { my( $x, $y )= @_; for my $i ( 0..$#x ) { my $cmp= $x[$i] <=> ( $y[$i] || 0 ); return $cmp if $cmp; } return 0 <=> ( $y[@x] || 0 ); } sub Delta_YMDHMS { my $x= [ splice @_, 0, 6 ]; my $y= [ @_ ]; my $sign= 1; if( Compare_Vectors( $x, $y ) < 0 ) { $sign= -1; ( $x, $y )= ( $y, $x ); } return map $sign*$_, Date::Calc::Delta_YMDHMS( @$x, @$y ); }

But the main thing I learned from this exercise is that I will probably never use Date::Calc. Even with this brief exposure to it, I find the module very scary.

                - tye

Replies are listed 'Best First'.
Re: Re: Unexplained Behavior in Date::Calc (yuck)
by dragonchild (Archbishop) on Dec 02, 2003 at 14:03 UTC
    I'm going to (quietly and without strong emphasis) defend Date::Calc. I've used it on many occasions and found it to be very helpful. But, then again, I never did anything wonky with it. Mostly, it was for date offsets and I wrote my own object around it. *shrugs* Having it on hand was nice cause DateTime didn't exist yet. Now that it does, I'll probably switch. But, for the nonce, it is still on my list of modules to install whenever I build a new dev server.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.