The Railscast on Time Zones has some sample code that lets you set a thread-local, per-request time zone with a before_filter:
# controllers/application.rb
before_filter :set_user_time_zone
private
def set_user_time_zone
Time.zone = current_user.time_zone if logged_in?
end
Problem is, the current user’s time zone will leak across to the next request on the same thread. If the next one is by a logged-out user, all times displayed on that next request will show in the first user’s time zone.
To prevent this, use an around_filter:
around_filter :set_time_zone
private
def set_time_zone
old_time_zone = Time.zone
Time.zone = current_user.time_zone if logged_in?
yield
ensure
Time.zone = old_time_zone
end
You might be tempted to combine the first two lines of set_time_zone into one line with the comma assignment syntax and add “if logged_in?” to the one line in the ensure clause. Don’t do it. If you did that, the user’s time zone would leak out to the next request when the user logs out.
I’ve submitted a Rails patch to use this method in the sample code for the Time.zone= docs.
February 4, 2011 at 9:16 am |
Update: I’ve made this change to the Time.zone= docs.
https://github.com/lifo/docrails/commit/3a29bfae2cbb956e469942ed1d4ea8c702085a1a
It turns out to make just a doc change you don’t need to submit a patch. You just check out the docrails project directly, make your change, and push it back to GitHub.
https://github.com/lifo/docrails
September 15, 2011 at 5:30 pm |
Hi Brian, I’ve been trying to set Time.zone directly as in: Time.zone=’CST’, I still get UTC if i check Time.zone. Time.now is in CDT and Time.now.zone is CDT. I’ve also tried Time.zone=Time.now.zone, yet Time.zone still yields UTC. Not sure what to make of all this. My goal is to set the local timezone so I can correctly test a controller action which converts a stored UTC date into a displayable local time – otherwise my test only passes in one timezone!
September 15, 2011 at 8:26 pm |
Hi Revision42,
‘CST’ is not one of the names Rails uses for time zones. You can see the full list by looking in the activesupport gem in lib/active_support/values/time_zone.rb.
The Rails name for CST is “Central Time (US & Canada)”.
One easy way to see this file is:
$ gem install gemedit
$ gem edit activesupport
This opens activesupport in your editor (whatever EDITOR is set to). Then navigate to the file.
September 19, 2011 at 3:37 pm |
Hi Brian, thanks for the response, it turns out Time.now.zone is just a string representation of a time-zone. Whereas ActiveSupport::TimeZone is really what I want to set Time.zone.
Your code worked for me if I put it directly in the action where it was needed. However the around_filter didnt work for me, the Time.zone seems to fall out of scope in the yield which surprised me.