Skip to content
This repository
Browse code

Make `Time.zone.parse` to work with JavaScript date strings

Chrome, Safari and Firefox serialize Date objects to strings such
as 'Mon May 28 2012 00:00:00 GMT-0700 (PDT)'. When these strings
are parsed the zone is interpreted as 'GMT-0700' which doesn't
exist in the TzInfo list of timezones.

By taking advantage of the improved date/time handling in 1.9.3
we can use `Date._parse` and the `:offset` value which is parsed
correctly.

Three tests were amended to make them pass:

1.  test_parse_with_old_date

    This needed changing to a different value because the original
    value was before EST was adopted so was being changed to a
    LMT (Local Mean Time) value after the change. It didn't before
    because `DateTime` just has offsets from UTC not timezones.

2.  test_parse_should_not_black_out_system_timezone_dst_jump

    Changed the implementation of this test as the stubs were
    dependent on internal implementation details of the test.
    Confirmed that the modified test still failed when the
    implementation of `parse` was restored to pre-#5571.

3.  test_parse_should_black_out_app_timezone_dst_jump

    Ditto.

Closes #5770.
  • Loading branch information...
commit 005d910624bbfa724b638426a000c8074d4201a2 1 parent 0a1c611
Andrew White authored December 01, 2012
2  activesupport/CHANGELOG.md
Source Rendered
... ...
@@ -1,5 +1,7 @@
1 1
 ## Rails 4.0.0 (unreleased) ##
2 2
 
  3
+*   Make `Time.zone.parse` to work with JavaScript format date strings. *Andrew White*
  4
+
3 5
 *   Add `DateTime#seconds_until_end_of_day` and `Time#seconds_until_end_of_day`
4 6
     as a complement for `seconds_from_midnight`; useful when setting expiration
5 7
     times for caches, e.g.:
27  activesupport/lib/active_support/values/time_zone.rb
@@ -278,18 +278,23 @@ def at(secs)
278 278
     #   Time.zone.now               # => Fri, 31 Dec 1999 14:00:00 HST -10:00
279 279
     #   Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
280 280
     def parse(str, now=now)
281  
-      date_parts = Date._parse(str)
282  
-      return if date_parts.empty?
283  
-      time = Time.parse(str, now) rescue DateTime.parse(str)
284  
-
285  
-      if date_parts[:offset].nil?
286  
-        if date_parts[:hour] && time.hour != date_parts[:hour]
287  
-          time = DateTime.parse(str)
288  
-        end
289  
-
290  
-        ActiveSupport::TimeWithZone.new(nil, self, time)
  281
+      parts = Date._parse(str, false)
  282
+      return if parts.empty?
  283
+
  284
+      time = Time.new(
  285
+        parts.fetch(:year, now.year),
  286
+        parts.fetch(:mon, now.month),
  287
+        parts.fetch(:mday, now.day),
  288
+        parts.fetch(:hour, now.hour),
  289
+        parts.fetch(:min, now.min),
  290
+        parts.fetch(:sec, now.sec) + parts.fetch(:sec_fraction, 0),
  291
+        parts.fetch(:offset, 0)
  292
+      )
  293
+
  294
+      if parts[:offset]
  295
+        TimeWithZone.new(time.utc, self)
291 296
       else
292  
-        time.in_time_zone(self)
  297
+        TimeWithZone.new(nil, self, time)
293 298
       end
294 299
     end
295 300
 
32  activesupport/test/time_zone_test.rb
@@ -178,8 +178,8 @@ def test_parse_string_with_timezone
178 178
 
179 179
   def test_parse_with_old_date
180 180
     zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
181  
-    twz = zone.parse('1850-12-31 19:00:00')
182  
-    assert_equal [0,0,19,31,12,1850], twz.to_a[0,6]
  181
+    twz = zone.parse('1883-12-31 19:00:00')
  182
+    assert_equal [0,0,19,31,12,1883], twz.to_a[0,6]
183 183
     assert_equal zone, twz.time_zone
184 184
   end
185 185
 
@@ -204,21 +204,25 @@ def test_parse_with_incomplete_date
204 204
   end
205 205
 
206 206
   def test_parse_should_not_black_out_system_timezone_dst_jump
207  
-    zone = ActiveSupport::TimeZone['Pacific Time (US & Canada)']
208  
-    zone.stubs(:now).returns(zone.now)
209  
-    Time.stubs(:parse).with('2012-03-25 03:29', zone.now).
210  
-                       returns(Time.local(0,29,4,25,3,2012,nil,nil,true,"+03:00"))
211  
-    twz = zone.parse('2012-03-25 03:29')
212  
-    assert_equal [0, 29, 3, 25, 3, 2012], twz.to_a[0,6]
  207
+    with_env_tz('EET') do
  208
+      zone = ActiveSupport::TimeZone['Pacific Time (US & Canada)']
  209
+      twz = zone.parse('2012-03-25 03:29:00')
  210
+      assert_equal [0, 29, 3, 25, 3, 2012], twz.to_a[0,6]
  211
+    end
213 212
   end
214 213
 
215 214
   def test_parse_should_black_out_app_timezone_dst_jump
216  
-    zone = ActiveSupport::TimeZone['Pacific Time (US & Canada)']
217  
-    zone.stubs(:now).returns(zone.now)
218  
-    Time.stubs(:parse).with('2012-03-11 02:29', zone.now).
219  
-                       returns(Time.local(0,29,2,11,3,2012,nil,nil,false,"+02:00"))
220  
-    twz = zone.parse('2012-03-11 02:29')
221  
-    assert_equal [0, 29, 3, 11, 3, 2012], twz.to_a[0,6]
  215
+    with_env_tz('EET') do
  216
+      zone = ActiveSupport::TimeZone['Pacific Time (US & Canada)']
  217
+      twz = zone.parse('2012-03-11 02:29:00')
  218
+      assert_equal [0, 29, 3, 11, 3, 2012], twz.to_a[0,6]
  219
+    end
  220
+  end
  221
+
  222
+  def test_parse_with_javascript_date
  223
+    zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
  224
+    twz = zone.parse("Mon May 28 2012 00:00:00 GMT-0700 (PDT)")
  225
+    assert_equal Time.utc(2012, 5, 28, 7, 0, 0), twz.utc
222 226
   end
223 227
 
224 228
   def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize

8 notes on commit 005d910

Andrew White
Owner

Looking at it now

Andrew White
Owner

Should be fixed by c89b6c4 - AR tests pass locally for me afterwards.

Димитър Копаров

BTW, why did you change this line to pass false as the second param? Was just wondering...
181: parts = Date._parse(str, false)

Andrew White
Owner

Because leaving it as true (which is the default) results in weird parsing - see: #12458 (comment)

Димитър Копаров

I see, there's been a whole discussion :) To me it seems the best solution would be to let people pass that param along, depending on their needs, which may be different in different circumstances... We had to monkeypatch that in for now

Andrew White
Owner

@mitkouwcad did you see the bit about it not working like you think - it's parsed as YY/MM/DD' which is probably not what your users want - use chronic or timeliness if you want natural language date parsing.

Please sign in to comment.
Something went wrong with that request. Please try again.