Friends,
So there I was, minding my own business, trying to make my rails app
timezone aware (DST, as well), since it’s all about the timing with
this one. There are a number of excellent articles that I’ve been
working off of, in particular this one internet archived version of
Scott B.'s very useful article on the subject that is no longer
available at the original URL that is linked to from the TZInfo page:
http://web.archive.org/web/20060425190845/http://lunchroom.lunchboxsoftware.com/pages/tzinfo_rails
I’m not looking to pull of anything fancy here, but I’ve ended up a
bit of a stumper in which utc_to_local appears to be adjusting the UTC-
stored time twice.
This is what I’ve done so far:
-
Set ENV[‘TZ’] = ‘UTC’ in config/environment.rb
-
Uncommented this line in config/environment.rb:
config.active_record.default_timezone = :utc -
Installed the tzinfo gem.
-
Added a require statement for tzinfo into config/environment.rb:
require ‘tzinfo’ -
Added a timezone selector to my user account profiles, and a string
field in the database to support it. Now this topic deserves a
diversion, as the use of an active record aggregation has been nothing
but problems, and treating it as a string has been the only useful
means of doing this. Here’s what I mean:
We want to be able to access the user’s timezone attribute as a
TZInfo::Timezone object instance so that we can say things like:
@current_user.tz.utc_to_local(some_date_time_object)
But whenever I used the standard aggregation shown in examples on this
topic, the drop down time_zone_select for TZInfo doesn’t recognize the
user’s previous selection! So I got around that like thus:
i. created a string field on the user model called time_zone
ii. added an accessor tz that will give us the object support
def tz
TZInfo::Timezone.get(self.time_zone)
end
iii. and this is my select that can reproduce the previous selection:
Timezone
<%= f.time_zone_select :time_zone,
TZInfo::Timezone.all.sort,
:model => TZInfo::Timezone %>
When I used the composed_of method to try and handle the time_zone
field without any other methods, the time_zone_select method kept
letting me down, not setting the previously selected value in the DDL.
Okay, diversion over. Moving on:
- So let’s just say my app allows users to make many foos. One at a
time. And each foo goes to the bar for a pint at a specific time, but
we initially set it to Time.now(), usually. Well, now I’ve updated
foo_controller to do it according to the user’s time_zone:
def new
@foo = Foo.new
@foo.goes_to_bar = @current_user.tz.utc_to_local(Time.now)
end
This correctly sets the selector for the timing of the Foo to “now” in
the user’s local time.
On save, this value is appropriately stored in postgresql in UTC.
- Now, it’s time display the Foo. I don’t want to just print out
the value in UTC, so I’ll use utc_to_local to show it off:
<%= @current_user.tz.utc_to_local(foo.goes_to_bar) %>
Eventually, I’ll make a helper for this. But let’s just work with
this example for now.
Right here is where I run into a problem (thank you for sticking
around this long!) When I print Foo’s datetime column, correctly
stored in UTC, I find that the resulting output appears to have been
twice calculated to local time. Thus, if it was stored at 1400 EDT,
instead of coming back 1400 EDT, it’s coming back 1000 UTC!
And to boot, when it gets printed, by default it’s getting shown with
this incorrect TZ (which I’ll eliminate later via sprintf, but still
it doesn’t bode well):
“Mon Apr 02 10:25:00 UTC 2007”
That’s not so great. Can anyone here help me shed some light on
this? I don’t think I’m doing anything particularly fancy here. My
initial thoughts are that:
a. perhaps I’m being redundant in environment.rb by setting UTC in two
places.
b. perhaps I need to set postgres’s timezone to UTC (this would be
annoying to do to other non-tz aware apps)
c. perhaps those “timestamp without time zone” columns that rails
generates for datetime columns need to support the time zone? this
doesn’t make much sense, since all things are agreeing to store time
in UTC. it might also be bad in general since that’s rather db-vendor-
dependent.
Thanks so much for any help! I think that this is a pretty common
journey folks are trying to make with Rails apps and the various
articles out there don’t seem to arrive at a full and working solution
(as they are building on eachother’s work, have old code, etc). I’d
love to give Jamis B.'s tztime a shot, but I feel like I need to
grasp the basics of this first.
All my best,
Billy