less than 1 minute read

We recently discovered that Rails maps a :timestamp in a migration file to an Oracle DATE instead of a TIMESTAMP. DATE types only have second precision, not millisecond.

To make things even weirder, rails still maps that column to a ruby Time object which has sub-second precision. This can lead to the following test failure:

Create a table with:

create_table :posts do |t|
  t.column :posted_at, :timestamp

And then try:

now = Time.now
Post.create(:posted_at => now)
assert_equal now, Post.find(1).posted_at

The failure occurs because now has milliseconds, but they are truncated when the Post is saved (since the table uses a DATE). When the record is read back in, the two times are different.

In order to get our tests passing, we decided to just compare the string versions of the two times rather than the times themselves. Since the string representations do not include milliseconds, the test will pass.

assert_equal now.to_s, Post.find(1).posted_at.to_s

You can read more about the Oracle date/time weirdness in the source: oracle_adapter.rb at line 100.