Rails で datetime型 にテキストをそのまま入れる形で find_or_initialize_by でレコード操作したらタイムゾーンが異なっていたためすべて新規レコードになってしまった

前提

find_or_initialize_by にこだわるのは本質的ではなく、find_bywhere などでも同じです。本質はタイムゾーンの違いです。

結論

具体例を見たほうが早いと思いますので、載せます。

NG な例

user = User.find_or_initialize_by(
  name: 'hoge',
  submitted_at: '2023/04/07 17:48:31'
)

これを繰り返し実行した場合に、常に user.new_record?true を返してしまいます。

OK な例

期待通りの動作をさせるためには、テキストでそのまま入れるのではなく Time.zone.parse します。

ただし、データソースによってはタイムゾーンには注意が必要ですこちらなどを参照)。

user = User.find_or_initialize_by(
  name: 'hoge',
  submitted_at: Time.zone.parse('2023/04/07 17:48:31')
)

これで find でちゃんと見つけてきてくれます。

検討

SQL を見てみるとよく分かります*1

NG な例

User.find_by(submitted_at: "2023/04/07 17:48:31")
#=> SELECT "users".* FROM "users" WHERE "users"."submitted_at" = $1 LIMIT $2  [["submitted_at", "2023-04-07 17:48:31"], ["LIMIT", 1]]

OK な例

User.find_by(submitted_at: Time.zone.parse("2023/04/07 17:48:31"))
#=> SELECT "users".* FROM "users" WHERE "users"."submitted_at" = $1 LIMIT $2  [["submitted_at", "2023-04-07 08:48:31"], ["LIMIT", 1]]

*1:PostgreSQL を使用しています

Powered by はてなブログ