Timezone awareness in Rails
I just committed another piece of the puzzle to Rails’ new time zone support, coming in Rails 2.1. This takes a different approach than my previous attempt at timezone-aware activerecord attributes. The whole idea behind this approach, is that times appear in the local time zone while you work with them, but are persisted to the database in UTC.
>> Time.zone = "Pacific Time (US & Canada)"
>> @status = Status.find :first
>> @status.created_at
=> Tue, 29 Jan 2008 15:30:09 PST -08:00
If you set the time, it makes any necessary conversions to your current time zone.
>> @status.created_at = Time.utc 2008, 1, 1
>> @status.created_at
=> Mon, 31 Dec 2007 16:00:00 PST -08:00
Multiparameter attributes (say, from a time select box) are changed to the current time zone properly.
>> @status.attributes = {
?> "created_at(1i)" => "2008", "created_at(2i)" => "1", "created_at(3i)" => "1",
?> "created_at(4i)" => "0", "created_at(5i)" => "0", "created_at(6i)" => "0" }
>> @status.created_at
=> Tue, 01 Jan 2008 00:00:00 PST -08:00
Now in our particular app, it’s very important that each user sets up their own timezone. The easiest way, we’ve found, is to take the offset from the browser. This way, the user can move around, hop on a business flight, or play with their system clock, and our app will still function correctly. First, we set a cookie with the timezone offset:
Cookie.set({tzoffset: (new Date()).getTimezoneOffset()});
Then, I wrote a before filter to process this automatically and set the Time.zone value appropriately.
# The browsers give the # of minutes that a local time needs to add to
# make it UTC, while TimeZone expects offsets in seconds to add to
# a UTC to make it local.
def browser_timezone
return nil if cookies[:tzoffset].blank?
@browser_timezone ||= begin
min = cookies[:tzoffset].to_i
TimeZone[-min.minutes]
end
end
def set_timezone
if logged_in? && browser_timezone \
&& browser_timezone.name != current_user.time_zone
current_user.update_attribute(:time_zone, browser_timezone.name)
end
Time.zone = logged_in? ? current_user.time_zone : browser_timezone
end
Keep in mind, this does only set the current offset. The offset doesn’t account for things like DST. However, since this will update on each request, it’ll correct itself once the DST status changes.
Huge thanks to Geoff Buesing, the mastermind behind this new TimeWithZone implementation.

by Nicolas on 04 Jan 19:42
Regarding user timezone; This works for dealing with Time.now, but breaks when the user specifies a date in the past or future, which may have a different DST value. It is also useless on the first page served, before the Javascript had a chance to provide the tzoffset cookie to the server.
For one to display the user’s local time on the first page load, I come to think that the clean way might be to do it completely in Javascript. If a form needs to be sent with a time, then the Javascript should populate a field (select or hidden) to let the server do the translation.