From: Bill Erickson Date: Wed, 22 Mar 2017 19:02:58 +0000 (-0400) Subject: LP#1635737 Due date calculation honors variable durations X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=186d55f56a92afddd4dc7289d6c1fbe1d7eafc8b;p=working%2FEvergreen.git LP#1635737 Due date calculation honors variable durations Due date calculations now take time changes, variable length months, and variable length (leap) years into account. For example, a duration of "1 month" results in a due date with the same day the following month, instead of simply adding 365/12=~30 days to today. Note, in cases where a matching day in the following month (or year) does not exist (e.g. Jan 31 + "1 month"), days are added to the result to accommodate the extra days. Jan 31 + "1 month" yields March 3rd in a non-leap year. Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm index a8c6c9ff63..b30dc1544f 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm @@ -5,6 +5,7 @@ use base qw/OpenILS::Application/; use OpenSRF::Utils::Cache; use OpenSRF::Utils::Logger qw/$logger/; use OpenILS::Utils::ModsParser; +use OpenSRF::Utils qw/:datetime/; use OpenSRF::EX qw(:try); use OpenILS::Event; use Data::Dumper; @@ -2384,6 +2385,35 @@ sub verify_migrated_user_password { $e, $user_id, md5_hex($salt . $md5_pass), $pw_type); } +# Adds an interval amount to a date, taking variable length durations +# into account, for example days across time changes boundaries, +# number of days in a month, and number of days in a (leap) year. +# If no date is provided, it defaults to NOW(). +sub add_interval { + my $class = shift; + my $interval = shift; + my $date = shift || DateTime->now(time_zone => 'local'); + + # Pass the interval value through a cycle of interval_to_seconds() + # and seconds_to_interval() to normalize the interval string. + my $seconds = OpenSRF::Utils->interval_to_seconds($interval); + $interval = OpenSRF::Utils->seconds_to_interval($seconds); + + # Then chop the interval up into components DateTime::add() understands. + # ::add() expects all lower case, plural duration components. + + my %parts; + for my $component (split(/, /, $interval)) { + my ($number, $part) = split(/ /, $component); + $part = lc($part); + $part .= 's' unless ($part =~ /s$/); # force plural + $parts{$part} = $number; + } + $date->add(\%parts); + + return $date; +} + 1; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm index 74e425b393..be2498682a 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm @@ -2083,8 +2083,7 @@ sub create_due_date { my $due_date = DateTime->now(time_zone => 'local'); $due_date = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($start_time)) if $start_time; - # add the circ duration - $due_date->add(seconds => OpenSRF::Utils->interval_to_seconds($duration)); + $due_date = $U->add_interval($duration, $due_date); if($date_ceiling) { my $cdate = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($date_ceiling));