📝
ransackで日付検索する時、タイムゾーンを意識して検索する
概要
問題
Ransack を使用して検索すると、タイムゾーンで日付のズレが発生していた。
前提条件
技術
- Ruby on Rails
- Ransack
タイムゾーン設定
タイムゾーン | |
---|---|
システム ( Ruby on Rails ) | UTC |
Ruby on Rails | Tokyo |
ActiveRecord | UTC |
システム ( DB ) | UTC |
DB ( MySQL ) | UTC |
解決策
目的
年
のみで検索したい。
例えば 2023 を指定すると、2023年に登録したユーザを検索する。
月
のみで検索したい。
例えば 12 を指定すると、登録年に関わらず、12月に登録したユーザを検索する。
実装例
Controller のイメージは以下の通り。
class UsersController < ApplicationController
def index
# クエリパラメータ例
# @q = { created_at_year_eq: "2023", created_atmonth_eq: "12" }
@user = User.ransack(@q)
end
end
Model のイメージは以下の通り。
class User < ActiveRecord::Base
ransacker :created_at_year do
Arel.sql("YEAR(CONVERT_TZ(created_at, 'UTC', 'Asia/Tokyo'))")
end
ransacker :created_at_year, formatter: proc { |v| format('%02d', v) } do
Arel.sql("MONTH(CONVERT_TZ(created_at, 'UTC', 'Asia/Tokyo'))")
end
end
発行されるクエリは以下の通り。
MySQL は CONVERT_TZ
により、タイムゾーンを変換可能であることから、検索対象のカラムを指定のタイムゾーンに変換し、 YEAR
と MONTH
で検索対象の数値を抽出し、検索している。
WHERE
YEAR(CONVERT_TZ(created_at, 'UTC', 'Asia/Tokyo')) = '2023'
AND
MONTH(CONVERT_TZ(created_at, 'UTC', 'Asia/Tokyo')) = '12'
終わりに
今回は、 DB が MySQL の場合の対応となる。
PostgreSQL の場合、タイムゾーン変換の構文が CONVERT_TZ ではないことから、別途調べる必要あり。 Ransack の公式ドキュメントにも実装例はあるため、参照すると良い。
Discussion