From 4974b1a483774f67fc2ac6595c3f492bad608605 Mon Sep 17 00:00:00 2001
From: Andrew White <andrew.white@unboxed.co>
Date: Fri, 3 Mar 2017 14:58:35 +0000
Subject: Add `ActiveSupport::TimeZone.iso8601` parsing method

Previously there was no way to get a ISO 8601 timestamp into a specific
timezone without either using `parse` or chaining methods. The new method
allows parsing directly into the timezone, e.g:

    >> Time.zone = "Hawaii"
    => "Hawaii"
    >> Time.zone.iso8601("1999-12-31T14:00:00Z")
    => Fri, 31 Dec 1999 14:00:00 HST -10:00

If the timestamp is a ISO 8601 date (YYYY-MM-DD) then the time is set
to midnight, e.g:

    >> Time.zone = "Hawaii"
    => "Hawaii"
    >> Time.zone.iso8601("1999-12-31")
    => Fri, 31 Dec 1999 00:00:00 HST -10:00

This new method has stricter semantics than the current `parse` method
and will raise an `ArgumentError` instead of returning nil, e.g:

    >> Time.zone = "Hawaii"
    => "Hawaii"
    >> Time.zone.iso8601("foobar")
    ArgumentError: invalid date
    >> Time.zone.parse("foobar")
    => nil
---
 activesupport/test/time_zone_test.rb | 72 ++++++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

(limited to 'activesupport/test')

diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 4794b55742..6cc5bf9d25 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -215,6 +215,78 @@ class TimeZoneTest < ActiveSupport::TestCase
     assert_equal secs, twz.to_f
   end
 
+  def test_iso8601
+    zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+    twz = zone.iso8601("1999-12-31T19:00:00")
+    assert_equal Time.utc(1999, 12, 31, 19), twz.time
+    assert_equal Time.utc(2000), twz.utc
+    assert_equal zone, twz.time_zone
+  end
+
+  def test_iso8601_with_invalid_string
+    zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+
+    exception = assert_raises(ArgumentError) do
+      zone.iso8601("foobar")
+    end
+
+    assert_equal "invalid date", exception.message
+  end
+
+  def test_iso8601_with_missing_time_components
+    zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+    twz = zone.iso8601("1999-12-31")
+    assert_equal Time.utc(1999, 12, 31, 0, 0, 0), twz.time
+    assert_equal Time.utc(1999, 12, 31, 5, 0, 0), twz.utc
+    assert_equal zone, twz.time_zone
+  end
+
+  def test_iso8601_with_old_date
+    zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+    twz = zone.iso8601("1883-12-31T19:00:00")
+    assert_equal [0, 0, 19, 31, 12, 1883], twz.to_a[0, 6]
+    assert_equal zone, twz.time_zone
+  end
+
+  def test_iso8601_far_future_date_with_time_zone_offset_in_string
+    zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+    twz = zone.iso8601("2050-12-31T19:00:00-10:00") # i.e., 2050-01-01 05:00:00 UTC
+    assert_equal [0, 0, 0, 1, 1, 2051], twz.to_a[0, 6]
+    assert_equal zone, twz.time_zone
+  end
+
+  def test_iso8601_should_not_black_out_system_timezone_dst_jump
+    with_env_tz("EET") do
+      zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
+      twz = zone.iso8601("2012-03-25T03:29:00")
+      assert_equal [0, 29, 3, 25, 3, 2012], twz.to_a[0, 6]
+    end
+  end
+
+  def test_iso8601_should_black_out_app_timezone_dst_jump
+    with_env_tz("EET") do
+      zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
+      twz = zone.iso8601("2012-03-11T02:29:00")
+      assert_equal [0, 29, 3, 11, 3, 2012], twz.to_a[0, 6]
+    end
+  end
+
+  def test_iso8601_doesnt_use_local_dst
+    with_env_tz "US/Eastern" do
+      zone = ActiveSupport::TimeZone["UTC"]
+      twz = zone.iso8601("2013-03-10T02:00:00")
+      assert_equal Time.utc(2013, 3, 10, 2, 0, 0), twz.time
+    end
+  end
+
+  def test_iso8601_handles_dst_jump
+    with_env_tz "US/Eastern" do
+      zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+      twz = zone.iso8601("2013-03-10T02:00:00")
+      assert_equal Time.utc(2013, 3, 10, 3, 0, 0), twz.time
+    end
+  end
+
   def test_parse
     zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
     twz = zone.parse("1999-12-31 19:00:00")
-- 
cgit v1.2.3