Question

I have a String "3:30 PM 2014-03-15 Eastern Time (US & Canada)" which I want to convert to a DateTime and store in my database.

This is pretty simple:

str = "3:30 PM 2014-03-15 Eastern Time (US & Canada)"
event.starts_at = DateTime.strptime(str, "%l:%M %p %Y-%m-%d %Z")

That creates a DateTime with the correct time zone. However, if I save the event to the database and then read it back out like so:

e.starts_at.in_time_zone("Eastern Time (US & Canada)")
    .strftime("%l:%M %p") => #outputs 4:30 PM, when I want it to say 3:30 PM

The output is giving the wrong time (seems to be because it is adjusting for DST)! What can I do to fix this issue?

Was it helpful?

Solution

DateTime.strptime belongs to Ruby, while time zone identifiers like "Eastern Time (US & Canada)" are only provided by Rails via ActiveSupport::TimeZone.

In simple terms, DateTime doesn't understand that time zone at all. You can find in the docs for DateTime that the %z and %Z tokens are defined as follows:

Time zone:
    %z - Time zone as hour and minute offset from UTC (e.g. +0900)
            %:z - hour and minute offset from UTC with a colon (e.g. +09:00)
            %::z - hour, minute and second offset from UTC (e.g. +09:00:00)
            %:::z - hour, minute and second offset from UTC
                                              (e.g. +09, +09:30, +09:30:30)
    %Z - Time zone abbreviation name or something similar information.

The %z definition makes sense, but the %Z definition is laughable. It doesn't really say what is allowed or expected. Perhaps it is looking for an abbreviation like "EST" or "EDT", or perhaps it will allow some keywords like "Eastern". I'm really not sure, but I don't think it's likely that it's fully aware of Rails's idea of time zones (which are also laughable - see the very bottom of the timezone tag wiki).

Logically, what's happening in your code sample is that the value is being parsed in Eastern Standard Time - even though Eastern Daylight Time is in effect for the time you're passing. Just a guess, but something in the parser is probably picking up "eastern" and assuming EST. It's then saved in your database incorrectly with a -5 offset. When Rails loads it back and processes it with the general "Eastern Time (US & Canada)" time zone, it is aware of the fact that EDT is in effect, so it loads it with the correct -4 offset. The difference in offsets is why you see the result an hour off.

Suggestion #1 - Give up on Rails time zone identifiers. Use the Ruby TZInfo gem instead, which gives you normal IANA time zone ids like America/New_York.

Suggestion #2 - If you must use Rails time zones, then use them in conjunction with Time.zone.parse from Rails, instead of DateTime.strptime.

(I'm not particularly fluent in Ruby or I would provide you an example. Perhaps someone can edit my response to provide one, or provide one in an answer of their own.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top