[interchange] Fix month and year adjustment wrapping issue.
Peter Ajamian <[email protected]> Mon, 05 Jun 2017 09:17:48 +0000
| Newsgroups | gmane.comp.web.interchange.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit fb8cd269c691a4d15f154826e4d1aa02eef0540a Author: Peter Ajamian <[email protected]> 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; }