LP#1552778: make clean_ISO8601 recognize 'Z' as a timezone specifier
authorGalen Charlton <gmc@equinoxinitiative.org>
Fri, 21 Sep 2018 18:30:01 +0000 (14:30 -0400)
committerKathy Lussier <klussier@masslnc.org>
Mon, 24 Sep 2018 18:41:04 +0000 (14:41 -0400)
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>
Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
Open-ILS/src/perlmods/t/14-OpenILS-Utils.t

index 69fec7a..05f0883 100644 (file)
@@ -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
index ec164a7..a341f3e 100644 (file)
@@ -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');