[open-ils-commits] [GIT] Evergreen ILS branch rel_3_1 updated. 1ca32b1ab05dd5df8b673148ee6fdf3d135e0a95

Evergreen Git git at git.evergreen-ils.org
Wed Apr 3 12:08:35 EDT 2019


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, rel_3_1 has been updated
       via  1ca32b1ab05dd5df8b673148ee6fdf3d135e0a95 (commit)
       via  5950878604ffe2d6a9fad71a3ccb5ff7b2e8a410 (commit)
       via  576bf6f3413314edbf995a500b5916dd1e7a720a (commit)
       via  152490f5ac385ba752f5ed8dd207d26bf2b7cd3f (commit)
       via  fedf81cb709292132a84e79aaf8515b334b4c28b (commit)
       via  3167d2ebf20ea5318d02b62f9692e40f66851c31 (commit)
       via  a8547404f734984f843eef4a2b086a0de36c5b39 (commit)
       via  8ffde173988c695ebdadb0dc814a65f4edb31eed (commit)
       via  2cfb9f049fcfeb0558684172947d0edf06d4ce13 (commit)
       via  6491e7e8cba0ca5d3209d52b2e7c7813ec0abe03 (commit)
       via  5a97270f80cbdd7857b3af13b9faa77c38ca22a6 (commit)
       via  c21002647b107dfed06a31641d3340835edb5169 (commit)
       via  ae98a50dd0763e378b0ebae3285ea306d24c4fb0 (commit)
       via  19c46d7ff1f43170b6298c11a56f6abfcb0993b1 (commit)
      from  f60cffcd84d87747b667759f81462aa5e7be0f82 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 1ca32b1ab05dd5df8b673148ee6fdf3d135e0a95
Author: Dan Wells <dbw2 at calvin.edu>
Date:   Tue Nov 20 12:33:49 2018 -0500

    LP#1804038 Fix-ups for older 'clense' functions
    
    Way back when, we switched from a misspelled 'clense_ISO8601' to a
    corrected 'cleanse_ISO8601' in OpenSRF.  This function has again moved,
    and is now part of OpenILS::Utils::DateTime, and is named
    'clean_ISO8601'.  This was done as part of LP bug #1552778.
    
    However, a few stragglers of the misspelling remained, so these were
    missed when the big changeover occurred.  This hopefully catches them
    all!
    
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>
    Signed-off-by: Jason Boyer <jboyer at library.in.gov>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
index 711be47e98..72599b84a3 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
@@ -4063,7 +4063,7 @@ sub _reset_password_request {
 
     # Guard against no active requests
     if ($active_requests->[0]->{'request_time'}) {
-        my $last_request = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($active_requests->[0]->{'request_time'}));
+        my $last_request = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($active_requests->[0]->{'request_time'}));
         my $now = DateTime::Format::ISO8601->new();
 
         # 3. if (num_active > throttle_threshold) and (now - last_request < 1 minute)
