ぴよログ

↓に移転したのでこっちは更新されません、多分。

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

移転しました →

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

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

Chef Solo 再チャレンジ まとめ 2013-04-16 02:15:19 UTC
Chefでnginxを導入してみる 2013-04-16 02:10:39 UTC
Chefを使ってiptablesの設定を変える 2013-04-16 01:19:33 UTC
knifeでOPSCODEのCookbookを使う 2013-04-16 01:01:07 UTC
vagrantとchef-soloを体験してみる 2013-04-15 07:52:24 UTC
正規表現でテキストの置換ができるwebアプリ 2013-04-07 01:18:51 UTC
NewRelicでherokuのRailsアプリケーションのパフォーマンスを測定する 2013-04-04 06:26:10 UTC
WordPressMarkdown記法を使う 2013-04-03 05:10:09 UTC
RSpec+Spork+Guardの環境で :js => trueのテストを無視する 2013-03-28 13:23:04 UTC
カラースキームからTwitter Bootstrapのテーマを作成する 2013-03-19 04:46:33 UTC


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

RSpec+Spork+Guardの環境で :js => trueのテストを無視する 2000-01-01 13:23:04 UTC
vagrantとchef-soloを体験してみる 2000-01-01 07:52:24 UTC
NewRelicでherokuのRailsアプリケーションのパフォーマンスを測定する 2000-01-01 06:26:10 UTC
WordPressMarkdown記法を使う 2000-01-01 05:10:09 UTC
カラースキームからTwitter Bootstrapのテーマを作成する 2000-01-01 04:46:33 UTC
Chef Solo 再チャレンジ まとめ 2000-01-01 02:15:19 UTC
Chefでnginxを導入してみる 2000-01-01 02:10:39 UTC
Chefを使ってiptablesの設定を変える 2000-01-01 01:19:33 UTC
正規表現でテキストの置換ができるwebアプリ 2000-01-01 01:18:51 UTC
knifeでOPSCODEのCookbookを使う 2000-01-01 01:01:07 UTC


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

確認その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を使用すると日付がおかしくなる(丸められると言ってもいいでしょう)ことは実はSQLiteMySQLともに起こります。問題というか、仕様ですかね。

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

Chef Solo 再チャレンジ まとめ 2000-01-01 02:15:19 UTC
Chefでnginxを導入してみる 2000-01-01 02:10:39 UTC
Chefを使ってiptablesの設定を変える 2000-01-01 01:19:33 UTC
knifeでOPSCODEのCookbookを使う 2000-01-01 01:01:07 UTC
vagrantとchef-soloを体験してみる 2000-01-01 07:52:24 UTC
正規表現でテキストの置換ができるwebアプリ 2000-01-01 01:18:51 UTC
NewRelicでherokuのRailsアプリケーションのパフォーマンスを測定する 2000-01-01 06:26:10 UTC
WordPressMarkdown記法を使う 2000-01-01 05:10:09 UTC
RSpec+Spork+Guardの環境で :js => trueのテストを無視する 2000-01-01 13:23:04 UTC
カラースキームからTwitter Bootstrapのテーマを作成する 2000-01-01 04:46:33 UTC


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

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

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