[interchange] Fix month and year adjustment wrapping issue.

Peter Ajamian interchange-cvs at icdevgroup.org
Mon Jun 5 09:17:48 UTC 2017


commit fb8cd269c691a4d15f154826e4d1aa02eef0540a
Author: Peter Ajamian <peter at pajamian.dhs.org>
Date:   Mon Jun 5 20:53:00 2017 +1200

    Fix month and year adjustment wrapping issue.
    
    Month and year adjustments introduced in 5.7 had a bug where adjusting the month
    to one where the target date doesn't exist caused the day to roll over into the
    following month (ex: May 31st - 1 month became May 1st instead of April 30th)
    and similarily with leapday year adjustments (Feb 29th, 2016 + 1 year), this was
    fixed to adjust to the last day of the correct month instead of rolling over
    into the next month.

 UPGRADE          |   13 +++++++++++--
 lib/Vend/Util.pm |   49 ++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 47 insertions(+), 15 deletions(-)
---
diff --git a/UPGRADE b/UPGRADE
index c5f8cc7..2740d87 100644
--- a/UPGRADE
+++ b/UPGRADE
@@ -8,8 +8,17 @@ Briefly summarized, here's what you can expect when upgrading from the
 following versions:
 
  5.10.x -- A minor bug was fixed in an edge-case usage of the [area] tag which
-	  could result in incompatibility if your code relies on the buggy
-	  behaviour.
+	   could result in incompatibility if your code relies on the buggy
+	   behaviour.
+
+	-- Month and year adjustments introduced in 5.7 had a bug where
+	   adjusting the month to one where the target date doesn't exist caused
+	   the day to roll over into the following month (ex: May 31st - 1 month
+	   became May 1st instead of April 30th) and similarily with leapday
+	   year adjustments (Feb 29th, 2016 + 1 year), this was fixed to adjust
+	   to the last day of the correct month instead of rolling over into the
+	   next month.  If your code relies on the old behavior please update
+	   it.
 
  5.6.x -- Perl 5.8.8 or newer is now generally required to run Interchange.
           See "Known Issues" below.
diff --git a/lib/Vend/Util.pm b/lib/Vend/Util.pm
index 7610e42..866ef3b 100644
--- a/lib/Vend/Util.pm
+++ b/lib/Vend/Util.pm
@@ -2490,6 +2490,27 @@ sub timecard_read {
 # optional.
 #
 sub adjust_time {
+    # We need special adjustments to take into account end of month or leap year
+    # issues in adjusting the month or year.  This sub will adjust the time
+    # passed in $time as well as kick back a unixtime of the adjusted time.
+    my $perform_adjust = sub {
+	my ($time, $adjust) = @_;
+	# Do an adjustment based on year and month first to check for issues
+	# with leap year and end of month variances.  We set isdst to -1 to
+	# avoid variances due to DST time change.
+	my @timecheck = @$time;
+	$timecheck[5] += $adjust->[5];
+	$timecheck[4] += $adjust->[4];
+	$timecheck[8] = -1;
+	my @adjusted = localtime(POSIX::mktime(@timecheck));
+	# If the day is off we need to add an additional adjustment for it.
+	$adjust->[3] -= $adjusted[3] if $adjusted[3] < $timecheck[3];
+	$time->[$_] += $adjust->[$_] for (0..5);
+	my $unixtime = POSIX::mktime(@$time);
+	@$time = localtime($unixtime);
+	return $unixtime;
+    };
+
     my ($adjust, $time, $compensate_dst) = @_;
     $time ||= time;
 
@@ -2511,6 +2532,7 @@ sub adjust_time {
     # or leave the time the same).
 
     my @times = localtime($time);
+    my @adjust = (0)x6;
     my $sign = 1;
 
     foreach my $amount ($adjust =~ /([+-]?\s*[\d\.]+\s*[a-z]*)/ig) {
@@ -2527,12 +2549,12 @@ sub adjust_time {
 	    $amount *= 7;
 	}
 
-	if ($unit =~ /^s/) { $times[0] += $amount }
-	elsif ($unit =~ /^mo/) { $times[4] += $amount } # has to come before min
-	elsif ($unit =~ /^m/) { $times[1] += $amount }
-	elsif ($unit =~ /^h/) { $times[2] += $amount }
-	elsif ($unit =~ /^d/) { $times[3] += $amount }
-	elsif ($unit =~ /^y/) { $times[5] += $amount }
+	if ($unit =~ /^s/) { $adjust[0] += $amount }
+	elsif ($unit =~ /^mo/) { $adjust[4] += $amount } # has to come before min
+	elsif ($unit =~ /^m/) { $adjust[1] += $amount }
+	elsif ($unit =~ /^h/) { $adjust[2] += $amount }
+	elsif ($unit =~ /^d/) { $adjust[3] += $amount }
+	elsif ($unit =~ /^y/) { $adjust[5] += $amount }
 
 	else {
 	    ::logError("adjust_time(): bad unit: $unit");
@@ -2546,26 +2568,27 @@ sub adjust_time {
     my @multip = (0, 60, 60, 24, 0, 12);
     my $monfrac = 0;
     foreach my $i (reverse 0..5) {
-	if ($times[$i] =~ /\./) {
+	if ($adjust[$i] =~ /\./) {
 	    if ($multip[$i]) {
-		$times[$i-1] += ($times[$i] - int $times[$i]) * $multip[$i];
+		$adjust[$i-1] += ($adjust[$i] - int $adjust[$i]) * $multip[$i];
 	    }
 
 	    elsif ($i == 4) {
 		# Fractions of a month need some really extra special handling.
-		$monfrac = $times[$i] - int $times[$i];
+		$monfrac = $adjust[$i] - int $adjust[$i];
 	    }
 
-	    $times[$i] = int $times[$i]
+	    $adjust[$i] = int $adjust[$i];
 	}
     }
 
-    $time = POSIX::mktime(@times);
+    $time = $perform_adjust->(\@times, \@adjust);
 
     # This is how we handle a fraction of a month:
     if ($monfrac) {
-	$times[4] += $monfrac > 0 ? 1 : -1;
-	my $timediff = POSIX::mktime(@times);
+	@adjust = (0)x6;
+	$adjust[4] = $monfrac > 0 ? 1 : -1;
+	my $timediff = $perform_adjust->(\@times, \@adjust);
 	$timediff = int(abs($timediff - $time) * $monfrac);
 	$time += $timediff;
     }



More information about the interchange-cvs mailing list