Protip: TimeWithZone, All The Time

If you’ve ever tried to retrieve a list of ActiveRecord objects based on their timestamps, you’ve probably been bitten by the quirky time support in Rails:

>> Goal.create(:description => "Run a mile") => #<Goal id: 1, description: "Run a mile", created_at: "2008-09-09 19:32:57", updated_at: "2008-09-09 19:32:57"> >> Goal.find(:all, :conditions => ['created_at < ?', Time.now]) => [] 

Huh? Checking the logs, we see that the two commands above correspond to the following queries:

INSERT INTO "goals" ("updated_at", "description", "created_at") VALUES('2008-09-09 19:32:57', 'Run a mile', '2008-09-09 19:32:57') SELECT * FROM "goals" WHERE created_at < '2008-09-09 15:33:17' 

Rails stores created_at relative to Coordinated Universal Time, while Time.now is based on the system clock, running four hours behind. The solution? ActiveSupport’s TimeWithZone:

>> Goal.find(:all, :conditions => ['created_at < ?', Time.zone.now]) => [#<Goal id: 1, description: "Run a mile", created_at: "2008-09-09 19:32:57", updated_at: "2008-09-09 19:32:57">] 

Rule of thumb: always use TimeWithZone in your Rails projects. Date, Time and DateTime simply don’t play well with ActiveRecord. Instantiate it with Time.zone.now and Time.zone.local. To discard the time element, use beginning_of_day.

BONUS TIP

Since it’s a subclass of Time, interpolating a range of TimeWithZone objects fills in every second between the two times — not so useful if you need a date for every day in a month:

>> t = Time.zone.now => Tue, 09 Sep 2008 14:26:45 EDT -04:00 >> (t..(t + 1.month)).to_a.size [9 minutes later] => 2592001 

Fortunately, the desired behavior is just a monkeypatch away:

class ActiveSupport::TimeWithZone def succ self + 1.day end end >> (t..(t + 1.month)).to_a.size => 31 

For more information about time zones in Rails, Geoff Buesing and Ryan Daigle have good, up-to-date posts.

David Eisinger

David is Viget's managing development director. From our Durham, NC, office, he builds high-quality, forward-thinking software for PUMA, the World Wildlife Fund, ID.me, and many others.

More articles by David

Sign up for The Viget Newsletter

Nobody likes popups, so we waited until now to recommend our newsletter, a curated periodical featuring thoughts, opinions, and tools for building a better digital world. Read the current issue.