Correctly handling multiple time zones in ruby on rails
A recent client I was working for had the unfortunate situation where we'd both independently assumed we understood the requirements, without realising a missing piece of complexity. It was around time zones. The system was implemented with a cut-off date being US EST, but most of their clientele are in LA so it needed to be US PST. What is a developer to do?
Well it turns out that timezone_fu will come to the rescue.
Installing time zone support into your rails app
Getting timezone_fu working with an existing rails app is a cinch, the only dependency is on the TZInfo package, so best you grab that now:
sudo gem install tzinfo
And the install the plugin in your rails app:
./script/plugin install http://www.thrivesmart.com/open_source/timezone_fu
Now before we do anything, make sure that we are defaulting to UTC time in environment.rb:
ENV['TZ'] = 'UTC'
Migrating your models so they know about time zones
Next, take your model that you want to apply your dates to. In my scenario it was an election which had a start and end date for when voting could occur. First thing I needed to do was to add a field to record the time zone that the dates were to be handled in:
And then amend my model definition in election.rb to add in the time zone helper:
class Election [:start_at, :end_at], :default_timezone => "America/Los_Angeles", :time_format => "%I:%M %p on %A %d %B %Y" end
Storing and retrieving time zones in your rails app
The wonderful thing about this setup is that dates saved or retrieved from the model using the normal attribute accessors will displayed in the local time (as determined by whatever you save in the timezone field). I've also used all of the attributes to show their usage, but you can safely leave efault_timezone and ime_format off if you desire. The latter is a strftime string format outlining how to format the string, so we can get something like:
>> @election.display_end_at => "08:10 PM on Wednesday 20 February 2008"
Unfortunately though the strftime method used by the display_attribute helpers still thinks that the timezone is in UTC format (I'll hopefully get around to a patch shortly), so within my views to display the date with the timezone it is actually more like: