I recently revisited the problem of detecting browser 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 ago.
I wanted a simpler solution: one that doesn’t require a js cookie library, nor around filters, nor a UI with a combo box with 100s of time zone choices.
The solution presented is to send browser info using jquery and then storing the time zone in the session for use in all subsequent requests.
in the view (this is haml), this can be in your layout on all pages:
- unless @time_zone
= javascript_tag "$.get('/controller/time_zone',
'offset_minutes':(-1 * (new Date()).getTimezoneOffset())})"
in the controller:
before_filter :init_time_zone #sets the time zone for this request if a session time zone exists #if it doesn't the default is UTC def init_time_zone @time_zone = ActiveSupport::TimeZone[session[:time_zone_name]] if session[:time_zone_name] Time.zone = @time_zone.name if @time_zone end #this receives browser info from a jquery request and stores #time zone info in the session def time_zone offset_seconds = params[:offset_minutes].to_i * 60 @time_zone = ActiveSupport::TimeZone[offset_seconds] @time_zone = ActiveSupport::TimeZone["UTC"] unless @time_zone session[:time_zone_name] = @time_zone.name if @time_zone render :text => "success" end in the formatter:def format_time(t) return "" unless t return t.in_time_zone.strftime('%Y-%m-%d %H:%M:%S %Z') end
Look how simple that is! I choose to default the time zone to UTC if one cannot be determined on the first attempt. Now the formatter will output all the UTC times you have in your db or anywhere in the user’s browser’s time zone.
If there is a better solution in Rails please let me know. We ‘ll see if this solution works for daylight saving time.
April 18, 2009 at 5:47 am |
Hey,
im implementing your code, and it works.
But could you give me some hint how to do the javascript part with prototype?
I do it like:
var pars = ‘offset_minutes=’ + (-1 * (new Date()).getTimezoneOffset());
new Ajax.Request(‘/controller/time_zone’, {
method: ‘post’,
parameters: pars});
but with the ajax call render :text => ’success’ is not really working, it displays the success text if called.
thanks for your help!
April 21, 2009 at 8:20 am |
My prototype skilz are a little rusty but you could make the time_zone action return “” instead of “success” or post to a hidden div.
April 21, 2009 at 8:24 am |
Here’s a fix for DST:
def time_zone
offset_seconds = params[:offset_minutes].to_i * 60
@time_zone = ActiveSupport::TimeZone.all.find { |z| ((z.now.dst? && z.utc_offset == offset_seconds-3600) || (!z.now.dst? && z.utc_offset == offset_seconds)) && !["Arizona","Chihuahua","Mazatlan"].include?(z.name)}
@time_zone = ActiveSupport::TimeZone["UTC"] unless @time_zone
session[:time_zone_name] = @time_zone.name if @time_zone
render :text => “success”
end
You can see it ignores some time zones so that Denver will be picked above Arizona. Sorry Arizona.
A more complete solution would have the browser detect if it’s in a tz that recognized dst. I’m going to have to write a plugin for all this.