#106 Time Zones (revised)
Oct 28, 2012 | 7 minutes | Active Support
Active Support makes it easy to work with time zones. Learn how to add a Time Zone select field to a user form and watch out for the gotchas.
- source codeProject Files in Zip (61.1 KB)
- mp4Full Size H.264 Video (14.3 MB)
- m4vSmaller H.264 Video (8.69 MB)
- webmFull Size VP8 Video (9.79 MB)
- ogvFull Size Theora Video (17.8 MB)
@ryanb, can you please elaborate a bit more about the leaky before filter? It's convenient that time accepts a block so around filter fits in perfectly, but i was just curious about the leaky before filter comment.
+1 for the elaboration
Aditya, as I understand it, when a web request comes in to a running web server, it will be handed off by Passenger (or whatever you use) to a running ruby process that has already handled a prior web request. Since the prior request most likely had a workflow that ran the before filter, and since the before filter sets the time zone for the entire process, that time zone is now being used for any code running in that process. If that is not undone when the request is done being responded to, then the next incoming request to that process will see all times be displayed in that prior zone. This is of course avoided if the before filter sets the time zone for every single incoming request, but it's safer (although probably slower) to use the around filter, in case you have any current or future case where the before filter might not be run for some reason.
If you want to get a time with a specific time zone, you can do:
Keep in mind that if you do things in a different ruby process (e.g. background jobs) you need to set the correct timezone there too.
I was wondering about Time.use_zone resp Time.zone in a threaded environment
If I interpret the documentation correctly, then Time.zone is a thread local
variable, which means it will just work fine. Can somebody confirm this?
Also the docu reads that Time.zone is a per request value (in mri). However,
that would mean that you actually don't need to use an around filter. A before
filter should suffice, doesn't it?
Time.zoneis a thread local. It is implemented here: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/time/zones.rb
But Time.zone is NOT request local. So if you change the Time.zone for User1, User2 will have their timezone changed also if their request occurs later in the same thread.
You can also use
Time.currentto get time in default time zone.
FWIW, I implemented a simple around_filter as shown here in my ApplicationController and it has massively slowed down my requests:
why not just before_filter?
He talks about this in the video: " If we used a before_filter here instead the time zone would leak through to other requests so it’s necessary to use an around filter instead."
I've never had an issue using before_filter when setting time zone. Its local.
It's local to the thread (which can serve multiple users) but it is not local to the session. So you absolutely can get 'cross talk' between sessions unless you run the before_filter AND set the timezone on every request (even requests for non-logged-in users)
Great episode, building off this for auto detection...
** How do you convert js values received as TZInfo identifiers to Rails TimeZone name/key? **
TO: "Eastern Time (US & Canada)" convention used in Rails TimeZone
I'm using the code introduced in this tutorial, and validation of the time_zone field fails with the error: Time zone is not included in the list.
Has anyone else encountered this?
Never mind. Got it to work. I was passing the wrong data into the validation.
Im getting this error also. Im using simple_form...which doesnt seem to change anything. But, using Ryans code...getting the same validation error you were. What did you end up changing to get the validation to pass?
Simple Form needs to submit the name of the zone, not the default
to_svalue. To do that, try the following in the form:
In the model I only validate against US time zones in the following fashion:
I'm using RailsAdmin and got the following error:
What went wrong?
Turned out it had nothing to do with this RailsCast, please ignore the comment above.
I am trying to query all of the Users that share a common time zone offset (so I can send them an email at a particular time everyday), and can not figure it out!
User.where(:time_zone => ['list','of time','zone names'])
How do I get a list of the time zone names for a particular offset? I've been tinkering with TzInfo and ActiveSupport::TimeZone.zones_map with limited success.
Am I taking the wrong approach here? I was thinking I could store the GMT offset on the user instead, and query that way?
My google-fu has let me down on this one.
figured it out:
In a model I only want to find those which didn't expire, yet.
For that I have a scope:
Should I use Time.current instead? I don't feel like I got the point :)
When I set this variable :ends_at, should I use Time.current + 30.days ?
Thx for the great work!
Why is that? As also noted, it's a good idea to use a different time zone for development so errors aren't obfuscated. So why not just leave config.time_zone at UTC? It seems like setting current time zone is only useful when all users are in the same time zone. Otherwise, it just adds complexity. Am I missing something?
Just a follow up on the issue with before_filter - if we set the time zone using before_filter at the top of the ApplicationController then why should bother about leaking it through to the whole process? Every new request to our application will go through the controller and for each new request the proper time zone should be set again right? We just need to make sure that it's always set and fallbacks to some default. Workers can handle time zone in a different way.
In Rails 4.1, we can no longer use this line:
It needs to be:
as discussed here
Thanks. Actually the comments for that answer state that
zones_map.keysis all you really need. So:
validates_inclusion_of :time_zone, in: ActiveSupport::TimeZone.zones_map.keys
It is useful when reading these articles, I have a few days to find these things. It too is excellent, very grateful for your profound contribution
Since the prior request most likely had a workflow that ran the before filter, and since the before filter sets the time zone for the entire process, that time zone is now being used for any code running in that process.