PIYO - Tech & Life -

RailsでtimeとdatetimeとSQLiteとMySQLではまる

SQLite MySQL Rails

RSSフィードを例にとって、僕がRailsを触っているときにはまったことを書いておきます。初歩かもしれんけどハマったもんは仕方ないし、Stackoverflow見てたら他にも同じような人がいるっぽかったんでちょっとまとめておきます。

前置き

RSSフィードは書く記事に更新日時が入っています。新しい順に並べるのが一般的なので更新日時のカラムで並べ替えて記事を並べてあげたいとします。

Entryモデルはこんな感じになっています。

# マイグレーション
class CreateEntries < ActiveRecord::Migration
  def change
    create_table :entries do |t|
      t.string :title
      t.time :published

      t.timestamps
    end
  end
end

# モデル
class Entry < ActiveRecord::Base
  default_scope {order('entries.published DESC')} # 新しい順に
end

このブログの場合はこうなって欲しいです。

ところが、MySQLを使うproduction環境では次のようになってしまいました。

更新日時がめちゃくちゃになっているのがわかります。そのせいか並び順もおかしくなっています。

確認その1 更新日時がおかしい

フィードを解析して得られる日時は正しい日付が入っていることはデバッグして確認できています。データベースに入れるときにおかしくなっているのだと考えました。

Rails consoleを使って次のように試してみると、、、

entry_data = feeddata.entries.first
e = Entry.new(title:entry_data.title, published:entry_data.published)
p e.published # => 2013-05-10 23:35:07 +0900
e.save
p e.reload.published # => 2000-01-01 14:35:07 UTC !!! 

こんな風になってしまうのでした。 ここではそもそもtimeを使おうとしていたのが間違いでした。timeでは日付部分は保存されないのでdatetimeを使わないといけません。

class CreateEntries < ActiveRecord::Migration
  def change
    create_table :entries do |t|
      t.string :title
      t.datetime :published # <= ここを直す

      t.timestamps
    end
  end
end

これで日付は直ります。

確認その2 並び順がおかしい

前述のtimeを使用すると日付がおかしくなる(丸められると言ってもいいでしょう)ことは実はSQLite、MySQLともに起こります。問題というか、仕様ですかね。

ところが、sqliteを使っているdevelopment環境では順番がおかしくならなかったため気がつくのが遅れました。日付がおかしくてもたまたまうまいこと欲しい順番で結果が返ってきていたというわけです。

こんなふうに、日付はおかしいんだけど新しい順にならんでいるという。

これはおそらくSQLiteの際にorderが効いていないのだと考えられます。 MySQLになったときに初めてorderが有効になった、けれどそのときに使っていたカラムの値がおかしかったので順番がめちゃくちゃに見えてしまった、というのが今回ハマったところでした。

developmentとproductionのDBを揃えば住むことなので、初めからそうしたほうがいいです。身を持って経験。