Keeping Time
Posted by Kristján
As a drummer, keeping time is pretty simple. Count to 4, rinse, repeat. As a coder, not so much. Time in the computer world is kept to at least second precision, and usually millisecond or further. When you’re trying to do a whole lot of things with different systems just as quick as you can, this can yield unexpected results.
Let’s take a look at some simple code on our Hamburger model, which inherits from ActiveRecord::Base and has a created_at column.
>> whopper = Hamburger.create!(:cheese => true) => #<Hamburger id: 1, cheese: true, created_at: "2009-06-12 07:44:48">
Cool, now we can eat. Let’s load up another copy of this hamburger instance because company is coming and physics is no barrier.
>> whopperjr = Hamburger.find 1 => #<Hamburger id: 1, cheese: true, created_at: "2009-06-12 07:44:48">
Super. Our cheese is safe, and we still know how old it is, which we can use in should_i_eat_this? But something funny has happened. Let’s tinker a bit.
>> whopper.created_at == whopperjr.created_at => false >> whopper.created_at > whopperjr.created_at => true
Huh? Now that’s weird. Time is fooling around on me, and I don’t trust that my sandwich is edible anymore. Let’s take a closer look.
>> whopper.created_at.to_f => 1244792688.91866 >> whopperjr.created_at.to_f => 1244792688.0
Now isn’t that something? Here’s what’s going on, in slow motion*:
>> whopper = Hamburger.new
>> whopper.cheese = true
>> whopper.created_at = Time.now # 1244792688.91866
>> whopper.save!
=> #<Hamburger id: 1, cheese: true, created_at: "2009-06-12 07:44:48">
>> whopperjr = Hamburger.new
>> whopperjr.cheese = true
>> whopperjr.created_at = Time.parse("2009-06-12 07:44:48") # 1244792688.0
=> #<Hamburger id: 1, cheese: true, created_at: "2009-06-12 07:44:48">
Our whopper has been assigned a Time object that represents right now, including all the fractions of a second you could want. Sadly, when the value is dumped into MySQL’s datetime column, the fractional portion is dropped on the floor. When loaded back into memory, then, whopperjr has forgotten exactly when it was pulled off the grill, and has to run with the less accurate representation, leaving us with an object that was actually created before it was created!
Now if only Doc Brown had known this, he could have saved a lot of trouble from those Libyans.
*No, it’s not entirely accurate, ActiveRecord is using integers, not strings to talk to MySQL. Let’s call it isomorphic.
Tags: back to the future, hard bugs, rails, time