Today fowlduck and I were talking in the #rubyonrails channel and we both wondered about timezones and why they were so (apparently) screwy. It turns out all I (and he possibly also) forgot to do was to put config.time_zone in the config/environment.rb. So what does this mysterious method do? Well:

In Rails 2.2...

  • 1. The Configuration Class
    This class begins all the way down on line #578 of railties/lib/initializer.rb in the Rails source. This just simply defines a class in the Rails module called Configuration. What we can get really excited about is on line #748 it defines an attr_accessor for :time_zone. This, as you probably already know defines two methods a setter (time_zone=) and a getter (time_zone) in which we can store values.
  • 2. Your config/environment.rb file
    By default this time_zone method will be set to nil. It's up to you to set it in your config/environment.rb file which you do by doing something along these lines:
    Rails::Initializer.run do |config|
      config.time_zone = "Adelaide"
    end
    
    This will set the time_zone value to be the Adelaide Time zone, something like: #<ActiveSupport::TimeZone:0x30f4f8 @tzinfo=nil, @name="Adelaide", @utc_offset=34200>. You don't have to set it to Adelaide, just try your nearest major city and it Should Just Work ™. If you don't set this in your config/environment.rb date and time values returned from the database will not be set to whatever time zone you specify.
  • 3. Back to you, Jeff. When your application loads it processes the config/environment.rb file and runs the initialize_time_zone method which is defined further down. This does all kinds of magic! Look at all the pretty sparkles! It firstly checks if you've set a time_zone in your config/environment.rb file and then if you have it sets the default time zone to be what you've specified. Additionally to this, it sets time_zone_aware_attributes to true so that when you do stuff like Topic.last.created_at it'll actually return the time zoned version of that time. It does this by calling into play define_read_method_for_time_zone_conversion(attr_name) (click for juicy details) which either just returns the time or calls in_time_zone on the time returned which converts it into the time zone stored in Time.zone (which is actually Thread.current[:time_zone] if there is one stored there or otherwise the zone_default which was originally set when we called config.time_zone! What a mouthful! By default, ActiveRecord will store timestamps as UTC as shown by ActiveRecord::Base.default_timezone = :utc. If you don't specify a time zone in your config/environment.rb this value defaults to :local, so all times will be stored as the local time in your database.
  • 4. And then... So, assuming you did as the above when you go into your script/console and type: Topic.last.created_at you'll get back the time when the topic was created relative to Adelaide. To change this, just redefine Time.zone by doing Time.zone= "Paris" and then doing Topic.last.created_at will give you time when the topic was created relative to Paris.

Changelog

Updated on April 23rd, 2009

  1. Fixed line number linkings, linking directly to 2-2-stable branch which, ideally, should now never change.