Rails Timezones | October 5th, 2008
In my spare time I have been working on a geo-location application that runs on a BlackBerry mobile phone connecting to a Rails server where the geo data is stored.
If you happen to still be using Rails < 2.1 then this may be interesting, however, with the recent release of 2.1 timezones have been fixed up pretty well. Of course I wanted to get something that worked for my application in pre 2.1 days so I rolled together a few different libraries that seems to work pretty well. Having said that, the JavaScript will help you determine a users timezone no matter what version of Rails you are using.
The particular use case that I want to hit is that people can login to the web application from wherever they want and the application will automagically display the times in the timezone of the user viewing the site - not the timezone of the user that created the geo data and not the timezone of the server.
The general approach taken is to get the users timezone offset through JavaScript in the browser, then save that in the users profile, and then have a helper method to convert any times that are saved in the servers timezone to the users local zone.
Prerequisites
Gems: tzinfo (0.3.9, 0.3.8), tztime (0.1.0)
Plugins, tzinfo_timezone, tztime
Now for the code
So how do we figure out the users timezone? Through JavaScript of course! In application.js put the following script at the top - don’t put it on the onload event since it should set as soon as a user hits the page:
Cookie.set("tz", (new Date()).getTimezoneOffset());
This sets a cookie in the browsr to be the JavaScript timezone offset, which we will use later on the server. Note that this is using a simple cookie abstraction.
Now in application.rb we need some magic that will take that timezone offset from the cookie and update the users profile with their current timezone. This will be achieved with an around_filter (damn I love around filters) like so:
around_filter :set_timezone
def set_timezone
if logged_in? && browser_timezone && (browser_timezone.name != current_user.time_zone || current_user.time_zone.nil?)
current_user.update_attribute(:time_zone, browser_timezone.name)
end
TzTime.zone = logged_in? ? current_user.time_zone : browser_timezone
yield
TzTime.reset!
end
def browser_timezone
return nil if cookies[:tz].blank?
@browser_timezone ||= begin
min = cookies[:tz].to_i
TimeZone[-min/60]
end
end
That takes care of getting the users current timezone. Now all we have left is to make it accessible to the application with a simple function in the application_helper.rb as follows:
def local_time(time_at)
TimeZone[TzTime.zone].utc_to_local(time_at.utc)
end
Finally, we can just call local_time from anywhere passing in a time that is in the servers timezone from the database:
local_time(location.updated_at)
So if you are stuck on Rails < 2.1 that is a good approach to getting timezones working.
February 27th, 2009 at 11:09 am
[...] time zone because I wanted to try the new time zone functionality in Rails 2.1. I found this post from Dave Johnson. To my disappointment this was the same solution Spongecell used in the personal calendar 3 years [...]
February 27th, 2009 at 11:19 am
Here’s an alternative solution that doesn’t require using a cookies but that does use jquery. For a simple use of time zones it could be a cleaner solution.
http://spongetech.wordpress.com/2009/02/27/detecting-browser-time-zone-with-rails/