今の現場で、以前から9時前にテストを走らせると落ちる問題があり、
不意に原因が分かったので対応しました。
理解が若干曖昧だったので、改めて再現させて、ベストプラクティスを考えてみました。

先ずは、現象を再現させる

前提: 9時前に実行するか、PCの時間を戻して実行

% date
2023年 8月 6日 日曜日 08時37分07秒 JST

ユーザー(何でも良い)を作成して、今日以降に作成したのを取得してみるが、
取れない(想定通り)

% rails c
> FactoryBot.create(:user)
                      created_at: 2023-08-05 23:37:35.960766

> User.where(created_at: Date.today..)
> User.where(created_at: Time.zone.today..)
> User.where(created_at: Time.zone.today.to_datetime..)
  User Load (0.7ms)  SELECT `users`.* FROM `users`
    WHERE `users`.`created_at` >= '2023-08-06 00:00:00'
 => []

原因

DBにはUTCで入っている為、日付だけ見ると前日になっている。

% rails db
> SELECT * FROM users ORDER BY id DESC LIMIT 1\G
                      created_at: 2023-08-05 23:37:35.960766

対策

.to_datetimeがタイムゾーンを持たない(+0000になる)のが原因の為、
.in_time_zoneを使うようにすればOK。
.to_timeでも挙動は問題ないけど、タイムゾーン持ちが明確になる。

% rails c
> User.where(created_at: Time.zone.today.in_time_zone..)
  User Load (1.3ms)  SELECT `users`.* FROM `users`
    WHERE `users`.`created_at` >= '2023-08-05 15:00:00'
 => [#<User <省略> created_at: "2023-08-06 08:37:35.960766000 +0900" <省略>>] 

ベストプラクティス

.to_datetime → ※あえて、+0000にしたい場合は除く
 (日時型と比較する場合).in_time_zone
 (日付型と比較する場合).to_date
.to_time → .in_time_zone

Date.today, Date.current, Time.current.to_date → Time.zone.today
Date.yesterday → Time.zone.yesterday
Date.tomorrow → Time.zone.tomorrow

Time.now → Time.current

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です