Saturday, October 20, 2007

Automatic Client Browser TimeZone Detection for Rails using Javascript and Ajax

After 2+ months of extensive development of Compliance at work, I finally had some spare time to throw into the second version of my pet project SoccerMap.

As usual, the timezone issue came up again, as soccer players can schedule games in their own time zones. After consulting with several existing solutions, TZInfo seems to be the way to go, but I didn’t quite like the fact that users have to explicitly set their own time zones. Like many other busy (or lazy) guys, I’d rather having everything just work out of the box without having me trying to customize it. Plus, what if I just traveled from west coast to east coast? All of the scheduled events will show up as west coast time in the application instead of east coast time.

My solution is actually quite simple ;-) Use javascript to notice the server as needed. This way, it doesn’t matter where you go, if the computer you are using has the local time zone set correctly, then we are good. User doesn’t have to do a thing.

1. Have the layout call a helper method get_local_time_zone, which will try to get the time zone if needed.

<%= get_local_time_zone %>
2. Implement the helper method in application_helper.rb. If we don’t see the gmtoffset stored in the session, then we would want to use a piece of javascript to get that information.

def get_local_time_zone
return '' if session[:gmtoffset].nil?
end
3. How do we get the time from javascript? We first get the client side time zone using javascript, and then use an ajax call to send it to the server.

Event.observe(window, 'load', function(e) {
var now = new Date();
var gmtoffset = now.getTimezoneOffset();
// use ajax to set the time zone here.
var set_time = new Ajax.Request('/gmtoffset/?gmtoffset='+gmtoffset, {
onSuccess: function(transport) {}
});
});
4. OK, It’s pretty obvious now that we need to implement the gmtoffset controller to take the ajax call.

def gmtoffset
session[:gmtoffset] = -params[:gmtoffset].to_i*60 if !params[:gmtoffset].nil? # notice that the javascript version of gmtoffset is in minutes ;-)
render :nothing => true # this can be improved by being able to return xml or json output to inform the client side whether the offset was successfully set on the server or not.
end
5. The rest is simple ;-) Just follow http://www.caboo.se/articles/2007/2/23/adding-timezone-to-your-rails-app, except in your application.rb, it should look like:

private
def set_timezone
TzTime.zone = TimeZone[session[:gmtoffset]] if session[:gmtoffset]
yield
TzTime.reset!
end
Reference:

1 comment:

eric said...

Everything worked great, except to get the name of the Timezone I had to do in my controller:

gmtoffset = -params[:gmtoffset].to_i*60 if !params[:gmtoffset].nil? # notice that the javascript version of gmtoffset is in minutes ;-)
result = ActiveSupport::TimeZone.all.select{|t|t.utc_offset == gmtoffset}.first

@user.time_zone = result.name