MySQL 5.6からTIME, DATETIME, TIMESTAMPがマイクロ秒精度をサポートしました。
MySQL 5.6 Reference Manual :: 11.3.6 Fractional Seconds in Time Values
で、ドキュメントにはちゃんとroundされるけどこれはSQL標準に従う挙動だからエラーとかワーニングは出ないよってさらっと書いてあるんですが、5.5では小数点以下は切り捨てだったんでマイクロ秒を付けてクエリ投げてた場合ハマりポイントになってます。
MySQL Bugs: #68760: Datetime rounding problem
ActiveRecordにおけるDATETIMEのマイクロ秒サポート
ここからはジョーカーさんのRails-4.2+MySQL-5.6での時刻オブジェクトのミリ秒の扱いについてへのアンサーエントリになるんですが、ActiveRecord 4.2では以下のPRがマージされたことでMySQLでもマイクロ秒を付けてクエリを送るようになりました(Rebuild: 56でmiyagawaさんがマージされるのに1年半ぐらいかかったって言ってたやつです)。
MySQL 5.6 Fractional Seconds by arthurnn · Pull Request #14359 · rails/rails
これは5.6のためにマイクロ秒付けるけど5.5には影響ないだろう(travis-ciはMySQL 5.5だからマイクロ秒のテストをCIで回せないというのがもともと1年半ぐらい放置されていたときの言い分だった)ということだったんだけど、roundされて切り上がると思った通りに動かないということをみんな甘くみていて、これ関係のissueがいくつか上がってきています。
queries with ActiveSupport::TimeZone have extra fractional time · Issue #17239 · rails/rails
小数点以下をそのカラムが持つマイクロ秒精度以上送ってroundされて切り上がるのは望ましくない挙動であることはコアチームの人も認識はしていて、精度に応じたフォーマットをするべきだよねとは言っています。
で、MySQLでは問題になってるけどPostgreSQLのほうはどうなってるのか、ちゃんと精度に応じたフォーマットがされているのかというと、PostgreSQLでも最大精度でマイクロ秒を送るので当然round問題は存在します。以下のテストはMySQLもPostgreSQLもdate
がroundされた結果2014-08-17 12:30:01
として保存されるのでFoo.find_by(created_at: date)
でレコードがヒットしません。
def test_formatting_datetime_according_to_precision
ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
t.datetime :created_at, precision: 0
t.datetime :updated_at, precision: 4
end
date = ::Time.utc(2014, 8, 17, 12, 30, 0, 999999)
Foo.create!(created_at: date, updated_at: date)
assert foo = Foo.find_by(created_at: date)
assert_equal date.to_s, foo.created_at.to_s
assert_equal date.to_s, foo.updated_at.to_s
assert_equal 000000, foo.created_at.usec
assert_equal 999900, foo.updated_at.usec
end
activerecord-mysql-awesome/test/cases/datetime_test.rb#L57-L69
PostgreSQLでこの挙動がこれまで問題とされてこなかったのは、精度を指定しなかった場合の挙動がMySQLとPostgreSQLでは異なるからです。MySQLでは精度を指定しない場合マイクロ秒を持たないとなりますが、PostgreSQLでは精度を指定しない場合マイクロ秒精度に制限を持たない(実質最大の精度を持つ)となるため、ふつうに使ってるひとがハマる機会があまりないということになります。
この問題は以前から認識していたので直す前段階として#17673をPRしてたけど空気的に4.2.0には入れてくれなさそうなんで4.2.0出たら直そうと思ってたんですが、ジョーカーさんがこの問題踏んだのでとりいそぎ修正PRを出しておきました。
このまま4.2.0が出るとMySQL使ってるひと阿鼻叫喚だと思うんで上記PRをバックポートしたactiverecord-mysql-awesome 0.0.2をリリースしておいたのでどうぞお納めください。これを使うと4.0, 4.1, まだ出てないけど4.2でマイクロ秒サポートの恩恵を受けることが出来ます。