Radio Bantik:
Days in the Life of an Alpha Geek

Corey Ehmke’s home on the web since 1996, IdolHands.com is an alpha-geek blog covering topics in Ruby on Rails development, Mac OS X, electronics, robotics, and other stuff important in the life of a technologist and tinkerer.

ActiveRecord Find Conditions and Time Zone Support

Posted by Corey Ehmke on March 29th, 2009 in Ruby on Rails | Permanent Link | Share/Save
Tags: , ,
How Soon is Now?

Rails 2.1 added the ability to set a default time zone in your environment file, like this:

config.time_zone = 'Central Time (US & Canada)'

This defaults Time.zone to the zone specified, and also sets all date and time values written in your database to UTC (Coordinated Universal Time). Getters and setters for your ActiveRecord models adjust automatically, but there’s a gotcha.

In script/console:

>> Time.now

=> Sun Mar 29 12:38:47 -0500 2009

>> c = Client.create(:name => "Date Test")

=> #<Client id: 6, address: nil, address_2: nil, , city: nil, country: nil, phone: nil, name: "Date Test", state: nil, url: nil, zip: nil, active: true, created_at: "2009-03-29 17:35:54", updated_at: "2009-03-29 17:35:54">

Notice that local time is 12:38, but the created_at in the database record is 5 hours in the future (UTC – Central Time = 5 hours). However, using an accessor, we get:

>> c.created_at

=> Sun, 29 Mar 2009 12:35:54 CDT -05:00

This is a Good Thing; the ActiveRecord getters and setters are respecting our time zone setting.

But here’s the gotcha: if you manually create find conditions to locate records by a time or date, you can’t just use Time.now as you might have in the past. For example:

>> Client.find(:last, :conditions => ['created_at < ?', Time.now - 5.minutes])

=> #<Client id: 4, address: "Rt 10 Green Hills", address_2: "P.O. Box 563", city: "Reading", country: "US", phone: "610-777-7777", name: "Some Other Client", state: "Pennsylvania", url: "http://www.foo.com", zip: "19603", active: true, created_at: "2008-10-28 15:00:19", updated_at: "2008-10-28 15:58:04">

This is not the record that we were expecting– this one is months old!

To fix the find condition, you have to change Time.now to Time.zone.now:

>> Client.find(:last, :conditions => ['created_at < ?', Time.zone.now - 5.minutes])

=> #<Client id: 6, primary_agent_id: nil, primary_contact_id: nil, address: nil, address_2: nil, camp_directory_url: nil, city: nil, country: nil, local_site_meta_description: nil, local_site_meta_keywords: nil, phone: nil, name: "Date Test", state: nil, url: nil, zip: nil, active: true, created_at: "2009-03-29 17:35:54", updated_at: "2009-03-29 17:35:54">

This caused some amount of chaos for me last week, as one member of the team added time zone configuration and suddenly scheduled background tasks weren’t running on schedule anymore. I strongly recommend searching your code base for Time.now and DateTime.now and modifying them accordingly; remember, anything that’s using accessors is fine as is, but other calls may cause unexpected results.


2 Responses to “ActiveRecord Find Conditions and Time Zone Support”

  1. Brad Gessler Says:

    This is good to know; we have some funky time zone problems in our admin panel that this will fix.

  2. Damian Nicholson Says:

    This caused quite a bit of bother for me too, thanks for pointing out your solution to this :D

Leave a Reply