Skip to content

Instantly share code, notes, and snippets.

@jarkko
Created March 20, 2012 11:02
Show Gist options
  • Save jarkko/2134102 to your computer and use it in GitHub Desktop.
Save jarkko/2134102 to your computer and use it in GitHub Desktop.
Rails Time.zone.parse bug
1.9.3p125 :003 > Time.zone = "Pacific Time (US & Canada)"
=> "Pacific Time (US & Canada)"
1.9.3p125 :004 > Time.zone
=> (GMT-08:00) Pacific Time (US & Canada)
1.9.3p125 :005 > Time.zone.parse("2012-03-25 03:29")
=> Sun, 25 Mar 2012 04:29:00 PDT -07:00
1.9.3p125 :006 > Time.zone.parse("2012-03-24 03:29")
=> Sat, 24 Mar 2012 03:29:00 PDT -07:00
1.9.3p125 :007 > Time.zone = "UTC"
=> "UTC"
1.9.3p125 :008 > Time.zone
=> (GMT+00:00) UTC
1.9.3p125 :009 > Time.zone.parse("2012-03-25 03:29")
=> Sun, 25 Mar 2012 04:29:00 UTC +00:00
1.9.3p125 :010 > Time.zone.parse("2012-03-24 03:29")
=> Sat, 24 Mar 2012 03:29:00 UTC +00:00
@jarkko
Copy link
Author

jarkko commented Mar 20, 2012

The issue here is that Pacific Time went to DST already on March 11th. UTC obviously is always the same. So it seems that Time.zone.parse will take the time for the one-hour jump (and only that) from my system TZ.

Note that this is not a general DST one-hour push, it only happens for the one hour that does not exist during the switch to DST:

1.9.3p125 :017 > Time.zone.parse("2012-03-26 03:29")
 => Mon, 26 Mar 2012 03:29:00 PDT -07:00

@jarkko
Copy link
Author

jarkko commented Mar 20, 2012

Ok, it gets even weirder. Seems it will do the jump both during the missing hour in my system TZ and the current Time.zone:

1.9.3p125 :015 > Time.zone.parse("2012-03-11 02:29")
 => Sun, 11 Mar 2012 03:29:00 PDT -07:00
1.9.3p125 :028 > Time.zone.parse("2012-03-11 03:29")
 => Sun, 11 Mar 2012 03:29:00 PDT -07:00 
1.9.3p125 :029 > Time.zone.parse("2012-03-12 02:29")
 => Mon, 12 Mar 2012 02:29:00 PDT -07:00 

However, if I switch Time.zone to my actual system timezone, this issue goes away:

1.9.3p125 :022 >   Time.zone = "EET"
 => "EET" 
1.9.3p125 :023 > Time.zone
 => (GMT+02:00) EET 
1.9.3p125 :024 > Time.zone.parse("2012-03-25 03:29")
 => Sun, 25 Mar 2012 04:29:00 EEST +03:00 
1.9.3p125 :025 > Time.zone.parse("2012-03-11 02:29")
 => Sun, 11 Mar 2012 02:29:00 EET +02:00 

@jarkko
Copy link
Author

jarkko commented Mar 20, 2012

And just to lay it out, I reproduced the issue with Rails 3.0 and 3.2.2, both with Ruby 1.8.7 and 1.9.3.

@jarkko
Copy link
Author

jarkko commented Mar 21, 2012

Found the reason. Time.zone.parse uses by default Time.parse, which internally uses the system timezone to determine the time. Thus, regardless of the chosen Time.zone, it will base the jump to DST on the system clock. On the other hand ActiveSupport::TimeWithZone takes care of the same thing for the current Time.zone, which means every year we have two missing hours instead of only one.

I think this would be fixed by just comparing time.hour to date_parts[:hour] (if it exists), and if they don't match, fall back to DateTime.parse. This bug can't really happen when the hour isn't specified in the time string, so the fact that DateTime.parse doesn't take an optional now argument shouldn't matter.

I'll have to cook up a patch with tests for this.

@jarkko
Copy link
Author

jarkko commented Mar 23, 2012

I added an issue for this: rails/rails#5559

@jarkko
Copy link
Author

jarkko commented Mar 23, 2012

...and a pull request: rails/rails#5560

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment