The Life of a Radar

How Rails Works #1: A Timezone Overview
31 Dec 2008

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...

REXML could not parse this XML/HTML: 
<ul>
  <li>1. <b>The <span class='term'>Configuration</span> Class</b><br />
This class begins all the way down on <a href='http://github.com/rails/rails/blob/2-2-stable/railties/lib/initializer.rb#L578'>line #578 of <i>railties/lib/initializer.rb</i></a> in the Rails source. This just simply defines a class in the <i>Rails</i> module called <i>Configuration</i>. What we can get really excited about is on <a href='http://github.com/rails/rails/blob/2-2-stable/railties/lib/initializer.rb#L748'>line #748</a> it defines an <span class='term'><a href='http://www.ruby-doc.org/core/classes/Module.html#M001704'>attr_accessor</a></span> for <span class='term'>:time_zone</span>. This, as you probably already know defines two methods a setter (<span class='term'>time_zone=</span>) and a getter (<span class='term'>time_zone</span>) in which we can store values.
  </li>

  • 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.
  • REXML could not parse this XML/HTML: 
    </ul>

    Changelog

    Updated on April 23rd, 2009
    1. Fixed line number linkings, linking directly to 2-2-stable branch which, ideally, should now never change.
    blog comments powered by Disqus