@@ -4170,7 +4170,7 @@ sub commit_password_reset {
 
     # Ensure we're still within the TTL for the request
     my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60;
-    my $threshold = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($aupr->[0]->request_time))->add(seconds => $aupr_ttl);
+    my $threshold = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($aupr->[0]->request_time))->add(seconds => $aupr_ttl);
     if ($threshold < DateTime->now(time_zone => 'local')) {
         $e->die_event;
         $logger->info("Password reset request needed to be submitted before $threshold");
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm
index d7ea2121d8..09680b8331 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm
@@ -830,7 +830,7 @@ sub could_capture {
                 $client->respond($bresv);
             } else {
                 my $start_time = $dt_parser->parse_datetime(
-                    clense_ISO8601($bresv->start_time)
+                    clean_ISO8601($bresv->start_time)
                 );
 
                 if ($now >= $start_time->subtract("seconds" => $elbow_room)) {
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm
index fe509e9b93..d41c688564 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm
@@ -77,7 +77,7 @@ sub _cleanse_dates {
     my $fields = shift;
 
     foreach my $field (@$fields) {
-        $item->$field(OpenSRF::Utils::clense_ISO8601($item->$field)) if $item->$field;
+        $item->$field(clean_ISO8601($item->$field)) if $item->$field;
     }
     return 0;
 }

commit 5950878604ffe2d6a9fad71a3ccb5ff7b2e8a410
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Nov 7 09:27:30 2018 -0500

    LP#1635737 Due date DST noncat thinko fix
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
index da23bf3e10..39b3a21e19 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
@@ -188,8 +188,8 @@ sub noncat_due_date {
 
     my $tz = $U->ou_ancestor_setting_value(
         $circ->circ_lib,
-        'lib.timezone',
-        $self->editor
+        'lib.timezone', 
+        $e,
     ) || 'local';
 
     my $duedate = $_dt_parser

commit 576bf6f3413314edbf995a500b5916dd1e7a720a
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Nov 6 16:22:43 2018 -0500

    LP#1635737 Due date DST-aware thinko fix
    
    Minor code refactor to fix thinko and syntax issues.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

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 5fc27fe33a..3e2e9020b1 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
@@ -2380,11 +2380,11 @@ sub create_due_date {
     ) || 'local';
 
     my $due_date = $start_time ?
-        DateTime->now(time_zone => $tz) :
-        $due_date = DateTime::Format::ISO8601
+        DateTime::Format::ISO8601
             ->new
             ->parse_datetime(clean_ISO8601($start_time))
-            ->set_time_zone($tz);
+            ->set_time_zone($tz) :
+        DateTime->now(time_zone => $tz);
 
     # add the circ duration
     $due_date->add(seconds => OpenILS::Utils::DateTime->interval_to_seconds($duration, $due_date));

commit 152490f5ac385ba752f5ed8dd207d26bf2b7cd3f
Author: Mike Rylander <mrylander at gmail.com>
Date:   Mon Jul 31 15:55:34 2017 -0400

    LP#1635737 Apply DST-aware timezone to context dates
    
    Do our best to enforce the rule required by OpenSRF's interval_to_seconds
    that when a context date is in use, and you care about DST awareness, you
    must set the timezone to a DST-aware value, e.g., 'America/New_York'. In
    most situations, 'local' will suffice for this, as the server is typically
    configured with a DST-aware timezone in its environment.  However, we will
    look for an org unit setting called 'lib.timezone' and use that where we
    can.  See LP#1705524 for info on that setting.
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    
    Conflicts:
            Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
            Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
    
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

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 b2352000f1..5fc27fe33a 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
@@ -2371,15 +2371,30 @@ sub apply_modified_due_date {
 sub create_due_date {
     my( $self, $duration, $date_ceiling, $force_date, $start_time ) = @_;
 
-    # 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(clean_ISO8601($start_time)) if $start_time;
+    # Look up circulating library's TZ, or else use client TZ, falling
+    # back to server TZ
+    my $tz = $U->ou_ancestor_setting_value(
+        $self->circ_lib,
+        'lib.timezone',
+        $self->editor
+    ) || 'local';
+
+    my $due_date = $start_time ?
+        DateTime->now(time_zone => $tz) :
+        $due_date = DateTime::Format::ISO8601
+            ->new
+            ->parse_datetime(clean_ISO8601($start_time))
+            ->set_time_zone($tz);
 
     # add the circ duration
     $due_date->add(seconds => OpenILS::Utils::DateTime->interval_to_seconds($duration, $due_date));
 
     if($date_ceiling) {
-        my $cdate = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($date_ceiling));
+        my $cdate = DateTime::Format::ISO8601
+            ->new
+            ->parse_datetime(clean_ISO8601($date_ceiling))
+            ->set_time_zone($tz);
+
         if ($cdate > DateTime->now and ($cdate < $due_date or $U->is_true( $force_date ))) {
             $logger->info("circulator: overriding due date with date ceiling: $date_ceiling");
             $due_date = $cdate;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
index cdf9a6fed7..da23bf3e10 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
@@ -186,7 +186,16 @@ sub noncat_due_date {
     my $otype = $e->retrieve_config_non_cataloged_type($circ->item_type) 
         or return $e->die_event;
 
-    my $duedate = $_dt_parser->parse_datetime( clean_ISO8601($circ->circ_time) );
+    my $tz = $U->ou_ancestor_setting_value(
+        $circ->circ_lib,
+        'lib.timezone',
+        $self->editor
+    ) || 'local';
+
+    my $duedate = $_dt_parser
+        ->parse_datetime( clean_ISO8601($circ->circ_time) )
+        ->set_time_zone( $tz );
+
     $duedate = $duedate
         ->add( seconds => interval_to_seconds($otype->circ_duration, $duedate) )
         ->strftime('%FT%T%z');

commit fedf81cb709292132a84e79aaf8515b334b4c28b
Author: Dan Wells <dbw2 at calvin.edu>
Date:   Fri Jul 21 14:28:16 2017 -0400

    LP#1635737 Use new OpenSRF interval_to_seconds() context
    
    Use the optional context for interval_to_seconds() to account for the
    variable length of duration components.  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.
    
    Also, remove some timestamp munging, as that happens within
    interval_to_seconds() already.
    
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    
    Conflicts:
            Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
    
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

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 9e5cf2e91d..b2352000f1 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
@@ -2371,16 +2371,12 @@ 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(clean_ISO8601($start_time)) if $start_time;
 
     # add the circ duration
-    $due_date->add(seconds => OpenILS::Utils::DateTime->interval_to_seconds($duration));
+    $due_date->add(seconds => OpenILS::Utils::DateTime->interval_to_seconds($duration, $due_date));
 
     if($date_ceiling) {
         my $cdate = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($date_ceiling));
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
index 0d576cf5d6..cdf9a6fed7 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
@@ -188,7 +188,7 @@ sub noncat_due_date {
 
     my $duedate = $_dt_parser->parse_datetime( clean_ISO8601($circ->circ_time) );
     $duedate = $duedate
-        ->add( seconds => interval_to_seconds($otype->circ_duration) )
+        ->add( seconds => interval_to_seconds($otype->circ_duration, $duedate) )
         ->strftime('%FT%T%z');
 
     my $offset = $U->storagereq(

commit 3167d2ebf20ea5318d02b62f9692e40f66851c31
Author: Mike Rylander <mrylander at gmail.com>
Date:   Tue Nov 6 15:27:23 2018 -0500

    LP#1635737: Unit tests for DST and date math
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
index 636d2ab93c..81cf90ffae 100644
--- a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
+++ b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
@@ -14,7 +14,7 @@
 # truckload to verify that everything would continue to work if
 # we turn it on across the board.
 
-use Test::More tests => 43;
+use Test::More tests => 47;
 use Test::Warn;
 use DateTime::TimeZone;
 use DateTime::Format::ISO8601;
@@ -121,6 +121,23 @@ is (OpenILS::Utils::DateTime::interval_to_seconds('1 hour'), 3600);
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 day'), 86400);
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 week'), 604800);
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 month'), 2628000);
+
+# With context, no DST change, with timezone
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 month',
+    DateTime::Format::ISO8601->new->parse_datetime('2017-02-04T23:59:59-04')->set_time_zone("America/New_York")), 2419200);
+
+# With context, with DST change, with timezone
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 month',
+    DateTime::Format::ISO8601->new->parse_datetime('2017-02-14T23:59:59-04')->set_time_zone("America/New_York")), 2415600);
+
+# With context, no DST change, no time zone
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 month',
+    DateTime::Format::ISO8601->new->parse_datetime('2017-02-04T23:59:59-04')), 2419200);
+
+# With context, with DST change, no time zone (so, not DST-aware)
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 month',
+    DateTime::Format::ISO8601->new->parse_datetime('2017-02-14T23:59:59-04')), 2419200);
+
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 year'), 31536000);
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 year 1 second'), 31536001);
 

commit a8547404f734984f843eef4a2b086a0de36c5b39
Author: Dan Wells <dbw2 at calvin.edu>
Date:   Tue Nov 6 15:13:47 2018 -0500

    LP#1635737 Add optional context to interval_to_seconds
    
    Any given interval (e.g. "1 month") can be a different amount of
    seconds depending on the context (i.e. "1 month" after February 1 is
    March 1, but "1 month" after March 1 is April 1, yet March is longer
    than February).  This affects months all the time, but also can
    affect days, hours, and even seconds once you consider DST and "leap"
    times.
    
    By giving an optional context to interval_to_seconds, you can find
    the true number of seconds in, for example, "1 month", when starting
    from "February 1" (the context).
    
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
index 05f0883bcd..1da0951f40 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
@@ -7,6 +7,7 @@ use FileHandle;
 use Digest::MD5 qw(md5 md5_hex md5_base64);
 use Exporter;
 use DateTime;
+use DateTime::Duration;
 use DateTime::Format::ISO8601;
 use DateTime::TimeZone;
 
@@ -53,7 +54,7 @@ sub AUTOLOAD {
 	return $self->{$name};
 }
 
-=head2 $thing->interval_to_seconds('interval') OR interval_to_seconds('interval')
+=head2 $thing->interval_to_seconds('interval', ['context']) OR interval_to_seconds('interval', ['context'])
 
 =head2 $thing->seconds_to_interval($seconds) OR seconds_to_interval($seconds)
 
@@ -98,31 +99,61 @@ for months (really (365 * 1d)/12 ... that may get smarter, though)
 
 for years (this is 365 * 1d)
 
+Passing in an optional 'context' (DateTime object) will give you the number of seconds for the passed interval *starting from* the given date (e.g. '1 month' from a context of 'February 1' would return the number of seconds needed to get to 'March 1', not the generic calculation of 1/12 of the seconds in a normal year).
+
 =back
 
 =cut
 sub interval_to_seconds {
-	my $self = shift;
-        my $interval = shift || $self;
+    my $class = shift; # throwaway
+    my $interval = ($class eq __PACKAGE__) ? shift : $class;
+    my $context = shift;
 
-	$interval =~ s/(\d{2}):(\d{2}):(\d{2})/ $1 h $2 min $3 s /go;
+    $interval =~ s/(\d{2}):(\d{2}):(\d{2})/ $1 h $2 min $3 s /go;
 
-        $interval =~ s/and/,/g;
-        $interval =~ s/,/ /g;
+    $interval =~ s/and/,/g;
+    $interval =~ s/,/ /g;
 
-        my $amount = 0;
+    my $amount;
+    if ($context) {
+        my $dur = DateTime::Duration->new();
+        while ($interval =~ /\s*([\+-]?)\s*(\d+)\s*(\w+)\s*/g) {
+            my ($sign, $count, $type) = ($1, $2, $3);
+            my $func = ($sign eq '-') ? 'subtract' : 'add';
+            if ($type =~ /^s/) {
+                $type = 'seconds';
+            } elsif ($type =~ /^m(?!o)/oi) {
+                $type = 'minutes';
+            } elsif ($type =~ /^h/) {
+                $type = 'hours';
+            } elsif ($type =~ /^d/oi) {
+                $type = 'days';
+            } elsif ($type =~ /^w/oi) {
+                $type = 'weeks';
+            } elsif ($type =~ /^mo/io) {
+                $type = 'months';
+            } elsif ($type =~ /^y/oi) {
+                $type = 'years';
+            }
+            $dur->$func($type => $count);
+        }
+        my $later = $context->clone->add_duration($dur);
+        $amount = $later->subtract_datetime_absolute($context)->in_units( 'seconds' );
+    } else {
+        $amount = 0;
         while ($interval =~ /\s*([\+-]?)\s*(\d+)\s*(\w+)\s*/g) {
-		my ($sign, $count, $type) = ($1, $2, $3);
-		$count = "$sign$count" if ($sign);
-                $amount += $count if ($type =~ /^s/);
-                $amount += 60 * $count if ($type =~ /^m(?!o)/oi);
-                $amount += 60 * 60 * $count if ($type =~ /^h/);
-                $amount += 60 * 60 * 24 * $count if ($type =~ /^d/oi);
-                $amount += 60 * 60 * 24 * 7 * $count if ($type =~ /^w/oi);
-                $amount += ((60 * 60 * 24 * 365)/12) * $count if ($type =~ /^mo/io);
-                $amount += 60 * 60 * 24 * 365 * $count if ($type =~ /^y/oi);
+            my ($sign, $count, $type) = ($1, $2, $3);
+            $count = "$sign$count" if ($sign);
+            $amount += $count if ($type =~ /^s/);
+            $amount += 60 * $count if ($type =~ /^m(?!o)/oi);
+            $amount += 60 * 60 * $count if ($type =~ /^h/);
+            $amount += 60 * 60 * 24 * $count if ($type =~ /^d/oi);
+            $amount += 60 * 60 * 24 * 7 * $count if ($type =~ /^w/oi);
+            $amount += ((60 * 60 * 24 * 365)/12) * $count if ($type =~ /^mo/io);
+            $amount += 60 * 60 * 24 * 365 * $count if ($type =~ /^y/oi);
         }
-        return $amount;
+    }
+    return $amount;
 }
 
 sub seconds_to_interval {

commit 8ffde173988c695ebdadb0dc814a65f4edb31eed
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Mon Sep 24 13:56:09 2018 -0400

    LP#1789442: turn of Perl taint-checking on 14-OpenILS-Utils.t
    
    This works around a bug in DateTime::TimeZone 1.63
    <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=737265> that
    causes one of the new tests to spuriously fail on Ubuntu 14.04 LTS.
    
    As the comment indicates, taint checking is not typically enabled
    when running Evergreen, but we may want to consider changing
    that should tuits arise.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
index a341f3e815..636d2ab93c 100644
--- a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
+++ b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
@@ -1,4 +1,18 @@
-#!perl -T
+#!perl
+
+# FIXME: unlike the rest of the test cases here, we're /not/ enabling
+# taint checks. The version of DateTime::TimeZone that ships with
+# Ubuntu 14.04 LTS (Trusty) has a bug where attempting to get the
+# local time zone can fail (https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=737265).
+#
+# It's arguable whether taint checking should be enabled at all in
+# the test suite. On the one hand, it is recommended practice for
+# all code that accepts external input; on the other hand, a typical
+# Evergreen installation doesn't run anything setuid/setgid that
+# would automatically trigger taint-checking. Ideally we would
+# eat our Wheaties, but we may be looking at consuming an entire
+# truckload to verify that everything would continue to work if
+# we turn it on across the board.
 
 use Test::More tests => 43;
 use Test::Warn;

commit 2cfb9f049fcfeb0558684172947d0edf06d4ce13
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Mon Sep 24 13:55:39 2018 -0400

    LP#1789442: restore column allocation for barcode input
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
index cd1a5d43a7..fdfaefe5da 100644
--- a/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
+++ b/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
@@ -1,7 +1,7 @@
 <!-- item checkout form / list -->
 
 <div class="row pad-vert">
-  <div class="col-md-5">
+  <div class="col-md-6">
     <form ng-submit="checkout(checkoutArgs)" role="form" class="form-inline">
       <div class="input-group">
 

commit 6491e7e8cba0ca5d3209d52b2e7c7813ec0abe03
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Sep 21 15:23:11 2018 -0400

    LP#1789442: fix editing due date of hourly loans in web staff
    
    This patch fixes a bug where editing the due date of an hourly
    loan would result in the time portion always getting set to 00:00.
    
    To test
    -------
    [1] Set up some daily and hourly loans in your database.
    [2] Apply the patch.
    [3] From the items out page, edit the due date for one or
        more hourly loans. Verify that the resulting due date
        is set correctly.
    [4] From the items out page, edit the due date for some
        daily loans and verify correctness. In particular,
        verify that the resulting due date is /not/ the
        following day if testing in time zones in the Americas.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
index b57ea25033..30a9110db4 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
@@ -654,6 +654,11 @@ sub set_circ_due_date {
     if (!(interval_to_seconds($circ->duration) % 86400)) { # duration is divisible by days
         my $original_date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($circ->due_date));
         my $new_date = DateTime::Format::ISO8601->new->parse_datetime($date);
+
+        # since the new date may be coming in as UTC, convert it
+        # to the same time zone as the original due date so that
+        # ->ymd is more likely to yield the expected results
+        $new_date->set_time_zone($original_date->time_zone());
         $date = clean_ISO8601( $new_date->ymd . 'T' . $original_date->strftime('%T%z') );
     }
 
diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/items_out.js b/Open-ILS/web/js/ui/default/staff/circ/patron/items_out.js
index f75b95aa64..39ba9bc5bc 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/patron/items_out.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/patron/items_out.js
@@ -320,13 +320,7 @@ function($scope , $q , $routeParams , $timeout , egCore , egUser , patronSvc ,
                     // Fire off the due-date updater for each circ.
                     // When all is done, close the dialog
                     $scope.ok = function(args) {
-                        // toISOString gives us Zulu time, so
-                        // adjust for that before truncating to date
-                        var adjust_date = new Date( $scope.args.due_date );
-                        adjust_date.setMinutes(
-                            $scope.args.due_date.getMinutes() - adjust_date.getTimezoneOffset()
-                        );
-                        var due = adjust_date.toISOString().replace(/T.*/,'');
+                        var due = $scope.args.due_date.toISOString();
                         console.debug("applying due date of " + due);
 
                         var promises = [];

commit 5a97270f80cbdd7857b3af13b9faa77c38ca22a6
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Sep 21 14:58:47 2018 -0400

    LP#1552778: add timepicker to webstaff checkout page
    
    This patch adds a timepicker to go along with the datepicker
    if the operator invokes the option to set a specific due
    date.
    
    To test
    -------
    [1] Arrange circ policies to have both hourly and daily loans.
    [2] Apply the patch.
    [3] Check out an item for an hourly loan. Verify that the
        due date and time is per policy.
    [4] Check out an item for a daily loan. Verify that the
        due date is per policy.
    [5] Check out an item for an hourly loan, but set a specific
        due date and time. Verify that the due date and time matches
        the selected value.
    [6] Check out an item for a daily loan, but set a specific
        due date. Verify that the due date matches the selected
        value; note that any time component is ignored since
        for daily loans the time gets normalized to 23:59.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
index 309c82a085..cd1a5d43a7 100644
--- a/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
+++ b/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
@@ -1,7 +1,7 @@
 <!-- item checkout form / list -->
 
 <div class="row pad-vert">
-  <div class="col-md-6">
+  <div class="col-md-5">
     <form ng-submit="checkout(checkoutArgs)" role="form" class="form-inline">
       <div class="input-group">
 
@@ -39,8 +39,8 @@
     <div class="flex-row">
       <div class="flex-cell"></div>
 
-<div class="input-group">
-<div class="input-group-btn" uib-dropdown>
+<div class="form-group row">
+<div class="col-md-3" uib-dropdown>
       <button type="button" ng-class="{'btn-success' : date_options.has_sticky_date}" class="btn btn-default" uib-dropdown-toggle>
         [% l('Date Options') %]
         <span class="caret"></span>
@@ -75,10 +75,8 @@
           [% l('Specific Due Date') %]
         </label>
       </div> -->
-      <!-- FIXME: This needs a time component as well, but type="datetime" 
-            is not yet supported by any browsers -->
-      <div>
-        <eg-date-input ng-model="checkoutArgs.due_date" ng-disabled="!date_options.has_sticky_date"></eg-date-input>
+      <div class="col-md-4">
+        <eg-date-input ng-model="checkoutArgs.due_date" ng-disabled="!date_options.has_sticky_date" show-time-picker></eg-date-input>
       </div>
 </div>
     </div>

commit c21002647b107dfed06a31641d3340835edb5169
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Sep 21 14:30:01 2018 -0400

    LP#1552778: make clean_ISO8601 recognize 'Z' as a timezone specifier
    
    Prior to this patch, clean_ISO8601 would ignore 'Z' as a timezone
    specifier (e.g., '2018-09-21T15:34:21Z') and treat it as if the
    timestamp were in the server's time zone, leading to incorrect
    results (e.g., '2018-09-21T15:34:21-04:00') unless user, client,
    and server all happen to be in UTC+0.  In particular, this allows
    date strings emitted by the JavaScript Date object's toISOString()
    method to be correctly parsed, as those strings invariably use
    'Z' as the timezone specifier.
    
    To test
    -------
    [1] Apply patch.
    [2] Verify that regression test in t/14-OpenILS-Utils.t passes.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
index 69fec7ae5e..05f0883bcd 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
@@ -253,6 +253,8 @@ sub clean_ISO8601 {
 			my $z;
 			if ($date =~ /([-+]{1})([0-9]{1,2})(?::?([0-9]{1,2}))*\s*$/o) {
 				$z = sprintf('%s%0.2d%0.2d',$1,$2,$3)
+			} elsif ($date =~ /Z\s*$/) {
+				$z = "+00:00";
 			} else {
 				$z =  DateTime::TimeZone::offset_as_string(
 					DateTime::TimeZone
diff --git a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
index ec164a7a79..a341f3e815 100644
--- a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
+++ b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
@@ -1,6 +1,6 @@
 #!perl -T
 
-use Test::More tests => 42;
+use Test::More tests => 43;
 use Test::Warn;
 use DateTime::TimeZone;
 use DateTime::Format::ISO8601;
@@ -110,14 +110,23 @@ is (OpenILS::Utils::DateTime::interval_to_seconds('1 month'), 2628000);
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 year'), 31536000);
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 year 1 second'), 31536001);
 
-# get current timezone offset for future use
-my $offset = DateTime::TimeZone::offset_as_string(
-                DateTime::TimeZone->new( name => 'local' )->offset_for_datetime(
-                    DateTime::Format::ISO8601->new()->parse_datetime('2018-09-17')
-                )
-            );
-$offset =~ s/^(.\d\d)(\d\d)+/$1:$2/;
+sub get_offset {
+    # get current timezone offset for future use
+    my $offset = DateTime::TimeZone::offset_as_string(
+                    DateTime::TimeZone->new( name => 'local' )->offset_for_datetime(
+                        DateTime::Format::ISO8601->new()->parse_datetime('2018-09-17')
+                    )
+                );
+    $offset =~ s/^(.\d\d)(\d\d)+/$1:$2/;
+    return $offset;
+}
 
 is (OpenILS::Utils::DateTime::clean_ISO8601('20180917'), '2018-09-17T00:00:00', 'plain date converted to ISO8601 timestamp');
 is (OpenILS::Utils::DateTime::clean_ISO8601('I am not a date'), 'I am not a date', 'non-date simply returned as is');
+my $offset = get_offset();
 is (OpenILS::Utils::DateTime::clean_ISO8601('20180917 08:31:15'), "2018-09-17T08:31:15$offset", 'time zone added to date/time');
+
+# force timezone to specific value to avoid a spurious
+# pass if this test happens to be run in UTC
+$ENV{TZ} = 'EST';
+is (OpenILS::Utils::DateTime::clean_ISO8601('2018-09-17T17:31:15Z'), "2018-09-17T17:31:15+00:00", 'interpret Z in timestamp correctly');

commit ae98a50dd0763e378b0ebae3285ea306d24c4fb0
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Sep 21 14:04:24 2018 -0400

    LP#1552778: add perldoc and unit tests for clean_ISO8601
    
    To test
    -------
    [1] Apply patch.
    [2] Verify new tests in t/14-OpenILS-Utils.t pass.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
index d6224800b4..69fec7ae5e 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
@@ -206,6 +206,41 @@ sub gmtime_ISO8601 {
 	return sprintf('%d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d+00:00', $y, $M, $d, $h, $m, $s);
 }
 
+=head2 clean_ISO8601($date_string)
+
+Given a date string or a date/time string in a variety of ad-hoc
+formats, returns an ISO8601-formatted date/time string.
+
+The date portion of the input is expected to consist of a four-digit
+year, followed by a two-digit month, followed by a two-digit year,
+with each part optionally separated by a hyphen.  If there is
+only a date portion, it will be normalized to use hyphens.
+
+If there is no time portion in the input, "T00:00:00" is appended
+before the results are returned.
+
+For example, "20180917" would become "2018-09-17T00:00:00".
+
+If the input does not have a recognizable date, it is simply
+returned as is.
+
+If there is a time portion, it is expected to consist of two-digit
+hour, minutes, and seconds delimited by colons.  That time is
+appended to the return with "T" separting the date and time
+portions.
+
+If there is an ISO8601-style numeric timezone offset, it is
+normalized and appended to the return. If there is no timezone
+offset supplied in the input, the offset of the server's
+time zone is append to the return. Note that as implied above,
+if only a date is supplied, the return value does not include a
+timezone offset.
+
+For example, for a server running in U.S. Eastern Daylight
+Savings time, "20180917 08:31:15" would become "2018-09-17T08:31:15-04:00".
+
+=cut
+
 sub clean_ISO8601 {
 	my $self = shift;
 	my $date = shift || $self;
diff --git a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
index dc6e2a1162..ec164a7a79 100644
--- a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
+++ b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
@@ -1,7 +1,9 @@
 #!perl -T
 
-use Test::More tests => 39;
+use Test::More tests => 42;
 use Test::Warn;
+use DateTime::TimeZone;
+use DateTime::Format::ISO8601;
 use utf8;
 
 use_ok( 'OpenILS::Utils::Configure' );
@@ -107,3 +109,15 @@ is (OpenILS::Utils::DateTime::interval_to_seconds('1 week'), 604800);
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 month'), 2628000);
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 year'), 31536000);
 is (OpenILS::Utils::DateTime::interval_to_seconds('1 year 1 second'), 31536001);
+
+# get current timezone offset for future use
+my $offset = DateTime::TimeZone::offset_as_string(
+                DateTime::TimeZone->new( name => 'local' )->offset_for_datetime(
+                    DateTime::Format::ISO8601->new()->parse_datetime('2018-09-17')
+                )
+            );
+$offset =~ s/^(.\d\d)(\d\d)+/$1:$2/;
+
+is (OpenILS::Utils::DateTime::clean_ISO8601('20180917'), '2018-09-17T00:00:00', 'plain date converted to ISO8601 timestamp');
+is (OpenILS::Utils::DateTime::clean_ISO8601('I am not a date'), 'I am not a date', 'non-date simply returned as is');
+is (OpenILS::Utils::DateTime::clean_ISO8601('20180917 08:31:15'), "2018-09-17T08:31:15$offset", 'time zone added to date/time');

commit 19c46d7ff1f43170b6298c11a56f6abfcb0993b1
Author: Galen Charlton <gmc at equinoxinitiative.org>
Date:   Fri Sep 21 11:00:25 2018 -0400

    LP#1552778: copy some date/time utils from OpenSRF
    
    As preparation for subsequent bugfixes, this patch
    copies several date/time routines from OpenSRF::Utils
    to a new module, OpenILS::Utils::DateTime. Specifically,
    the routines copied over are:
    
    * clean_ISO8601() (renaming of the OpenSRF cleanse_ISO8601)
    * gmtime_ISO8601()
    * interval_to_seconds()
    * seconds_to_interval()
    
    This move will allow us to fix bugs in this core routines
    without requiring a mandatory OpenSRF upgrade. Furthermore,
    with the exception of interval_to_seconds() (and in only one
    place), none of those routines are used by OpenSRF itself.
    
    To test
    -------
    [1] Apply the patch.
    [2] Verify that unit tests pass.
    [3] Verify that all Perl services start correctly.
    [4] Verify that date/time and interval calculations continue
        to work as expected, particularly in circulation.
    
    Signed-off-by: Galen Charlton <gmc at equinoxinitiative.org>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    
    Conflicts:
            Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/ClosedDates.pm
    
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/offline/offline.pl b/Open-ILS/src/offline/offline.pl
index 4bc9e518a7..ae972276f4 100755
--- a/Open-ILS/src/offline/offline.pl
+++ b/Open-ILS/src/offline/offline.pl
@@ -10,7 +10,8 @@ use OpenSRF::EX qw/:try/;
 use Data::Dumper;
 use OpenILS::Utils::Fieldmapper;
 use Digest::MD5 qw/md5_hex/;
-use OpenSRF::Utils qw/:daemon cleanse_ISO8601/;
+use OpenSRF::Utils qw/:daemon/;
+use OpenILS::Utils::DateTime qw/clean_ISO8601/;
 use OpenILS::Utils::OfflineStore;
 use OpenSRF::Utils::SettingsClient;
 use OpenSRF::Utils;
@@ -723,7 +724,7 @@ sub ol_handle_checkout {
             && ! $seen_barcode{$barcode}
         ) {
             $seen_barcode{$barcode} = 1;
-            my $cts = DateTime::Format::ISO8601->parse_datetime( cleanse_ISO8601($c->status_changed_time()) )->epoch();
+            my $cts = DateTime::Format::ISO8601->parse_datetime( clean_ISO8601($c->status_changed_time()) )->epoch();
             my $xts = $command->{timestamp}; # Transaction Time Stamp
             $logger->activity("offline: ol_handle_checkout: considering status_changed_time for barcode=$barcode, cts=$cts, xts=$xts");
 
@@ -786,7 +787,7 @@ sub ol_handle_renew {
             && ! $seen_barcode{$barcode}
         ) {
             $seen_barcode{$barcode} = 1;
-            my $cts = DateTime::Format::ISO8601->parse_datetime( cleanse_ISO8601($c->status_changed_time()) )->epoch();
+            my $cts = DateTime::Format::ISO8601->parse_datetime( clean_ISO8601($c->status_changed_time()) )->epoch();
             my $xts = $command->{timestamp}; # Transaction Time Stamp
             $logger->activity("offline: ol_handle_renew: considering status_changed_time for barcode=$barcode, cts=$cts, xts=$xts");
 
@@ -843,7 +844,7 @@ sub ol_handle_checkin {
             && ! $seen_barcode{$barcode}
         ) {
             $seen_barcode{$barcode} = 1;
-            my $cts = DateTime::Format::ISO8601->parse_datetime( cleanse_ISO8601($c->status_changed_time()) )->epoch();
+            my $cts = DateTime::Format::ISO8601->parse_datetime( clean_ISO8601($c->status_changed_time()) )->epoch();
             my $xts = $command->{timestamp}; # Transaction Time Stamp
             $logger->activity("offline: ol_handle_checkin: considering status_changed_time for barcode=$barcode, cts=$cts, xts=$xts");
 
@@ -982,7 +983,7 @@ sub ol_handle_register {
     # calculate the expire date for the patron based on the profile group
     my ($grp) = grep {$_->id == $actor->profile} @$user_groups;
     if($grp) {
-        my $seconds = OpenSRF::Utils->interval_to_seconds($grp->perm_interval);
+        my $seconds = OpenILS::Utils::DateTime->interval_to_seconds($grp->perm_interval);
         my $expire_date = DateTime->from_epoch(epoch => DateTime->now->epoch + $seconds)->epoch;
 		$logger->debug("offline: setting expire date to $expire_date");
         $actor->expire_date($U->epoch2ISO8601($expire_date));
diff --git a/Open-ILS/src/perlmods/MANIFEST b/Open-ILS/src/perlmods/MANIFEST
index cb980a6e22..af568b54b5 100644
--- a/Open-ILS/src/perlmods/MANIFEST
+++ b/Open-ILS/src/perlmods/MANIFEST
@@ -135,6 +135,7 @@ lib/OpenILS/Utils/Configure.pm
 lib/OpenILS/Utils/Cronscript.pm
 lib/OpenILS/Utils/Cronscript.pm.in
 lib/OpenILS/Utils/CStoreEditor.pm
+lib/OpenILS/Utils/DateTime.pm
 lib/OpenILS/Utils/Fieldmapper.pm
 lib/OpenILS/Utils/HTTPClient.pm
 lib/OpenILS/Utils/ISBN.pm
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
index 71ed2f9899..711be47e98 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
@@ -16,7 +16,7 @@ use OpenILS::Application::AppUtils;
 use OpenILS::Utils::Fieldmapper;
 use OpenILS::Utils::ModsParser;
 use OpenSRF::Utils::Logger qw/$logger/;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::SettingsClient;
 
 use OpenSRF::Utils::Cache;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm
index 37270114fa..d7ea2121d8 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm
@@ -7,7 +7,7 @@ use POSIX qw/strftime/;
 use OpenILS::Application;
 use base qw/OpenILS::Application/;
 
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
 use OpenILS::Utils::Fieldmapper;
 use OpenILS::Application::AppUtils;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
index 3a59bf93aa..b57ea25033 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
@@ -20,7 +20,7 @@ use DateTime::Format::ISO8601;
 
 use OpenILS::Application::AppUtils;
 
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::AppSession;
 use OpenILS::Utils::ModsParser;
 use OpenILS::Event;
@@ -457,14 +457,14 @@ sub set_circ_claims_returned {
     $circ->stop_fines_time('now') unless $circ->stop_fines_time;
 
     if( $backdate ) {
-        $backdate = cleanse_ISO8601($backdate);
+        $backdate = clean_ISO8601($backdate);
 
-        my $original_date = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($circ->due_date));
+        my $original_date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($circ->due_date));
         my $new_date = DateTime::Format::ISO8601->new->parse_datetime($backdate);
         $backdate = $new_date->ymd . 'T' . $original_date->strftime('%T%z');
 
         # clean it up once again; need a : in the timezone offset. E.g. -06:00 not -0600
-        $backdate = cleanse_ISO8601($backdate);
+        $backdate = clean_ISO8601($backdate);
 
         # make it look like the circ stopped at the cliams returned time
         $circ->stop_fines_time($backdate);
@@ -611,8 +611,8 @@ sub post_checkin_backdate_circ_impl {
         $backdate and $circ->checkin_time;
 
     # update the checkin and stop_fines times to reflect the new backdate
-    $circ->stop_fines_time(cleanse_ISO8601($backdate));
-    $circ->checkin_time(cleanse_ISO8601($backdate));
+    $circ->stop_fines_time(clean_ISO8601($backdate));
+    $circ->checkin_time(clean_ISO8601($backdate));
     $e->update_action_circulation($circ) or return $e->die_event;
 
     # now void the overdues "erased" by the back-dating
@@ -649,12 +649,12 @@ sub set_circ_due_date {
         or return $e->die_event;
 
     return $e->die_event unless $e->allowed('CIRC_OVERRIDE_DUE_DATE', $circ->circ_lib);
-    $date = cleanse_ISO8601($date);
+    $date = clean_ISO8601($date);
 
     if (!(interval_to_seconds($circ->duration) % 86400)) { # duration is divisible by days
-        my $original_date = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($circ->due_date));
+        my $original_date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($circ->due_date));
         my $new_date = DateTime::Format::ISO8601->new->parse_datetime($date);
-        $date = cleanse_ISO8601( $new_date->ymd . 'T' . $original_date->strftime('%T%z') );
+        $date = clean_ISO8601( $new_date->ymd . 'T' . $original_date->strftime('%T%z') );
     }
 
     $circ->due_date($date);
@@ -712,7 +712,7 @@ sub create_in_house_use {
     }
 
     if( $use_time ne 'now' ) {
-        $use_time = cleanse_ISO8601($use_time);
+        $use_time = clean_ISO8601($use_time);
         $logger->debug("in_house_use setting use time to $use_time");
     }
 
@@ -1559,7 +1559,7 @@ sub mark_item_missing_pieces {
 
                 $logger->info('open-ils.circ.mark_item_missing_pieces: item needed for hold, shortening due date');
                 my $due_date = DateTime->now(time_zone => 'local');
-                $co_params->{'due_date'} = cleanse_ISO8601( $due_date->strftime('%FT%T%z') );
+                $co_params->{'due_date'} = clean_ISO8601( $due_date->strftime('%FT%T%z') );
             } else {
                 $logger->info('open-ils.circ.mark_item_missing_pieces: item not needed for hold');
             }
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircCommon.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircCommon.pm
index 879644f189..3b6bfe9310 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircCommon.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircCommon.pm
@@ -3,7 +3,7 @@ use strict; use warnings;
 use DateTime;
 use DateTime::Format::ISO8601;
 use OpenILS::Application::AppUtils;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenILS::Event;
 use OpenSRF::Utils::Logger qw(:logger);
 use OpenILS::Utils::CStoreEditor q/:funcs/;
@@ -50,11 +50,11 @@ sub void_or_zero_overdues {
         # turn it into an interval that interval_to_seconds can parse
         my $duration = $circ->fine_interval;
         $duration =~ s/(\d{2}):(\d{2}):(\d{2})/$1 h $2 m $3 s/o;
-        my $interval = OpenSRF::Utils->interval_to_seconds($duration);
+        my $interval = OpenILS::Utils::DateTime->interval_to_seconds($duration);
 
-        my $date = DateTime::Format::ISO8601->parse_datetime(cleanse_ISO8601($backdate));
-        my $due_date = DateTime::Format::ISO8601->parse_datetime(cleanse_ISO8601($circ->due_date))->epoch;
-        my $grace_period = extend_grace_period( $class, $circ->circ_lib, $circ->due_date, OpenSRF::Utils->interval_to_seconds($circ->grace_period), $e);
+        my $date = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($backdate));
+        my $due_date = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($circ->due_date))->epoch;
+        my $grace_period = extend_grace_period( $class, $circ->circ_lib, $circ->due_date, OpenILS::Utils::DateTime->interval_to_seconds($circ->grace_period), $e);
         if($date->epoch <= $due_date + $grace_period) {
             $logger->info("backdate $backdate is within grace period, voiding all");
         } else {
@@ -223,7 +223,7 @@ sub extend_grace_period {
     my($class, $circ_lib, $due_date, $grace_period, $e, $h) = @_;
     if ($grace_period >= 86400) { # Only extend grace periods greater than or equal to a full day
         my $parser = DateTime::Format::ISO8601->new;
-        my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $due_date ) );
+        my $due_dt = $parser->parse_datetime( clean_ISO8601( $due_date ) );
         my $due = $due_dt->epoch;
 
         my $grace_extend = $U->ou_ancestor_setting_value($circ_lib, 'circ.grace.extend');
@@ -293,7 +293,7 @@ sub extend_grace_period {
                         if ($cl and @$cl) {
                             $closed = 1;
                             foreach (@$cl) {
-                                my $cl_dt = $parser->parse_datetime( cleanse_ISO8601( $_->close_end ) );
+                                my $cl_dt = $parser->parse_datetime( clean_ISO8601( $_->close_end ) );
                                 while ($due_dt <= $cl_dt) {
                                     $due_dt->add( seconds => 86400 );
                                     $new_grace_period += 86400;
@@ -493,7 +493,7 @@ sub generate_fines {
             # each (ils) transaction is processed in its own (db) transaction
             $e->xact_begin if $commit;
 
-            my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $c->$due_date_method ) );
+            my $due_dt = $parser->parse_datetime( clean_ISO8601( $c->$due_date_method ) );
     
             my $due = $due_dt->epoch;
             my $now = time;
@@ -541,8 +541,8 @@ sub generate_fines {
     
             my $last_fine;
             if ($fine) {
-                $conn->respond( "Last billing time: ".$fine->billing_ts." (clensed format: ".cleanse_ISO8601( $fine->billing_ts ).")") if $conn;
-                $last_fine = $parser->parse_datetime( cleanse_ISO8601( $fine->billing_ts ) )->epoch;
+                $conn->respond( "Last billing time: ".$fine->billing_ts." (clensed format: ".clean_ISO8601( $fine->billing_ts ).")") if $conn;
+                $last_fine = $parser->parse_datetime( clean_ISO8601( $fine->billing_ts ) )->epoch;
             } else {
                 $logger->info( "Potential first billing for circ ".$c->id );
                 $last_fine = $due;
@@ -1051,7 +1051,7 @@ sub _has_refundable_payments {
 
     if ($last_payment->[0]) {
         my $interval_secs = interval_to_seconds($interval);
-        my $payment_ts = DateTime::Format::ISO8601->parse_datetime(cleanse_ISO8601($last_payment->[0]->payment_ts))->epoch;
+        my $payment_ts = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($last_payment->[0]->payment_ts))->epoch;
         my $now = time;
         return 1 if ($payment_ts + $interval_secs >= $now);
     }
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 c2d55186cf..9e5cf2e91d 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
@@ -385,7 +385,7 @@ use OpenSRF::Utils::Cache;
 use Digest::MD5 qw(md5_hex);
 use DateTime::Format::ISO8601;
 use OpenILS::Utils::PermitHold;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::SettingsClient;
 use OpenILS::Application::Circ::Holds;
 use OpenILS::Application::Circ::Transit;
@@ -1011,7 +1011,7 @@ sub mk_env {
             unless $U->is_true($patron->card->active);
     
         my $expire = DateTime::Format::ISO8601->new->parse_datetime(
-            cleanse_ISO8601($patron->expire_date));
+            clean_ISO8601($patron->expire_date));
     
         $self->bail_on_events(OpenILS::Event->new('PATRON_ACCOUNT_EXPIRED'))
             if( CORE::time > $expire->epoch ) ;
@@ -1190,8 +1190,8 @@ sub do_copy_checks {
                 );
 
                 if($auto_renew_intvl) {
-                    my $intvl_seconds = OpenSRF::Utils->interval_to_seconds($auto_renew_intvl);
-                    my $checkout_time = DateTime::Format::ISO8601->new->parse_datetime( cleanse_ISO8601($old_circ->xact_start) );
+                    my $intvl_seconds = OpenILS::Utils::DateTime->interval_to_seconds($auto_renew_intvl);
+                    my $checkout_time = DateTime::Format::ISO8601->new->parse_datetime( clean_ISO8601($old_circ->xact_start) );
 
                     if(DateTime->now > $checkout_time->add(seconds => $intvl_seconds)) {
                         $payload->{auto_renew} = 1;
@@ -2151,7 +2151,7 @@ sub build_checkout_circ_object {
     # if the user provided an overiding checkout time,
     # (e.g. the checkout really happened several hours ago), then
     # we apply that here.  Does this need a perm??
-    $circ->xact_start(cleanse_ISO8601($self->checkout_time))
+    $circ->xact_start(clean_ISO8601($self->checkout_time))
         if $self->checkout_time;
 
     # if a patron is renewing, 'requestor' will be the patron
@@ -2251,7 +2251,7 @@ sub booking_adjusted_due_date {
         return $self->bail_on_events($self->editor->event)
             unless $self->editor->allowed('CIRC_OVERRIDE_DUE_DATE', $self->circ_lib);
 
-       $circ->due_date(cleanse_ISO8601($self->due_date));
+       $circ->due_date(clean_ISO8601($self->due_date));
 
     } else {
 
@@ -2281,14 +2281,14 @@ sub booking_adjusted_due_date {
         return $self->bail_on_events($bookings) if ref($bookings) eq 'HASH';
         
         my $dt_parser = DateTime::Format::ISO8601->new;
-        my $due_date = $dt_parser->parse_datetime( cleanse_ISO8601($circ->due_date) );
+        my $due_date = $dt_parser->parse_datetime( clean_ISO8601($circ->due_date) );
 
         for my $bid (@$bookings) {
 
             my $booking = $self->editor->retrieve_booking_reservation( $bid );
 
-            my $booking_start = $dt_parser->parse_datetime( cleanse_ISO8601($booking->start_time) );
-            my $booking_end = $dt_parser->parse_datetime( cleanse_ISO8601($booking->end_time) );
+            my $booking_start = $dt_parser->parse_datetime( clean_ISO8601($booking->start_time) );
+            my $booking_end = $dt_parser->parse_datetime( clean_ISO8601($booking->end_time) );
 
             return $self->bail_on_events( OpenILS::Event->new('COPY_RESERVED') )
                 if ($booking_start < DateTime->now);
@@ -2309,7 +2309,7 @@ sub booking_adjusted_due_date {
             $new_circ_duration++ if $new_circ_duration % 86400 == 0;
             $circ->duration("$new_circ_duration seconds");
 
-            $circ->due_date(cleanse_ISO8601($due_date->strftime('%FT%T%z')));
+            $circ->due_date(clean_ISO8601($due_date->strftime('%FT%T%z')));
             $changed = 1;
         }
 
@@ -2331,7 +2331,7 @@ sub apply_modified_due_date {
         return $self->bail_on_events($self->editor->event)
             unless $self->editor->allowed('CIRC_OVERRIDE_DUE_DATE', $self->circ_lib);
 
-      $circ->due_date(cleanse_ISO8601($self->due_date));
+      $circ->due_date(clean_ISO8601($self->due_date));
 
    } else {
 
@@ -2377,13 +2377,13 @@ sub create_due_date {
 
     # 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;
+    $due_date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($start_time)) if $start_time;
 
     # add the circ duration
-    $due_date->add(seconds => OpenSRF::Utils->interval_to_seconds($duration));
+    $due_date->add(seconds => OpenILS::Utils::DateTime->interval_to_seconds($duration));
 
     if($date_ceiling) {
-        my $cdate = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($date_ceiling));
+        my $cdate = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($date_ceiling));
         if ($cdate > DateTime->now and ($cdate < $due_date or $U->is_true( $force_date ))) {
             $logger->info("circulator: overriding due date with date ceiling: $date_ceiling");
             $due_date = $cdate;
@@ -2464,7 +2464,7 @@ sub checkout_noncat {
 
    my $lib      = $self->noncat_circ_lib || $self->circ_lib;
    my $count    = $self->noncat_count || 1;
-   my $cotime   = cleanse_ISO8601($self->checkout_time) || "";
+   my $cotime   = clean_ISO8601($self->checkout_time) || "";
 
    $logger->info("circulator: circ creating $count noncat circs with checkout time $cotime");
 
@@ -2509,8 +2509,8 @@ sub check_transit_checkin_interval {
     # transit from X to X for whatever reason has no min interval
     return if $self->transit->source == $self->transit->dest;
 
-    my $seconds = OpenSRF::Utils->interval_to_seconds($interval);
-    my $t_start = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($self->transit->source_send_time));
+    my $seconds = OpenILS::Utils::DateTime->interval_to_seconds($interval);
+    my $t_start = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($self->transit->source_send_time));
     my $horizon = $t_start->add(seconds => $seconds);
 
     # See if we are still within the transit checkin forbidden range
@@ -3538,9 +3538,9 @@ sub handle_fines {
         # If we have a grace period
         if($obj->can('grace_period')) {
             # Parse out the due date
-            my $due_date = $dt_parser->parse_datetime( cleanse_ISO8601($obj->due_date) );
+            my $due_date = $dt_parser->parse_datetime( clean_ISO8601($obj->due_date) );
             # Add the grace period to the due date
-            $due_date->add(seconds => OpenSRF::Utils->interval_to_seconds($obj->grace_period));
+            $due_date->add(seconds => OpenILS::Utils::DateTime->interval_to_seconds($obj->grace_period));
             # Don't generate fines on circs still in grace period
             $skip_for_grace = $due_date > DateTime->now;
         }
@@ -3770,7 +3770,7 @@ sub checkin_handle_lost_or_longoverdue {
             int($tm[3]), int($tm[4]), int($tm[5]), int($tm[6]));
 
         my $last_chance = 
-            OpenSRF::Utils->interval_to_seconds($max_return) + int($due);
+            OpenILS::Utils::DateTime->interval_to_seconds($max_return) + int($due);
 
         $logger->info("MAX OD: $max_return LAST ACTIVITY: ".
             "$last_activity DUEDATE: ".$circ->due_date." TODAY: $today ".
@@ -3835,8 +3835,8 @@ sub checkin_handle_backdate {
     # not the input.  Do we need to do this?  This certainly interferes with
     # backdating of hourly checkouts, but that is likely a very rare case.
     # ------------------------------------------------------------------
-    my $bd = cleanse_ISO8601($self->backdate);
-    my $original_date = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($self->circ->due_date));
+    my $bd = clean_ISO8601($self->backdate);
+    my $original_date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($self->circ->due_date));
     my $new_date = DateTime::Format::ISO8601->new->parse_datetime($bd);
     $new_date->set_hour($original_date->hour());
     $new_date->set_minute($original_date->minute());
@@ -3847,7 +3847,7 @@ sub checkin_handle_backdate {
         $logger->info("circulator: ignoring future backdate: $new_date");
         delete $self->{backdate};
     } else {
-        $self->backdate(cleanse_ISO8601($new_date->datetime()));
+        $self->backdate(clean_ISO8601($new_date->datetime()));
     }
 
     return undef;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm
index 8efb0086ab..39c4d16255 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm
@@ -33,7 +33,7 @@ use OpenILS::Application::Circ::Transit;
 use OpenILS::Application::Actor::Friends;
 use DateTime;
 use DateTime::Format::ISO8601;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use Digest::MD5 qw(md5_hex);
 use OpenSRF::Utils::Cache;
 use OpenSRF::Utils::JSON;
@@ -269,7 +269,7 @@ sub create_hold {
     my $expire_setting = $U->ou_ancestor_setting_value($recipient->home_ou, OILS_SETTING_BLOCK_HOLD_FOR_EXPIRED_PATRON);
     if ($expire_setting) {
         my $expire = DateTime::Format::ISO8601->new->parse_datetime(
-            cleanse_ISO8601($recipient->expire_date));
+            clean_ISO8601($recipient->expire_date));
 
         push( @events, OpenILS::Event->new(
             'PATRON_ACCOUNT_EXPIRED',
@@ -588,7 +588,7 @@ sub retrieve_holds {
         } elsif($cancel_age) { # limit by age
 
             # find all of the canceled holds that were canceled within the configured time frame
-            my $date = DateTime->now->subtract(seconds => OpenSRF::Utils::interval_to_seconds($cancel_age));
+            my $date = DateTime->now->subtract(seconds => OpenILS::Utils::DateTime->interval_to_seconds($cancel_age));
             $date = $U->epoch2ISO8601($date->epoch);
             $holds_query->{where}->{cancel_time} = {'>=' => $date};
         }
@@ -1103,10 +1103,10 @@ sub set_hold_shelf_expire_time {
     return undef unless $shelf_expire;
 
     $start_time = ($start_time) ?
-        DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($start_time)) :
+        DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($start_time)) :
         DateTime->now(time_zone => 'local'); # without time_zone we get UTC ... yuck!
 
-    my $seconds = OpenSRF::Utils->interval_to_seconds($shelf_expire);
+    my $seconds = OpenILS::Utils::DateTime->interval_to_seconds($shelf_expire);
     my $expire_time = $start_time->add(seconds => $seconds);
 
     # if the shelf expire time overlaps with a pickup lib's
@@ -1117,7 +1117,7 @@ sub set_hold_shelf_expire_time {
 
     if($dateinfo) {
         my $dt_parser = DateTime::Format::ISO8601->new;
-        $expire_time = $dt_parser->parse_datetime(cleanse_ISO8601($dateinfo->{end}));
+        $expire_time = $dt_parser->parse_datetime(clean_ISO8601($dateinfo->{end}));
 
         # TODO: enable/disable time bump via setting?
         $expire_time->set(hour => '23', minute => '59', second => '59');
@@ -1305,8 +1305,8 @@ sub _hold_status {
                             dest_recv_time => {'!=' => undef},
                          })->[0];
         my $start_time = ($transit) ? $transit->dest_recv_time : $hold->capture_time;
-        $start_time    = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($start_time));
-        my $end_time   = $start_time->add(seconds => OpenSRF::Utils::interval_to_seconds($hs_wait_interval));
+        $start_time    = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($start_time));
+        my $end_time   = $start_time->add(seconds => OpenILS::Utils::DateTime->interval_to_seconds($hs_wait_interval));
 
         return 5 if $end_time > DateTime->now;
         return 4;
@@ -1456,7 +1456,7 @@ sub retrieve_hold_queue_status_impl {
         $user_org, OILS_SETTING_HOLD_ESIMATE_WAIT_INTERVAL, $e);
     my $min_wait = $U->ou_ancestor_setting_value(
         $user_org, 'circ.holds.min_estimated_wait_interval', $e);
-    $min_wait = OpenSRF::Utils::interval_to_seconds($min_wait || '0 seconds');
+    $min_wait = OpenILS::Utils::DateTime->interval_to_seconds($min_wait || '0 seconds');
     $default_wait ||= '0 seconds';
 
     # Estimated wait time is the average wait time across the set
@@ -1469,7 +1469,7 @@ sub retrieve_hold_queue_status_impl {
     for my $wait_data (@$hold_data) {
         my $count += $wait_data->{count};
         $combined_secs += $count *
-            OpenSRF::Utils::interval_to_seconds($wait_data->{avg_wait_time} || $default_wait);
+            OpenILS::Utils::DateTime->interval_to_seconds($wait_data->{avg_wait_time} || $default_wait);
         $num_potentials += $count;
     }
 
@@ -4282,7 +4282,7 @@ sub calculate_expire_time
     my $ou = shift;
     my $interval = $U->ou_ancestor_setting_value($ou, OILS_SETTING_HOLD_EXPIRE);
     if($interval) {
-        my $date = DateTime->now->add(seconds => OpenSRF::Utils::interval_to_seconds($interval));
+        my $date = DateTime->now->add(seconds => OpenILS::Utils::DateTime->interval_to_seconds($interval));
         return $U->epoch2ISO8601($date->epoch);
     }
     return undef;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm
index 4fa1d1f458..6b84a7892e 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm
@@ -32,7 +32,7 @@ use OpenILS::Utils::Penalty;
 use Business::Stripe;
 $Data::Dumper::Indent = 0;
 use OpenILS::Const qw/:const/;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use DateTime::Format::ISO8601;
 my $parser = DateTime::Format::ISO8601->new;
 
@@ -1290,7 +1290,7 @@ __PACKAGE__->register_method(
 sub _to_epoch {
     my $ts = shift @_;
 
-    return $parser->parse_datetime(cleanse_ISO8601($ts))->epoch;
+    return $parser->parse_datetime(clean_ISO8601($ts))->epoch;
 }
 
 my %_statement_sort = (
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
index 26b85d2cd7..0d576cf5d6 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/NonCat.pm
@@ -5,7 +5,7 @@ use OpenSRF::EX qw(:try);
 use Data::Dumper;
 use DateTime;
 use DateTime::Format::ISO8601;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::Logger qw(:logger);
 use OpenILS::Application::AppUtils;
 use OpenILS::Utils::Fieldmapper;
@@ -186,7 +186,7 @@ sub noncat_due_date {
     my $otype = $e->retrieve_config_non_cataloged_type($circ->item_type) 
         or return $e->die_event;
 
-    my $duedate = $_dt_parser->parse_datetime( cleanse_ISO8601($circ->circ_time) );
+    my $duedate = $_dt_parser->parse_datetime( clean_ISO8601($circ->circ_time) );
     $duedate = $duedate
         ->add( seconds => interval_to_seconds($otype->circ_duration) )
         ->strftime('%FT%T%z');
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Collections.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Collections.pm
index a377029b66..b451cbd8ee 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Collections.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Collections.pm
@@ -3,7 +3,7 @@ use strict; use warnings;
 use OpenSRF::EX qw(:try);
 use OpenILS::Application::AppUtils;
 use OpenSRF::Utils::Logger qw(:logger);
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenILS::Application;
 use OpenILS::Utils::Fieldmapper;
 use base 'OpenILS::Application';
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm
index dfe98789b0..fe509e9b93 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm
@@ -43,7 +43,7 @@ use base qw/OpenILS::Application/;
 use OpenILS::Application::AppUtils;
 use OpenILS::Event;
 use OpenSRF::AppSession;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::Logger qw/:logger/;
 use OpenILS::Utils::CStoreEditor q/:funcs/;
 use OpenILS::Utils::Fieldmapper;
@@ -1060,7 +1060,7 @@ sub make_prediction_values {
         $logger->debug('make_prediction_values reviving holdings: ' . OpenSRF::Utils::JSON->perl2JSON($predict_from_siss));
         $options->{predict_from} = _revive_holding($predict_from_siss->holding_code, $caption_field, 1); # fresh MFHD Record, so we simply default to 1 for seqno
         if ($fake_chron_needed) {
-            $options->{faked_chron_date} = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($predict_from_siss->date_published));
+            $options->{faked_chron_date} = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($predict_from_siss->date_published));
         }
         $logger->debug('make_prediction_values predicting with options: ' . OpenSRF::Utils::JSON->perl2JSON($options));
         push( @predictions, _generate_issuance_values($mfhd, $options) );
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm
index 0d3c4118e6..941085133d 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm
@@ -4,7 +4,7 @@ use warnings;
 package OpenILS::Application::Storage::Driver::Pg::QueryParser;
 use OpenILS::Application::Storage::QueryParser;
 use base 'QueryParser';
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::JSON;
 use OpenILS::Application::AppUtils;
 use OpenILS::Utils::CStoreEditor;
@@ -755,7 +755,7 @@ __PACKAGE__->add_search_modifier( 'metabib' );
 package OpenILS::Application::Storage::Driver::Pg::QueryParser::query_plan;
 use base 'QueryParser::query_plan';
 use OpenSRF::Utils::Logger qw($logger);
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use Data::Dumper;
 use OpenILS::Application::AppUtils;
 use OpenILS::Utils::Normalize qw/search_normalize/;
@@ -1671,7 +1671,7 @@ sub flatten {
                             # useless use of filter
                         } else {
                             # "before $cend"
-                            $cend = cleanse_ISO8601($cend);
+                            $cend = clean_ISO8601($cend);
                             $where .= $joiner if $where ne '';
                             $where .= "bre.$datefilter <= \$_$$\$$cend\$_$$\$";
                         }
@@ -1680,14 +1680,14 @@ sub flatten {
                         if ($cstart eq '-infinity') {
                             # useless use of filter
                         } else { # "after $cstart"
-                            $cstart = cleanse_ISO8601($cstart);
+                            $cstart = clean_ISO8601($cstart);
                             $where .= $joiner if $where ne '';
                             $where .= "bre.$datefilter >= \$_$$\$$cstart\$_$$\$";
                         }
                     } else { # both supplied
                         # "between $cstart and $cend"
-                        $cstart = cleanse_ISO8601($cstart);
-                        $cend = cleanse_ISO8601($cend);
+                        $cstart = clean_ISO8601($cstart);
+                        $cend = clean_ISO8601($cend);
                         $where .= $joiner if $where ne '';
                         $where .= "bre.$datefilter BETWEEN \$_$$\$$cstart\$_$$\$ AND \$_$$\$$cend\$_$$\$";
                     }
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher.pm
index db78f30a73..dd90f177cc 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher.pm
@@ -5,6 +5,7 @@ our $VERSION = 1;
 use Digest::MD5 qw/md5_hex/;
 use OpenSRF::EX qw/:try/;
 use OpenSRF::Utils;
+use OpenILS::Utils::DateTime;
 use OpenSRF::Utils::Logger qw/:level/;
 use OpenILS::Utils::Fieldmapper;
 
@@ -112,7 +113,7 @@ sub cachable_wrapper {
         OpenSRF::Utils::Cache->new->put_cache(
             $cache_key =>
             [@res[int($cache_page * $cache_args{cache_page_size}) .. int(($cache_page + 1) * $cache_args{cache_page_size}) ]] =>
-            OpenSRF::Utils->interval_to_seconds( $cache_args{timeout} )
+            OpenILS::Utils::DateTime->interval_to_seconds( $cache_args{timeout} )
         );
     } catch Error with {
         my $e = shift;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm
index bc14a40260..120e7a6ce0 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm
@@ -3,7 +3,7 @@ use parent qw/OpenILS::Application::Storage::Publisher/;
 use strict;
 use warnings;
 use OpenSRF::Utils::Logger qw/:level :logger/;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::JSON;
 use OpenSRF::AppSession;
 use OpenSRF::EX qw/:try/;
@@ -1211,7 +1211,7 @@ sub new_hold_copy_targeter {
             $_->delete for (@oldmaps);
 
             if ($hold->expire_time) {
-                my $ex_time = $parser->parse_datetime( cleanse_ISO8601( $hold->expire_time ) );
+                my $ex_time = $parser->parse_datetime( clean_ISO8601( $hold->expire_time ) );
                 if ( DateTime->compare($ex_time, DateTime->now) < 0 ) {
 
                     # cancel cause = un-targeted expiration
@@ -1670,7 +1670,7 @@ sub process_recall {
 
         # Give the user a new due date of either a full recall threshold,
         # or the return interval, whichever is further in the future
-        my $threshold_date = DateTime::Format::ISO8601->parse_datetime(cleanse_ISO8601($circ->xact_start))->add(seconds => interval_to_seconds($recall_threshold))->iso8601();
+        my $threshold_date = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($circ->xact_start))->add(seconds => interval_to_seconds($recall_threshold))->iso8601();
         if (DateTime->compare(DateTime::Format::ISO8601->parse_datetime($threshold_date), DateTime::Format::ISO8601->parse_datetime($return_date)) == 1) {
             $return_date = $threshold_date;
         }
@@ -1749,7 +1749,7 @@ sub reservation_targeter {
 
             die "OK\n" if (!$bresv or $bresv->capture_time or $bresv->cancel_time);
 
-            my $end_time = $parser->parse_datetime( cleanse_ISO8601( $bresv->end_time ) );
+            my $end_time = $parser->parse_datetime( clean_ISO8601( $bresv->end_time ) );
             if (DateTime->compare($end_time, DateTime->now) < 0) {
 
                 # cancel cause = un-targeted expiration
@@ -1824,8 +1824,8 @@ sub reservation_targeter {
 
                     if (@$circs) {
                         my $due_date = $circs->[0]->due_date;
-                        $due_date = $parser->parse_datetime( cleanse_ISO8601( $due_date ) );
-                        my $start_time = $parser->parse_datetime( cleanse_ISO8601( $bresv->start_time ) );
+                        $due_date = $parser->parse_datetime( clean_ISO8601( $due_date ) );
+                        my $start_time = $parser->parse_datetime( clean_ISO8601( $bresv->start_time ) );
                         if (DateTime->compare($start_time, $due_date) < 0) {
                             $conflicts{$res->id} = $circs->[0]->to_fieldmapper;
                             next;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/actor.pm
index 1cef88bfd6..302c038070 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/actor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/actor.pm
@@ -2,7 +2,7 @@ package OpenILS::Application::Storage::Publisher::actor;
 use base qw/OpenILS::Application::Storage/;
 use OpenILS::Application::Storage::CDBI::actor;
 use OpenSRF::Utils::Logger qw/:level/;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenILS::Utils::Fieldmapper;
 use OpenSRF::Utils::SettingsClient;
 use OpenILS::Application::AppUtils;
@@ -263,8 +263,8 @@ sub make_closure_spanset {
 
         $spanset = $spanset->union(
             DateTime::Span->new(
-                start => $_dt_parser->parse_datetime(cleanse_ISO8601($c->{close_start})),
-                end   => $_dt_parser->parse_datetime(cleanse_ISO8601($c->{close_end}))
+                start => $_dt_parser->parse_datetime(clean_ISO8601($c->{close_start})),
+                end   => $_dt_parser->parse_datetime(clean_ISO8601($c->{close_end}))
             )
         );
     }
@@ -293,7 +293,7 @@ sub new_org_closed_overlap {
           LIMIT 1
     SQL
 
-    $date = cleanse_ISO8601($date);
+    $date = clean_ISO8601($date);
 
     my $target_date = $_dt_parser->parse_datetime( $date );
     my ($begin, $end) = ($target_date, $target_date);
@@ -312,7 +312,7 @@ sub new_org_closed_overlap {
             $begin->subtract( minutes => 1 );
 
             while ( my $_b = new_org_closed_overlap($self, $client, $ou, $begin->strftime('%FT%T%z'), -1, 1 ) ) {
-                $begin = $_dt_parser->parse_datetime( cleanse_ISO8601($_b->{start}) );
+                $begin = $_dt_parser->parse_datetime( clean_ISO8601($_b->{start}) );
             }
         }
 
@@ -320,7 +320,7 @@ sub new_org_closed_overlap {
             $end->add( minutes => 1 );
 
             while ( my $_a = new_org_closed_overlap($self, $client, $ou, $end->strftime('%FT%T%z'), 1, 1 ) ) {
-                $end = $_dt_parser->parse_datetime( cleanse_ISO8601($_a->{end}) );
+                $end = $_dt_parser->parse_datetime( clean_ISO8601($_a->{end}) );
             }
         }
     }
@@ -337,7 +337,7 @@ sub new_org_closed_overlap {
             $begin->subtract( minutes => 1 );
 
             while ( my $_b = new_org_closed_overlap($self, $client, $ou, $begin->strftime('%FT%T%z'), -1 ) ) {
-                $begin = $_dt_parser->parse_datetime( cleanse_ISO8601($_b->{start}) );
+                $begin = $_dt_parser->parse_datetime( clean_ISO8601($_b->{start}) );
             }
         }
     
@@ -348,7 +348,7 @@ sub new_org_closed_overlap {
 
 
             while ( my $_b = new_org_closed_overlap($self, $client, $ou, $end->strftime('%FT%T%z'), -1 ) ) {
-                $end = $_dt_parser->parse_datetime( cleanse_ISO8601($_b->{end}) );
+                $end = $_dt_parser->parse_datetime( clean_ISO8601($_b->{end}) );
             }
         }
     }
@@ -385,23 +385,23 @@ sub org_closed_overlap {
           LIMIT 1
     SQL
 
-    $date = cleanse_ISO8601($date);
+    $date = clean_ISO8601($date);
     my ($begin, $end) = ($date,$date);
 
     my $hoo = actor::org_unit::hours_of_operation->retrieve($ou);
 
     if (my $closure = actor::org_unit::closed_date->db_Main->selectrow_hashref( $sql, {}, $date, $ou )) {
-        $begin = cleanse_ISO8601($closure->{close_start});
-        $end = cleanse_ISO8601($closure->{close_end});
+        $begin = clean_ISO8601($closure->{close_start});
+        $end = clean_ISO8601($closure->{close_end});
 
         if ( $direction <= 0 ) {
             $before = $_dt_parser->parse_datetime( $begin );
             $before->subtract( minutes => 1 );
 
             while ( my $_b = org_closed_overlap($self, $client, $ou, $before->strftime('%FT%T%z'), -1, 1 ) ) {
-                $before = $_dt_parser->parse_datetime( cleanse_ISO8601($_b->{start}) );
+                $before = $_dt_parser->parse_datetime( clean_ISO8601($_b->{start}) );
             }
-            $begin = cleanse_ISO8601($before->strftime('%FT%T%z'));
+            $begin = clean_ISO8601($before->strftime('%FT%T%z'));
         }
 
         if ( $direction >= 0 ) {
@@ -409,9 +409,9 @@ sub org_closed_overlap {
             $after->add( minutes => 1 );
 
             while ( my $_a = org_closed_overlap($self, $client, $ou, $after->strftime('%FT%T%z'), 1, 1 ) ) {
-                $after = $_dt_parser->parse_datetime( cleanse_ISO8601($_a->{end}) );
+                $after = $_dt_parser->parse_datetime( clean_ISO8601($_a->{end}) );
             }
-            $end = cleanse_ISO8601($after->strftime('%FT%T%z'));
+            $end = clean_ISO8601($after->strftime('%FT%T%z'));
         }
     }
 
@@ -425,7 +425,7 @@ sub org_closed_overlap {
 
                 my $count = 1;
                 while ($hoo->$begin_open_meth eq '00:00:00' and $hoo->$begin_close_meth eq '00:00:00') {
-                    $begin = cleanse_ISO8601($_dt_parser->parse_datetime( $begin )->subtract( days => 1)->strftime('%FT%T%z'));
+                    $begin = clean_ISO8601($_dt_parser->parse_datetime( $begin )->subtract( days => 1)->strftime('%FT%T%z'));
                     $begin_dow++;
                     $begin_dow %= 7;
                     $count++;
@@ -438,7 +438,7 @@ sub org_closed_overlap {
                     $before = $_dt_parser->parse_datetime( $begin );
                     $before->subtract( minutes => 1 );
                     while ( my $_b = org_closed_overlap($self, $client, $ou, $before->strftime('%FT%T%z'), -1 ) ) {
-                        $before = $_dt_parser->parse_datetime( cleanse_ISO8601($_b->{start}) );
+                        $before = $_dt_parser->parse_datetime( clean_ISO8601($_b->{start}) );
                     }
                 }
             }
@@ -450,7 +450,7 @@ sub org_closed_overlap {
     
                 $count = 1;
                 while ($hoo->$end_open_meth eq '00:00:00' and $hoo->$end_close_meth eq '00:00:00') {
-                    $end = cleanse_ISO8601($_dt_parser->parse_datetime( $end )->add( days => 1)->strftime('%FT%T%z'));
+                    $end = clean_ISO8601($_dt_parser->parse_datetime( $end )->add( days => 1)->strftime('%FT%T%z'));
                     $end_dow++;
                     $end_dow %= 7;
                     $count++;
@@ -464,9 +464,9 @@ sub org_closed_overlap {
                     $after->add( minutes => 1 );
 
                     while ( my $_a = org_closed_overlap($self, $client, $ou, $after->strftime('%FT%T%z'), 1 ) ) {
-                        $after = $_dt_parser->parse_datetime( cleanse_ISO8601($_a->{end}) );
+                        $after = $_dt_parser->parse_datetime( clean_ISO8601($_a->{end}) );
                     }
-                    $end = cleanse_ISO8601($after->strftime('%FT%T%z'));
+                    $end = clean_ISO8601($after->strftime('%FT%T%z'));
                 }
             }
 
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger.pm
index 3336dcbc54..9027beb16c 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger.pm
@@ -10,7 +10,7 @@ use OpenSRF::AppSession;
 use OpenSRF::MultiSession;
 use OpenSRF::Utils::SettingsClient;
 use OpenSRF::Utils::Logger qw/$logger/;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 
 use DateTime;
 use DateTime::Format::ISO8601;
@@ -93,7 +93,7 @@ sub create_active_events_for_object {
 
             if (my $dfield = $def->delay_field) {
                 if ($target->$dfield()) {
-                    $date = DateTime::Format::ISO8601->new->parse_datetime( cleanse_ISO8601($target->$dfield) );
+                    $date = DateTime::Format::ISO8601->new->parse_datetime( clean_ISO8601($target->$dfield) );
                 } else {
                     next;
                 }
@@ -187,7 +187,7 @@ sub create_event_for_object_and_def {
 
             if (my $dfield = $def->delay_field) {
                 if ($target->$dfield()) {
-                    $date = DateTime::Format::ISO8601->new->parse_datetime( cleanse_ISO8601($target->$dfield) );
+                    $date = DateTime::Format::ISO8601->new->parse_datetime( clean_ISO8601($target->$dfield) );
                 } else {
                     next;
                 }
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
index f334f66ddd..bf6b67eb2f 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
@@ -6,7 +6,7 @@ use DateTime;
 use DateTime::Format::ISO8601;
 use Unicode::Normalize;
 use XML::LibXML;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::Logger qw(:logger);
 use OpenSRF::Utils::JSON;
 use OpenILS::Application::AppUtils;
@@ -43,7 +43,7 @@ $_TT_helpers = {
     # turns a date into something TT can understand
     format_date => sub {
         my $date = shift;
-        $date = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($date));
+        $date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($date));
         return sprintf(
             "%0.2d:%0.2d:%0.2d %0.2d-%0.2d-%0.4d",
             $date->hour,
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm
index 1a53dfbc40..f9a4d2266b 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm
@@ -2,7 +2,7 @@ package OpenILS::Application::Trigger::Validator;
 use strict; use warnings;
 use DateTime;
 use DateTime::Format::ISO8601;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::Logger qw/:logger/;
 use OpenILS::Const qw/:const/;
 use OpenILS::Application::AppUtils;
@@ -44,7 +44,7 @@ sub MinPassiveTargetAge {
         return 0; # no-op false
     }
 
-    my $delay_field_ts = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($target->$delay_field()));
+    my $delay_field_ts = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($target->$delay_field()));
 
     # to get the minimum time that the target must have aged to, add the min age to the delay field
     $delay_field_ts->add( seconds => interval_to_seconds( $env->{params}->{min_target_age} ) );
@@ -66,7 +66,7 @@ sub CircIsOverdue {
         return 0 if (!$self->MinPassiveTargetAge($env));
     }
 
-    my $due_date = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($circ->due_date));
+    my $due_date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($circ->due_date));
     return 0 if $due_date > DateTime->now;
 
     return 1;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
index e2639cc535..500efa22bc 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
@@ -23,7 +23,7 @@ use OpenSRF::AppSession;
 use OpenILS::Utils::Fieldmapper;
 use OpenSRF::Utils::SettingsClient;
 use OpenILS::Application::AppUtils;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use DateTime::Format::ISO8601;
 
 my $U = 'OpenILS::Application::AppUtils';
@@ -147,7 +147,7 @@ sub format_date {
     return "" unless $date;
 
     my $dt = DateTime::Format::ISO8601->new->
-        parse_datetime(OpenSRF::Utils::cleanse_ISO8601($date));
+        parse_datetime(clean_ISO8601($date));
 
     # actor.usr.dob stores dates without time/timezone, which causes
     # DateTime to assume the date is stored as UTC.  Tell DateTime
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Item.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Item.pm
index 0c598257b8..e727732e6f 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Item.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Item.pm
@@ -9,7 +9,7 @@ use OpenILS::SIP::Transaction;
 use OpenILS::Application::AppUtils;
 # use Data::Dumper;
 use OpenILS::Const qw/:const/;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use DateTime::Format::ISO8601;
 use OpenSRF::Utils::SettingsClient;
 my $U = 'OpenILS::Application::AppUtils';
@@ -404,7 +404,7 @@ sub hold_pickup_date {
         $shelf_expire_setting_cache{$hold->pickup_lib->id} = $interval;
 
         if($interval) {
-            my $seconds = OpenSRF::Utils->interval_to_seconds($interval);
+            my $seconds = OpenILS::Utils::DateTime->interval_to_seconds($interval);
             $date = DateTime->now->add(seconds => $seconds);
             $date = $date->strftime('%FT%T%z') if $date;
         }
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm
index 9e593e5d50..3b458cb784 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm
@@ -18,7 +18,7 @@ use OpenILS::SIP;
 use OpenILS::Application::AppUtils;
 use OpenILS::Application::Actor;
 use OpenILS::Const qw/:const/;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use DateTime::Format::ISO8601;
 my $U = 'OpenILS::Application::AppUtils';
 
@@ -337,7 +337,7 @@ sub charge_ok {
     my $circ_is_blocked = 0;
 
     # compute expiration date for borrowing privileges
-    my $expire = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($u->expire_date));
+    my $expire = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($u->expire_date));
 
     $circ_is_blocked =
         (($u->barred eq 't') or
@@ -356,7 +356,7 @@ sub renew_ok {
     my $renew_is_blocked = 0;
 
     # compute expiration date for borrowing privileges
-    my $expire = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($u->expire_date));
+    my $expire = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($u->expire_date));
 
     $renew_is_blocked =
         (($u->barred eq 't') or
@@ -382,7 +382,7 @@ sub hold_ok {
     my $hold_is_blocked = 0;
 
     # compute expiration date for borrowing privileges
-    my $expire = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($u->expire_date));
+    my $expire = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($u->expire_date));
 
     $hold_is_blocked =
         (($u->barred eq 't') or
@@ -450,7 +450,7 @@ sub screen_msg {
     return $b if $u->standing_penalties and @{$u->standing_penalties};
 
     # has the patron account expired?
-    my $expire = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($u->expire_date));
+    my $expire = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($u->expire_date));
     return $b if CORE::time > $expire->epoch;
 
     return '';
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
new file mode 100644
index 0000000000..d6224800b4
--- /dev/null
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
@@ -0,0 +1,246 @@
+package OpenILS::Utils::DateTime;
+
+use Time::Local;
+use Errno;
+use POSIX;
+use FileHandle;
+use Digest::MD5 qw(md5 md5_hex md5_base64);
+use Exporter;
+use DateTime;
+use DateTime::Format::ISO8601;
+use DateTime::TimeZone;
+
+=head1 NAME
+
+OpenILS::Utils::DateTime;
+
+=head1 DESCRIPTION
+
+This contains several routines for doing date and time calculation. This
+is derived from the date/time routines from OpenSRF::Utils.
+
+=head1 VERSION
+
+=cut
+
+our $VERSION = 1.000;
+
+use vars qw/@ISA $AUTOLOAD %EXPORT_TAGS @EXPORT_OK @EXPORT/;
+push @ISA, 'Exporter';
+
+%EXPORT_TAGS = (
+	datetime	=> [qw(clean_ISO8601 gmtime_ISO8601 interval_to_seconds seconds_to_interval)],
+);
+Exporter::export_ok_tags('datetime');  # add aa, cc and dd to @EXPORT_OK
+
+our $date_parser = DateTime::Format::ISO8601->new;
+
+=head1 METHODS
+
+
+=cut
+
+sub AUTOLOAD {
+	my $self = shift;
+	my $type = ref($self) or return undef;
+
+	my $name = $AUTOLOAD;
+	$name =~ s/.*://;   # strip fully-qualified portion
+
+	if (defined($_[0])) {
+		return $self->{$name} = shift;
+	}
+	return $self->{$name};
+}
+
+=head2 $thing->interval_to_seconds('interval') OR interval_to_seconds('interval')
+
+=head2 $thing->seconds_to_interval($seconds) OR seconds_to_interval($seconds)
+
+Returns the number of seconds for any interval passed, or the interval for the seconds.
+This is the generic version of B<interval> listed below.
+
+The interval must match the regex I</\s*\+?\s*(\d+)\s*(\w{1})\w*\s*/g>, for example
+B<2 weeks, 3 d and 1hour + 17 Months> or
+B<1 year, 5 Months, 2 weeks, 3 days and 1 hour of seconds> meaning 46148400 seconds.
+
+	my $expire_time = time() + $thing->interval_to_seconds('17h 9m');
+
+The time size indicator may be one of
+
+=over 2
+
+=item s[econd[s]]
+
+for seconds
+
+=item m[inute[s]]
+
+for minutes
+
+=item h[our[s]]
+
+for hours
+
+=item d[ay[s]]
+
+for days
+
+=item w[eek[s]]
+
+for weeks
+
+=item M[onth[s]]
+
+for months (really (365 * 1d)/12 ... that may get smarter, though)
+
+=item y[ear[s]]
+
+for years (this is 365 * 1d)
+
+=back
+
+=cut
+sub interval_to_seconds {
+	my $self = shift;
+        my $interval = shift || $self;
+
+	$interval =~ s/(\d{2}):(\d{2}):(\d{2})/ $1 h $2 min $3 s /go;
+
+        $interval =~ s/and/,/g;
+        $interval =~ s/,/ /g;
+
+        my $amount = 0;
+        while ($interval =~ /\s*([\+-]?)\s*(\d+)\s*(\w+)\s*/g) {
+		my ($sign, $count, $type) = ($1, $2, $3);
+		$count = "$sign$count" if ($sign);
+                $amount += $count if ($type =~ /^s/);
+                $amount += 60 * $count if ($type =~ /^m(?!o)/oi);
+                $amount += 60 * 60 * $count if ($type =~ /^h/);
+                $amount += 60 * 60 * 24 * $count if ($type =~ /^d/oi);
+                $amount += 60 * 60 * 24 * 7 * $count if ($type =~ /^w/oi);
+                $amount += ((60 * 60 * 24 * 365)/12) * $count if ($type =~ /^mo/io);
+                $amount += 60 * 60 * 24 * 365 * $count if ($type =~ /^y/oi);
+        }
+        return $amount;
+}
+
+sub seconds_to_interval {
+	my $self = shift;
+        my $interval = shift || $self;
+
+        my $limit = shift || 's';
+        $limit =~ s/^(.)/$1/o;
+
+        my ($y,$ym,$M,$Mm,$w,$wm,$d,$dm,$h,$hm,$m,$mm,$s,$string);
+        my ($year, $month, $week, $day, $hour, $minute, $second) =
+                ('year','Month','week','day', 'hour', 'minute', 'second');
+
+        if ($y = int($interval / (60 * 60 * 24 * 365))) {
+                $string = "$y $year". ($y > 1 ? 's' : '');
+                $ym = $interval % (60 * 60 * 24 * 365);
+        } else {
+                $ym = $interval;
+        }
+        return $string if ($limit eq 'y');
+
+        if ($M = int($ym / ((60 * 60 * 24 * 365)/12))) {
+                $string .= ($string ? ', ':'')."$M $month". ($M > 1 ? 's' : '');
+                $Mm = $ym % ((60 * 60 * 24 * 365)/12);
+        } else {
+                $Mm = $ym;
+        }
+        return $string if ($limit eq 'M');
+
+        if ($w = int($Mm / 604800)) {
+                $string .= ($string ? ', ':'')."$w $week". ($w > 1 ? 's' : '');
+                $wm = $Mm % 604800;
+        } else {
+                $wm = $Mm;
+        }
+        return $string if ($limit eq 'w');
+
+        if ($d = int($wm / 86400)) {
+                $string .= ($string ? ', ':'')."$d $day". ($d > 1 ? 's' : '');
+                $dm = $wm % 86400;
+        } else {
+                $dm = $wm;
+        }
+        return $string if ($limit eq 'd');
+
+        if ($h = int($dm / 3600)) {
+                $string .= ($string ? ', ' : '')."$h $hour". ($h > 1 ? 's' : '');
+                $hm = $dm % 3600;
+        } else {
+                $hm = $dm;
+        }
+        return $string if ($limit eq 'h');
+
+        if ($m = int($hm / 60)) {
+                $string .= ($string ? ', ':'')."$m $minute". ($m > 1 ? 's' : '');
+                $mm = $hm % 60;
+        } else {
+                $mm = $hm;
+        }
+        return $string if ($limit eq 'm');
+
+        if ($s = int($mm)) {
+                $string .= ($string ? ', ':'')."$s $second". ($s > 1 ? 's' : '');
+        } else {
+                $string = "0s" unless ($string);
+        }
+        return $string;
+}
+
+sub gmtime_ISO8601 {
+	my $self = shift;
+	my @date = gmtime;
+
+	my $y = $date[5] + 1900;
+	my $M = $date[4] + 1;
+	my $d = $date[3];
+	my $h = $date[2];
+	my $m = $date[1];
+	my $s = $date[0];
+
+	return sprintf('%d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d+00:00', $y, $M, $d, $h, $m, $s);
+}
+
+sub clean_ISO8601 {
+	my $self = shift;
+	my $date = shift || $self;
+	if ($date =~ /^\s*(\d{4})-?(\d{2})-?(\d{2})/o) {
+		my $new_date = "$1-$2-$3";
+
+		if ($date =~/(\d{2}):(\d{2}):(\d{2})/o) {
+			$new_date .= "T$1:$2:$3";
+
+			my $z;
+			if ($date =~ /([-+]{1})([0-9]{1,2})(?::?([0-9]{1,2}))*\s*$/o) {
+				$z = sprintf('%s%0.2d%0.2d',$1,$2,$3)
+			} else {
+				$z =  DateTime::TimeZone::offset_as_string(
+					DateTime::TimeZone
+						->new( name => 'local' )
+						->offset_for_datetime(
+							$date_parser->parse_datetime($new_date)
+						)
+				);
+			}
+
+			if (length($z) > 3 && index($z, ':') == -1) {
+				substr($z,3,0) = ':';
+				substr($z,6,0) = ':' if (length($z) > 6);
+			}
+		
+			$new_date .= $z;
+		} else {
+			$new_date .= "T00:00:00";
+		}
+
+		return $new_date;
+	}
+	return $date;
+}
+
+1;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/HoldTargeter.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/HoldTargeter.pm
index 6d4c77f9e7..a42638c806 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Utils/HoldTargeter.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/HoldTargeter.pm
@@ -19,7 +19,7 @@ use DateTime;
 use OpenSRF::AppSession;
 use OpenSRF::Utils::Logger qw(:logger);
 use OpenSRF::Utils::JSON;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenILS::Application::AppUtils;
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
 
@@ -161,7 +161,7 @@ sub init {
 
     if ($self->{soft_retarget_interval}) {
 
-        my $secs = OpenSRF::Utils->interval_to_seconds(
+        my $secs = OpenILS::Utils::DateTime->interval_to_seconds(
             $self->{soft_retarget_interval});
 
         $self->{soft_retarget_time} = 
@@ -176,7 +176,7 @@ sub init {
     # it overrides the retarget_interval.
     my $next_check_secs = 
         $self->{next_check_interval} ?
-        OpenSRF::Utils->interval_to_seconds($self->{next_check_interval}) :
+        OpenILS::Utils::DateTime->interval_to_seconds($self->{next_check_interval}) :
         $retarget_seconds;
 
     my $next_check_date = 
@@ -258,7 +258,7 @@ use strict;
 use warnings;
 use DateTime;
 use OpenSRF::AppSession;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::Logger qw(:logger);
 use OpenILS::Application::AppUtils;
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
@@ -403,7 +403,7 @@ sub handle_expired_hold {
     return 1 unless $hold->expire_time;
 
     my $ex_time =
-        $dt_parser->parse_datetime(cleanse_ISO8601($hold->expire_time));
+        $dt_parser->parse_datetime(clean_ISO8601($hold->expire_time));
     return 1 unless 
         DateTime->compare($ex_time, DateTime->now(time_zone => 'local')) < 0;
 
@@ -781,7 +781,7 @@ sub inspect_previous_target {
         # soft_retarget_time and the retarget_time.
 
         my $pct = $dt_parser->parse_datetime(
-            cleanse_ISO8601($hold->prev_check_time));
+            clean_ISO8601($hold->prev_check_time));
 
         $soft_retarget =
             DateTime->compare($pct, $self->parent->{retarget_time}) > 0;
@@ -1184,7 +1184,7 @@ sub process_recalls {
     # Give the user a new due date of either a full recall threshold,
     # or the return interval, whichever is further in the future.
     my $threshold_date = DateTime::Format::ISO8601
-        ->parse_datetime(cleanse_ISO8601($circ->xact_start))
+        ->parse_datetime(clean_ISO8601($circ->xact_start))
         ->add(seconds => interval_to_seconds($threshold))
         ->iso8601();
 
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/Penalty.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Penalty.pm
index 94c561c218..c91bbc8d8a 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Utils/Penalty.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Penalty.pm
@@ -4,7 +4,7 @@ use DateTime;
 use Data::Dumper;
 use OpenSRF::EX qw(:try);
 use OpenSRF::Utils::Cache;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenILS::Application::AppUtils;
 use OpenSRF::Utils::Logger qw(:logger);
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
index d3aecaa0b0..56aee5bfb0 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
@@ -6,7 +6,7 @@ use Digest::MD5 qw(md5_hex);
 use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST);
 use OpenSRF::AppSession;
 use OpenSRF::EX qw/:try/;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::JSON;
 use OpenSRF::Utils::Logger qw/$logger/;
 use OpenILS::Application::AppUtils;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
index 3cb07997db..730586a128 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
@@ -267,7 +267,7 @@ sub init_ro_object_cache {
             $date = '000' . $date;
         }
 
-        my $cleansed_date = cleanse_ISO8601($date);
+        my $cleansed_date = clean_ISO8601($date);
 
         $date = DateTime::Format::ISO8601->new->parse_datetime($cleansed_date);
         if ($context_org) {
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/Exporter.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/Exporter.pm
index 8508ab6ede..576dffd515 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/Exporter.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/Exporter.pm
@@ -16,7 +16,7 @@ use Data::Dumper;
 use Text::CSV;
 
 use OpenSRF::EX qw(:try);
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::Cache;
 use OpenSRF::System;
 use OpenSRF::AppSession;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm
index a5fea7e630..b43d266069 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm
@@ -13,7 +13,7 @@ use SRU::Request;
 use SRU::Response;
 
 use OpenSRF::EX qw(:try);
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::Cache;
 use OpenSRF::System;
 use OpenSRF::AppSession;
@@ -1639,7 +1639,7 @@ sub create_record_feed {
         }
 
         $node->id($item_tag);
-        #$node->update_ts(cleanse_ISO8601($record->edit_date));
+        #$node->update_ts(clean_ISO8601($record->edit_date));
         $node->link(alternate => $feed->unapi . "?id=$item_tag&format=opac" => 'text/html') if ($flesh > 0);
         $node->link(slimpac => $feed->unapi . "?id=$item_tag&format=htmlholdings-full" => 'text/html') if ($flesh > 0);
         $node->link(opac => $feed->unapi . "?id=$item_tag&format=opac") if ($flesh > 0);
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat/Feed.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat/Feed.pm
index 56146cb988..92064092cc 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat/Feed.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat/Feed.pm
@@ -229,7 +229,7 @@ sub description {};
 
 package OpenILS::WWW::SuperCat::Feed::atom;
 use base 'OpenILS::WWW::SuperCat::Feed';
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 
 sub new {
     my $class = shift;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/TemplateBatchBibUpdate.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/TemplateBatchBibUpdate.pm
index fa4fc5d2c5..33d0f042f3 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/TemplateBatchBibUpdate.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/TemplateBatchBibUpdate.pm
@@ -16,7 +16,7 @@ use Data::Dumper;
 use Text::CSV;
 
 use OpenSRF::EX qw(:try);
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::Cache;
 use OpenSRF::System;
 use OpenSRF::AppSession;
diff --git a/Open-ILS/src/perlmods/live_t/03-overdue_circ.t b/Open-ILS/src/perlmods/live_t/03-overdue_circ.t
index fd83b2cac5..767980853c 100644
--- a/Open-ILS/src/perlmods/live_t/03-overdue_circ.t
+++ b/Open-ILS/src/perlmods/live_t/03-overdue_circ.t
@@ -16,7 +16,7 @@ my $script = OpenILS::Utils::TestUtils->new();
 
 use DateTime;
 use DateTime::Format::ISO8601;
-use OpenSRF::Utils qw/cleanse_ISO8601/;
+use OpenILS::Utils::DateTime qw/clean_ISO8601/;
 
 our $apputils = 'OpenILS::Application::AppUtils';
 
@@ -145,8 +145,8 @@ if (my $bill_resp = $bill_req->recv) {
     }
 }
 
-my $xact_start = DateTime::Format::ISO8601->parse_datetime(cleanse_ISO8601($circ->xact_start));
-my $due_date = DateTime::Format::ISO8601->parse_datetime(cleanse_ISO8601($circ->due_date));
+my $xact_start = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($circ->xact_start));
+my $due_date = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($circ->due_date));
 
 # Rewrite history; technically we should rewrite status_changed_item on the copy as well, but, meh...
 $circ->xact_start( $xact_start->subtract( days => 20 )->iso8601() );
diff --git a/Open-ILS/src/perlmods/live_t/04-overdue_with_closed_dates.t b/Open-ILS/src/perlmods/live_t/04-overdue_with_closed_dates.t
index 739fe26302..6501ef99bb 100644
--- a/Open-ILS/src/perlmods/live_t/04-overdue_with_closed_dates.t
+++ b/Open-ILS/src/perlmods/live_t/04-overdue_with_closed_dates.t
@@ -16,7 +16,7 @@ my $script = OpenILS::Utils::TestUtils->new();
 
 use DateTime;
 use DateTime::Format::ISO8601;
-use OpenSRF::Utils qw/cleanse_ISO8601/;
+use OpenILS::Utils::DateTime qw/clean_ISO8601/;
 
 our $apputils   = "OpenILS::Application::AppUtils";
 
@@ -194,8 +194,8 @@ if (my $bill_resp = $bill_req->recv) {
     }
 }
 
-my $xact_start = DateTime::Format::ISO8601->parse_datetime(cleanse_ISO8601($circ->xact_start));
-my $due_date = DateTime::Format::ISO8601->parse_datetime(cleanse_ISO8601($circ->due_date));
+my $xact_start = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($circ->xact_start));
+my $due_date = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($circ->due_date));
 
 # Rewrite history; technically we should rewrite status_changed_item on the copy as well, but, meh...
 $circ->xact_start( $xact_start->subtract( days => 20 )->iso8601() );
diff --git a/Open-ILS/src/perlmods/live_t/05-pay_bills.t b/Open-ILS/src/perlmods/live_t/05-pay_bills.t
index 94d380dc50..6e9fd1c89d 100644
--- a/Open-ILS/src/perlmods/live_t/05-pay_bills.t
+++ b/Open-ILS/src/perlmods/live_t/05-pay_bills.t
@@ -16,7 +16,7 @@ my $script = OpenILS::Utils::TestUtils->new();
 
 use DateTime;
 use DateTime::Format::ISO8601;
-use OpenSRF::Utils qw/cleanse_ISO8601/;
+use OpenILS::Utils::DateTime qw/clean_ISO8601/;
 
 our $apputils   = "OpenILS::Application::AppUtils";
 
diff --git a/Open-ILS/src/perlmods/live_t/09-lp1198465_neg_balances.t b/Open-ILS/src/perlmods/live_t/09-lp1198465_neg_balances.t
index 318f52ca92..2b83736766 100644
--- a/Open-ILS/src/perlmods/live_t/09-lp1198465_neg_balances.t
+++ b/Open-ILS/src/perlmods/live_t/09-lp1198465_neg_balances.t
@@ -11,7 +11,7 @@ use strict; use warnings;
 
 use DateTime;
 use DateTime::Format::ISO8601;
-use OpenSRF::Utils qw/cleanse_ISO8601/;
+use OpenILS::Utils::DateTime qw/clean_ISO8601/;
 use OpenILS::Utils::TestUtils;
 my $script = OpenILS::Utils::TestUtils->new();
 use Data::Dumper;
diff --git a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
index 548bea3101..dc6e2a1162 100644
--- a/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
+++ b/Open-ILS/src/perlmods/t/14-OpenILS-Utils.t
@@ -1,6 +1,6 @@
 #!perl -T
 
-use Test::More tests => 30;
+use Test::More tests => 39;
 use Test::Warn;
 use utf8;
 
@@ -21,6 +21,7 @@ use_ok( 'OpenILS::Utils::RemoteAccount' );
 use_ok( 'OpenILS::Utils::ZClient' );
 use_ok( 'OpenILS::Utils::EDIReader' );
 use_ok( 'OpenILS::Utils::HTTPClient' );
+use_ok( 'OpenILS::Utils::DateTime' );
 
 # LP 800269 - Test MFHD holdings for records that only contain a caption field
 my $co_marc = MARC::Record->new();
@@ -98,3 +99,11 @@ is($edi_msgs->[0]->{purchase_order}, '24', 'edi reader: PO number');
 is($edi_msgs->[1]->{invoice_ident}, '5TST084027', 'edi reader: invoice ident');
 is(scalar(@{$edi_msgs->[1]->{lineitems}}), '2', 'edi reader: lineitem count');
 
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 second'), 1);
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 minute'), 60);
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 hour'), 3600);
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 day'), 86400);
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 week'), 604800);
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 month'), 2628000);
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 year'), 31536000);
+is (OpenILS::Utils::DateTime::interval_to_seconds('1 year 1 second'), 31536001);
diff --git a/Open-ILS/src/support-scripts/generate_circ_notices.pl b/Open-ILS/src/support-scripts/generate_circ_notices.pl
index 998cc227ab..b27f5d8096 100755
--- a/Open-ILS/src/support-scripts/generate_circ_notices.pl
+++ b/Open-ILS/src/support-scripts/generate_circ_notices.pl
@@ -23,7 +23,7 @@ use Email::Send;
 use Getopt::Long;
 use Unicode::Normalize;
 use DateTime::Format::ISO8601;
-use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::DateTime qw/:datetime/;
 use OpenSRF::Utils::JSON;
 use OpenSRF::Utils::SettingsClient;
 use OpenSRF::AppSession;
@@ -128,12 +128,12 @@ sub main {
     $predue_notices = [$predue_notices] unless ref $predue_notices eq 'ARRAY'; 
 
     my @overdues = sort { 
-        OpenSRF::Utils->interval_to_seconds($a->{notify_interval}) <=> 
-        OpenSRF::Utils->interval_to_seconds($b->{notify_interval}) } @$overdue_notices;
+        OpenILS::Utils::DateTime->interval_to_seconds($a->{notify_interval}) <=> 
+        OpenILS::Utils::DateTime->interval_to_seconds($b->{notify_interval}) } @$overdue_notices;
 
     my @predues = sort { 
-        OpenSRF::Utils->interval_to_seconds($a->{notify_interval}) <=> 
-        OpenSRF::Utils->interval_to_seconds($b->{notify_interval}) } @$predue_notices;
+        OpenILS::Utils::DateTime->interval_to_seconds($a->{notify_interval}) <=> 
+        OpenILS::Utils::DateTime->interval_to_seconds($b->{notify_interval}) } @$predue_notices;
 
     for my $db (($opt_days_back) ? split(',', $opt_days_back) : 0) {
         if($opt_notice_types =~ /overdue/) {
@@ -174,7 +174,7 @@ sub global_overdue_output {
 sub generate_notice_set {
     my($notice, $type, $days_back) = @_;
 
-    my $notify_interval = OpenSRF::Utils->interval_to_seconds($notice->{notify_interval});
+    my $notify_interval = OpenILS::Utils::DateTime->interval_to_seconds($notice->{notify_interval});
     $notify_interval = -$notify_interval if $type eq 'overdue';
 
     my ($start_date, $end_date) = make_date_range($notify_interval - $days_back * 86400);
@@ -328,7 +328,7 @@ sub get_bib_attr {
 # provides a date that Template::Plugin::Date can parse
 sub parse_due_date {
     my $circ = shift;
-    my $due = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($circ->due_date));
+    my $due = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($circ->due_date));
     return sprintf(
         "%0.2d:%0.2d:%0.2d %0.2d-%0.2d-%0.4d",
         $due->hour,
diff --git a/Open-ILS/src/support-scripts/set_pbx_holidays.pl b/Open-ILS/src/support-scripts/set_pbx_holidays.pl
index 15483c0c97..aea2117b7a 100755
--- a/Open-ILS/src/support-scripts/set_pbx_holidays.pl
+++ b/Open-ILS/src/support-scripts/set_pbx_holidays.pl
@@ -4,7 +4,7 @@ require "/openils/bin/oils_header.pl";
 
 use strict;
 use warnings;
-use OpenSRF::Utils qw/cleanse_ISO8601/;
+use OpenILS::Utils::DateTime qw/clean_ISO8601/;
 use OpenSRF::Utils::Logger qw/$logger/;
 use OpenSRF::Utils::SettingsClient;
 
@@ -16,7 +16,7 @@ use Getopt::Std;
 sub unixify {
     my ($stringy_ts) = @_;
     return (new DateTime::Format::ISO8601)->parse_datetime(
-        cleanse_ISO8601($stringy_ts)
+        clean_ISO8601($stringy_ts)
     )->epoch;
 }
 

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/src/offline/offline.pl                    |  11 +-
 Open-ILS/src/perlmods/MANIFEST                     |   1 +
 .../src/perlmods/lib/OpenILS/Application/Actor.pm  |   6 +-
 .../perlmods/lib/OpenILS/Application/Booking.pm    |   4 +-
 .../src/perlmods/lib/OpenILS/Application/Circ.pm   |  27 +-
 .../lib/OpenILS/Application/Circ/CircCommon.pm     |  22 +-
 .../lib/OpenILS/Application/Circ/Circulate.pm      |  69 +++--
 .../perlmods/lib/OpenILS/Application/Circ/Holds.pm |  22 +-
 .../perlmods/lib/OpenILS/Application/Circ/Money.pm |   4 +-
 .../lib/OpenILS/Application/Circ/NonCat.pm         |  15 +-
 .../lib/OpenILS/Application/Collections.pm         |   2 +-
 .../src/perlmods/lib/OpenILS/Application/Serial.pm |   6 +-
 .../Application/Storage/Driver/Pg/QueryParser.pm   |  12 +-
 .../lib/OpenILS/Application/Storage/Publisher.pm   |   3 +-
 .../Application/Storage/Publisher/action.pm        |  12 +-
 .../OpenILS/Application/Storage/Publisher/actor.pm |  40 +--
 .../perlmods/lib/OpenILS/Application/Trigger.pm    |   6 +-
 .../lib/OpenILS/Application/Trigger/Reactor.pm     |   4 +-
 .../lib/OpenILS/Application/Trigger/Validator.pm   |   6 +-
 Open-ILS/src/perlmods/lib/OpenILS/SIP.pm           |   4 +-
 Open-ILS/src/perlmods/lib/OpenILS/SIP/Item.pm      |   4 +-
 Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm    |  10 +-
 .../src/perlmods/lib/OpenILS/Utils/DateTime.pm     | 314 +++++++++++++++++++++
 .../src/perlmods/lib/OpenILS/Utils/HoldTargeter.pm |  14 +-
 Open-ILS/src/perlmods/lib/OpenILS/Utils/Penalty.pm |   2 +-
 .../src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm    |   2 +-
 .../perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm   |   2 +-
 Open-ILS/src/perlmods/lib/OpenILS/WWW/Exporter.pm  |   2 +-
 Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm  |   4 +-
 .../src/perlmods/lib/OpenILS/WWW/SuperCat/Feed.pm  |   2 +-
 .../lib/OpenILS/WWW/TemplateBatchBibUpdate.pm      |   2 +-
 Open-ILS/src/perlmods/live_t/03-overdue_circ.t     |   6 +-
 .../perlmods/live_t/04-overdue_with_closed_dates.t |   6 +-
 Open-ILS/src/perlmods/live_t/05-pay_bills.t        |   2 +-
 .../perlmods/live_t/09-lp1198465_neg_balances.t    |   2 +-
 Open-ILS/src/perlmods/t/14-OpenILS-Utils.t         |  67 ++++-
 .../src/support-scripts/generate_circ_notices.pl   |  14 +-
 Open-ILS/src/support-scripts/set_pbx_holidays.pl   |   4 +-
 .../src/templates/staff/circ/patron/t_checkout.tt2 |  10 +-
 .../js/ui/default/staff/circ/patron/items_out.js   |   8 +-
 40 files changed, 575 insertions(+), 178 deletions(-)
 create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list