Timezones, along with charset and multi-browser support, are a pain in the ass to deal with.
With rails, since 2.1, timezones are supported by default via the
config.time_zone option :
# config/environment.rb config.time_zone = ‘UTC’
You can configure it to something else like :
# config/environment.rb config.time_zone = 'Paris'
Or in the controller, per user basis :
# controllers/application.rb before_filter :set_time_zone def set_time_zone Time.zone = @current_user.time_zone if @current_user end
With all this, rails and active record handle timezones for you. But be very careful when building SQL queries by hand.
ActiveRecord converts dates to UTC timezone before saving them to the database, and convert them back to the current timezone when you load the record from the database. And this can lead to something strange when doing queries like:
User.all(:conditions => ["created_at > ?", Date.today.at_beginning_of_day])
ActiveRecord converts date, but only when saving or loading records. But you can deal with it if you don't forget timezones:
User.all(:conditions => ["created_at > ?", Time.zone.now.at_beginning_of_day])
Something strange is that Rails adds timezone support to the Time class, but not to the date calculations:
>> Time.zone = 'Paris' >> Date.today.to_s(:db) => "2010-02-05" >> Time.now.to_s(:db) => "2010-02-05 00:07:49" # The same date everywhere >> Time.zone.today.to_s(:db) => "2010-02-05" >> Time.zone.now => Fri, 05 Feb 2010 00:06:13 CET +01:00 # All is right here
Let's make some calculations :
>> Time.zone.today.at_beginning_of_day.to_s(:db) => "2010-02-05 00:00:00" # Seems to be ok, but >> Time.zone.now.at_beginning_of_day.to_s(:db) => "2010-02-04 23:00:00" # Woot ! the database is still yesterday !
Something you can do if you absolutely need to use Time.today (something stolen from Barry Hess):
class ::Date def beginning_of_day_in_zone Time.zone.parse(self.to_s) end alias_method :at_beginning_of_day_in_zone, :beginning_of_day_in_zone alias_method :midnight_in_zone, :beginning_of_day_in_zone alias_method :at_midnight_in_zone, :beginning_of_day_in_zone def end_of_day_in_zone Time.zone.parse((self+1).to_s) – 1 end end