Timezone awareness in Rails
I just committed "another piece of the puzzle":http://dev.rubyonrails.org/changeset/8806 to "Rails' new time zone support":http://ryandaigle.com/articles/2008/1/25/what-s-new-in-edge-rails-easier-timezones, coming in Rails 2.1. This takes a different approach than my "previous attempt":http://svn.rubyonrails.org/rails/plugins/tztime/ 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(Daylight Savings Time). However, since this will update on each request, it'll correct itself once the DST status changes.
Huge thanks to "Geoff Buesing":http://mad.ly, the mastermind behind this new TimeWithZone implementation.
related
- 2010 Jul 13 In-Process Node.js Queues
- 2010 Jul 07 Geek Talk Interview
- 2010 Jun 28 Tee and Child Processes
- 2010 Jun 23 You can let go now
- 2010 May 17 Railsconf: Building APIs
- 2010 May 10 Nori: Node.js Riak wrapper
- 2010 May 10 No, I did not create a mobile phone framework too
- 2010 May 04 Escaping your test suite with your life
- 2010 Apr 05 Will the iPad kill comic books?
- 2010 Apr 05 First day at GitHub

