I'm experiencing some very odd behavior with DateTime, which I'm at a loss to understand...

I have a subroutine that uses two DateTime objects. It calls delta_ms() to get the delta between the two objects, does a bit of math, then calls add() to add the delta to one of the objects. Although both objects are created with time_zone => local, occasionally the result is 4 hours ahead of what I expect (which would correspond to UTC).

Suppose the current date/time is 2009-10-23 13:15:43.
I call next_delivery_date('2009-10-23 13:15:00', 'every 1 minutes').
I would expect to get back '2009-10-23 13:16:00'. However, sometimes, I get '2009-10-23 17:16:00' instead, four hours in the future.

Here's the code (minus the handling for other schedule formats):

sub next_delivery_date {
  my ($current_delivery_date, $schedule) = @_;

  my %tmp;
  @tmp{qw/ year month day hour minute second /} =
    $current_delivery_date =~ /(\d+)/g;
  my $dt = DateTime->new(%tmp, time_zone => 'local');

  # determine the date/time for the next delivery,
  # based on the existing delivery date/time and the schedule

  my ($quantity, $unit);

  if ($schedule =~ /^every (\d+) ([a-z]+)\z/) {
    # at the specified duration

    ($quantity, $unit) = ($1, $2);
  } else {
    die "Unrecognized delivery schedule '$schedule'";
  }

  my $now = DateTime->now(time_zone => 'local');

  # jump ahead rather than doing repeated adds,
  # in case the current delivery date is far in the past
  my $delta = 0;
  if ($unit eq 'minutes') {
    $delta = $now->delta_ms($dt)->{$unit};
  } elsif ($unit eq 'days') {
    $delta = $now->delta_days($dt)->{$unit};
  }

  my $add = 0;
  if ($delta > $quantity) {
    $add = $delta - ($delta % $quantity);
  }

  $dt->add($unit => $add);

  # make sure the new delivery date is in the future
  while ($dt < $now) {
    $dt->add($unit => $quantity);
  }

  $dt->set(second => 0);

  return $dt->ymd . ' ' . $dt->hms;
}


Some background on the program: I have various delivery events that happen on specific schedules, such as 'every 1 minutes' or 'daily 08:00'. My program runs as a daemon, checking for delivery events that need to be run. For each one, it forks off a child process. The child process runs the delivery event, then calculates the next date/time that event should be run.

Anyone have any ideas what could be causing this?

Ronald

Reply via email to