Migrate to the Rails Default Time Zones

June 3rd, 2008 by Trevor

I was recently upgrading my open-source app, El Dorado, up to Rails 2.1 and decided to switch over to the default time zones provided by the Rails. I'm not sure where I picked up the time zone definitions I was using before, but they were things like "US/Central" as opposed to "Central Time (US & Canada)".

Switching to the new time zone definitions provided by Rails means not having to rely on the TZInfo gem, because a stripped-down version is now packaged with Rails 2.1.

Here's the migration used to achieve this, which was made with some help from Geoff Buesing himself:

 
# db/migrate/20080603023415_use_rails_new_default_time_zones.rb
 
class UseRailsNewDefaultTimeZones < ActiveRecord::Migration
  def self.up
    @users = User.all
    @users.each do |user|
      user.time_zone = 'UTC' if user.time_zone.blank?
      tz = TZInfo::Timezone.get(user.time_zone) rescue TimeZone[user.time_zone] || TimeZone['UTC']
      time_zone = if tz.is_a?(TZInfo::Timezone)
        linked_timezone = tz.instance_variable_get('@linked_timezone')
        name = linked_timezone ? linked_timezone.name : tz.name
        TimeZone::MAPPING.index(name)
      else
        tz.name
      end
      user.update_attribute(:time_zone, time_zone) unless time_zone == user.time_zone
    end unless @users.empty?
  end
 
  def self.down
  end
end
 

If you haven't played with the new time zone stuff in Rails 2.1, make sure to check it out. It makes dealing with time zones so easy, it's almost unbelievable. Thanks, Geoff!

Comments

Posting code? Please use Pastie.

Have a question? Please visit the Forum.

3 Comments

  1. I wrote a migration to set the user’s time zone by US state if you have a predominantly American user base.

    You could change it’s default to UTC. It doesn’t have to do an update for every user record.

    “You can find it here.”http://www.jrmiii.com/2008/6/3/quick-migration-to-set-user-time-zones-for-rails-2-1

    Cheers

    Comment by Joe Martinez on June 4, 2008

  2. First, thanks for this script. I was a bit surprised that there is a lot of cheering about the new Rails time zone support, but I bet there are a lot of projects using the old TZInfo stuff and how to convert your time zones then? When I used the above script, I found a lot of zones are missing in the new system.

    One of the things that I found is that the original TZInfo GEM has more zones, so the TZInfo::Timezone.get call retrieves more zones (e.g. Europe/Oslo). I kept this GEM before I converted my data and after that I removed it (of course I want as little external stuff as needed).

    But I also added something to select at least the same time zone (with a different city) in case the old time zone can’t be found in TimeZone::MAPPING.

    My migration looks like this (maybe it helps others), based on the example above. By the way, I kept the old time zone in a backup field, to make a backwards conversion possible (for the time being, that is).

    def self.up
    # convert all user time zones
    change_column :users, :timezone, :string, :limit => 40, :null => false, :default => ‘London’
    add_column :users, :old_timezone, :string, :limit => 40, :null => false, :default => ‘Europe/London’
    execute ‘UPDATE users SET old_timezone = timezone’

    User.all.each do |user|
    tz = TZInfo::Timezone.get(user.timezone) rescue TimeZone[user.timezone] || TimeZone['UTC']
    if tz.is_a?(TZInfo::Timezone)
    linked_timezone = tz.instance_variable_get(’@linked_timezone’)
    name = linked_timezone ? linked_timezone.name : tz.name
    time_zone = TimeZone::MAPPING.index(name)
    if time_zone.nil?
    compatible_zones = TimeZone.all.select { |t| t.utc_offset == tz.current_period.offset.utc_total_offset }
    time_zone = compatible_zones.empty? ? ‘UTC’ : compatible_zones.first.name
    end
    else
    time_zone = tz.name
    end
    user.update_attribute :timezone, time_zone
    end

    def self.down
    execute ‘UPDATE users SET timezone = old_timezone’
    remove_column :users, :old_timezone
    change_column :users, :timezone, :string, :limit => 40, :null => false, :default => ‘Europe/London’
    end

    (My field in the users table is called timezone).


  3. [...] over time, various tips have cropped up to convert old to new. But neither tip worked good for me; I found them too [...]