From b1c3b925764f6d8bf7cfe8b9f9c9a4ebe4c09d04 Mon Sep 17 00:00:00 2001
From: Galen Charlton <gmc@equinoxinitiative.org>
Date: Fri, 21 Sep 2018 14:30:01 -0400
Subject: [PATCH] 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@equinoxinitiative.org>
Signed-off-by: Kathy Lussier <klussier@masslnc.org>
---
 .../src/perlmods/lib/OpenILS/Utils/DateTime.pm     |  2 ++
 Open-ILS/src/perlmods/t/14-OpenILS-Utils.t         | 25 +++++++++++++++-------
 2 files changed, 19 insertions(+), 8 deletions(-)

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');
-- 
2.11.0