Dave Rolsky wrote:

Well, when you want to know how far apart two datetimes are in duration, you'd always do that based on UTC. UTC is the "real" datetime, and the time zone adjusted datetime is just for presentation.

In other words, the duration is the actual number of hours that passed, aka the floating point number of rotations of the earth on its axis that have occurred. If it were to measure the difference based on non-UTC datetimes, the results would not have any correspondence to the real universe (at least sometimes).

Having a timezone-specific Duration object is not necessarily useful for doing further calculations, but it is useful as output of a reporting tool that must take DST values as input and output.

If you just want to know how many days have passed, that's what the delta_days method is for.

Actually, the bug I was experiencing was incorrect calculation of months passed. I was using delta_md, which in turn calls subtract_datetime. I suppose one could also make delta_months, but at some point it might be easier to implement subtract_datetime_local that wraps it all up in a Duration object.

Can you tell me what output you expect to get from this script? AFAICT, the output it gives makes sense but I think you have a bug in the orthogonal method where you add the duration to $end instead of $begin (line 20).

Yes, that was a typo. I apologize. For that matter, I also specified the wrong version in the subject line. I meant to say 1.28.

If I fix that then the two begin/end times are always the same, which according to the comment in your code is what you would expect to happen.

Even after correcting the typo, I am still seeing unusual results.
Case 3: Central DST: Spring forward ***********************************
Begin: 2005-03-01T00:00:00  End: 2005-06-01T00:00:00  Delta-Months: 2
New-Begin: 2005-03-01T01:00:00  New-End: 2005-05-31T23:00:00

Case 4: Central DST: Fall back ***********************************
Begin: 2005-09-01T00:00:00  End: 2005-12-01T00:00:00  Delta-Months: 3
New-Begin: 2005-08-31T23:00:00  New-End: 2005-12-01T01:00:00

In each case, the new value is an hour off. If it worked as you say, add_duration would only modify the underlying UTC values, and the presentation layer would convert those back to the desired local timezone as needed.

If you add 3 months to a given datetime, I would _not_ expect the time portion to ever change. So the adding definitely seems right. Adding is done only on the units specified. If you only add months, that's all that's affected. Basically it breaks it up into individual units, does the addition, and then reassembles the units into a datetime at the end.

I would expect an API that is attempting to implement arithmetic-like operations would try to preserve as many arithmetic properties as possible. Under DST, the addition and subtraction methods don't seem to match. I've attached the corrected script. If you are getting different results and think there is something wrong with my local configuration, or this truly is the intended behavior, please tell me.

The change you're referencing was done based on someone else's bug report, so it's not clear to me how to reconcile the two.

Like I said, either delta_months or subtract_datetime_local would allow for the timezone specific behavior.


- Alex Docauer <[EMAIL PROTECTED]>


#!/usr/bin/perl -w 

require DateTime;
use Data::Dumper;

# Show the time difference between the two dates.
sub compare {
  my($begin, $end) = @_;
  $diff = $end->clone()->subtract_datetime($begin);
  print("Begin: $begin  End: $end  " . 
         "Delta-Months: " . $diff->in_units('months') . "\n" . 
         Dumper($diff->deltas()));
}

# We should be able to use the difference to get back to where we started
sub orthogonal {
  my($begin, $end) = @_;
  $diff = $end->clone()->subtract_datetime($begin);
  $new_begin = $end->clone()->subtract_duration($diff);
  $new_end   = $begin->clone()->add_duration($diff);
  print("New-Begin: $new_begin  New-End: $new_end\n" ); 
}

sub case {
  print "\n$_[0] ***********************************\n";
}

case("Case 1: Floating");
$float_begin = new DateTime(year => 2005, month => 3);
$float_end   = new DateTime(year => 2005, month => 6);
compare($float_begin, $float_end);
orthogonal($float_begin, $float_end);

case("Case 2: GMT");
$gmt_begin   = new DateTime(year => 2005, month => 3, 
                            time_zone => "GMT");
$gmt_end     = new DateTime(year => 2005, month => 6, 
                            time_zone => "GMT");
compare($gmt_begin, $gmt_end);
orthogonal($gmt_begin, $gmt_end);

case("Case 3: Central DST: Spring forward");
$local_begin = new DateTime(year => 2005, month => 3, 
                            time_zone => "America/Chicago");
$local_end   = new DateTime(year => 2005, month => 6, 
                            time_zone => "America/Chicago");
compare($local_begin, $local_end);
orthogonal($local_begin, $local_end);

case("Case 4: Central DST: Fall back");
$alt_begin   = new DateTime(year => 2005, month => 9, 
                            time_zone => "America/Chicago");
$alt_end     = new DateTime(year => 2005, month => 12, 
                            time_zone => "America/Chicago");
compare($alt_begin, $alt_end);
orthogonal($alt_begin, $alt_end);


Reply via email to