Mailing List Archive

[interchange] Fix month and year adjustment wrapping issue.
commit fb8cd269c691a4d15f154826e4d1aa02eef0540a
Author: Peter Ajamian <peter@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;
}

_______________________________________________
interchange-cvs mailing list
interchange-cvs@icdevgroup.org
http://www.icdevgroup.org/mailman/listinfo/interchange-cvs