Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

RailsでDBのtime型同士を比較する際の注意点

Railsではデータベースのtime型を使用できます。例えば、PostgreSQLでは、時刻だけを扱うtime型を使用できます。本記事では、Railsでtime型カラム同士を比較する際に期待した判定結果にならないケースについて紹介します。

環境

  • Rails: 7.1.3
  • Ruby: 3.3.3
  • PostgreSQL 16

データベースのタイムゾーンはUTC、RailsのタイムゾーンはAsia/Tokyoに設定しています。

また以下のように、Railsでtime型のカラムを定義しています。

create_table(:works) do |t|
  t.time :time_from
  t.time :time_to
end

マイグレーション実行後にtime without time zone型として追加されます。
ちなみにtime with time zone型はRailsではサポートしていないようで型変換がされません。timestamp型はtimestamptzとしてwith time zoneをサポートしている模様です。

参考: rails/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb at v7.1.3 · rails/rails

バグになるコード

次のようなコードでは、期待通りに動作しない場合があります。
errors.add(:time_from, :invalid) if time_from > time_to
このコードは、time_fromtime_toがどちらもtime型であり、time_fromtime_toを上回っている場合にエラーとするものです。しかし、この判定が正しく動作しない場合があります。

何が問題か

time型はRailsで参照される際に、ActiveSupport::TimeWithZoneクラスとして扱われます。RubyやRailsには純粋なtime型を表すクラスが存在しないため、time型を参照する際、日時が含まれていない部分にデフォルトの日付(2000-01-01)がセットされます。

問題となるのは、データベースのタイムゾーンがUTC、Rails側のタイムゾーンがJSTの場合、00:00 ~ 08:59の値がUTCからJSTに変換される際に日付が2000-01-01から2000-01-02に変更されることです。
例えば、08:59を保存した場合、データベースでは23:59:00で保存されます。そしてRailsでJSTに変換する際、日付をまたぐため、2000-01-02 08:59:00 +0900のように扱われます。

このため、time_fromだけが日付をまたいで変換されている場合、時刻的にはtime_fromtime_toより小さくても、time_from > time_toの判定がtrueとなります。

DB保存前は問題なし

データベースに保存する前のtime型同士の比較では、UTCからJSTへの変換が行われないため、日付が進むことはありません。

irb(main):020> Work.new(time_from: '8:59').time_from
=> Sat, 01 Jan 2000 08:59:00.000000000 JST +09:00

そのため、入力フォームなどで新規レコードを作成する際のバリデーションでは問題が発生しません。しかし、レコードを保存した後の更新処理で問題が発生する可能性があります。

対策

社内メンバーからも意見をもらい、以下のような対応が考えられると思います。

  • time型カラム参照時に時刻部分だけ抽出した文字列に変換して文字列同士で比較する
  • attributes APIでカスタムタイプを作成しtime型カラムを上書きする

その他に使ったことはないですが、Tod というtime型を扱えるgemがあるようです。

JackC/tod - GitHub

また、本記事で扱った問題をRailsのIssue #51679ですでに取り上げているようでした。こちらも参考になるかもしれません。

まとめ

Railsにおけるtime型同士の比較はデータベースとアプリケーションのタイムゾーンの違いにより、特定の時刻で日付が変更されることがあるため注意したほうが良さそうです。


BPSアドベントカレンダー2024

関連記事

Rails: ActiveRecord標準のattributes APIドキュメント(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。