From d47e01c87cddc2992e407969c736b9bd585e70f0 Mon Sep 17 00:00:00 2001 From: Marcel de Rooy Date: Tue, 9 May 2017 17:01:46 +0200 Subject: [PATCH] Bug 18356: Fix date calculations for yearly frequencies in Serials Content-Type: text/plain; charset=utf-8 The problem as described on BZ 18356 is a combination of an error in GetFictiveIssueNumber and GetNextDate for unit==year. [1] In GetNextDate the Add_Delta_YM calculation should be applied only to frequencies based on years per unit. In the case of multiple units per year we calculate the number of days to add. And if we have reached the end of a cycle, we correct the rounding applied in the cycle. NOTE 1: We obsolete the idea here of rebasing dates on firstacqui. In case of manual adjustments, we probably do not want it. And otherwise we do not need it anymore due to the correction at the end of a cycle. NOTE 2: The calls to Add_Delta_YM are intentionally not corrected for leap years. Say you start at 2016-02-29. If you use 1/yr or 1/2yr, you will switch to the Feb 28th in the following years. In 2020 there will be no switch to Feb 29 again; if someone should need it, please use a manual adjustment. This is probably highly exceptional. [2] In GetFictiveIssueNumber the year should be decreased by one if you have more units per year and you did not yet reach firstacqui day and month. This affects calculations in GetNextDate with irregularities. NOTE: In case of manual adjustments this calculation does not really make sense. Another report should deal with improving irregularities. Test plan: [1] Verify that both GetNextDate.t as well as GetFictiveIssueNumber.t pass. [2] Look at the prediction pattern for a few frequencies. For example: 1 iss/y, 1 iss/2y, 5 iss/y. Signed-off-by: Marcel de Rooy --- C4/Serials.pm | 52 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/C4/Serials.pm b/C4/Serials.pm index c1316f2..04e907d 100644 --- a/C4/Serials.pm +++ b/C4/Serials.pm @@ -2270,6 +2270,9 @@ depending on how many rows are in serial table. The issue number calculation is based on subscription frequency, first acquisition date, and $publisheddate. +The routine is used to skip irregularities when calculating the next issue +date (in GetNextDate) or the next issue number (in GetNextSeq). + =cut sub GetFictiveIssueNumber { @@ -2295,8 +2298,24 @@ sub GetFictiveIssueNumber { $delta = ($fa_year == $year) ? ($month - $fa_month) : ( ($year-$fa_year-1)*12 + (12-$fa_month+$month) ); - } elsif($unit eq 'year') { - $delta = $year - $fa_year; + } elsif( $unit eq 'year' ) { + ( $delta ) = N_Delta_YMD( $fa_year, $fa_month, $fa_day, $year, $month, $day ); + if( $frequency->{issuesperunit} > 1 ) { + $issueno = 1 + $delta * $frequency->{'issuesperunit'}; + my @fa_upd = Add_Delta_YM( $fa_year, $fa_month, $fa_day, $delta, 0 ); + $delta = Delta_Days( @fa_upd, $year, $month, $day ); + my $incr = int( 365 / $frequency->{issuesperunit} ); + if( $incr > 0 ) { + $delta = int( $delta / $incr ); + $delta = $delta < $frequency->{issuesperunit} ? $delta : $frequency->{issuesperunit} - 1; + } else { + $delta = $frequency->{issuesperunit} - 1; + } + $issueno += $delta; + } else { + $issueno = 1 + int( $delta / $frequency->{'unitsperissue'} ); + } + return $issueno; } if($frequency->{'unitsperissue'} == 1) { $issueno = $delta * $frequency->{'issuesperunit'} + $subscription->{'countissuesperunit'}; @@ -2372,23 +2391,24 @@ sub _get_next_date_month { sub _get_next_date_year { my ($subscription, $freqdata, $year, $month, $day) = @_; - my ($fa_year, $fa_month, $fa_day) = split /-/, $subscription->{firstacquidate}; + my @newissue; # ( yy, mm, dd ) + my $delta_days = int( 365 / $freqdata->{issuesperunit} ); - if ($subscription->{countissuesperunit} + 1 > $freqdata->{issuesperunit}){ - $subscription->{countissuesperunit} = 1; - ($year) = Add_Delta_YM($year,$month,$day, $freqdata->{"unitsperissue"},0); - $month = $fa_month; - my $days_in_month = Days_in_Month($year, $month); - $day = $fa_day <= $days_in_month ? $fa_day : $days_in_month; - } else { - # Try to guess the next day in year - my $days_in_year = Days_in_Year($year,12); #Sum the days of all the months of this year - my $delta_days = int(($days_in_year - ($fa_day - 1)) / $freqdata->{issuesperunit}); - ($year,$month,$day) = Add_Delta_Days($year, $month, $day, $delta_days); + if( $freqdata->{issuesperunit} == 1 ) { + # Add full years + @newissue = Add_Delta_YM( $year, $month, $day, $freqdata->{"unitsperissue"}, 0 ); + } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) { + # Add rounded number of days based on frequency. + @newissue = Add_Delta_Days( $year, $month, $day, $delta_days ); $subscription->{countissuesperunit}++; + } else { + # We finished a cycle of issues within a unit. + # Subtract delta * (issues - 1), add 1 year + @newissue = Add_Delta_Days( $year, $month, $day, -$delta_days * ($freqdata->{issuesperunit} - 1) ); + @newissue = Add_Delta_YM( @newissue, 1, 0 ); + $subscription->{countissuesperunit} = 1; } - - return ($year, $month, $day); + return @newissue; } =head2 GetNextDate -- 2.1.4