LP#1635737 Due date honors variable durations
authorBill Erickson <berickxx@gmail.com>
Thu, 23 Mar 2017 18:18:34 +0000 (14:18 -0400)
committerBill Erickson <berickxx@gmail.com>
Fri, 19 May 2017 15:09:45 +0000 (11:09 -0400)
The due date calculation now takes the length of duration components
into consideration.  For example, "1 day" may be shorter or longer than
24 hours during a time change event, "1 month" may be shorter or longer
depending on which month it is currently, etc.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm

index a8c6c9f..e77bfee 100644 (file)
@@ -4,6 +4,7 @@ use OpenILS::Application;
 use base qw/OpenILS::Application/;
 use OpenSRF::Utils::Cache;
 use OpenSRF::Utils::Logger qw/$logger/;
+use OpenSRF::Utils qw/:datetime/;
 use OpenILS::Utils::ModsParser;
 use OpenSRF::EX qw(:try);
 use OpenILS::Event;
@@ -2384,6 +2385,37 @@ sub verify_migrated_user_password {
         $e, $user_id, md5_hex($salt . $md5_pass), $pw_type);
 }
 
+# Adds an interval to a date using PG's interval addition routines.
+# This takes the varying length of different intervals into account.
+# It knows about time changes, variable length months, and leap years.
+# $date     : DateTime object
+# $interval : interval string
+# returns   : DateTime object
+sub date_plus_interval {
+    my ($self, $date, $interval, $e) = @_;
+
+    $e ||= OpenILS::Utils::CStoreEditor->new;
+
+    my $pg_date = $e->json_query({
+        from => [
+            'pg_catalog.interval_pl_timestamptz', 
+            $interval, $date->strftime('%FT%T%z')
+        ]
+    });
+
+    unless ($pg_date && @$pg_date) {
+        $logger->error("Invalid date or interval string in ".
+            "date_plus_interval(date=$date, interval=$interval)");
+        return undef;
+    }
+
+    return DateTime::Format::ISO8601->new->parse_datetime(
+        cleanse_ISO8601(
+            $pg_date->[0]->{'pg_catalog.interval_pl_timestamptz'}
+        )
+    );
+}
+
 
 1;
 
index e12828c..24e4681 100644 (file)
@@ -2077,16 +2077,10 @@ sub apply_modified_due_date {
 sub create_due_date {
     my( $self, $duration, $date_ceiling, $force_date, $start_time ) = @_;
 
-    # if there is a raw time component (e.g. from postgres), 
-    # turn it into an interval that interval_to_seconds can parse
-    $duration =~ s/(\d{2}):(\d{2}):(\d{2})/$1 h $2 m $3 s/o;
-
     # for now, use the server timezone.  TODO: use workstation org timezone
     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->date_plus_interval($due_date, $duration, $self->editor);
 
     if($date_ceiling) {
         my $cdate = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($date_ceiling));