目的
月次の集計結果や、職歴の開始年月など、日付が意味を持たず年月のみだけを管理したいケースがある。
そういった場合にどう実装するか。
方針
- DB上は、DATE型で値を保持する
- 日付は必ず月初日(1日)にして意味を持たせない
文字列等他の形式で保持する方法もあるが、DATE型にするとデータの整合性を守りやすく、ソートや検索時の利便性も高い。
問題点
年月を画面から入力する際、html の input 要素には month 型がある。
<input type="month"> - HTML | MDN
Rails の form にも month_field が用意されている。
month_field | Railsドキュメント
しかし、 month 型はパラメータの値が YYYY-MM
形式の文字列であるため、どこかで日付型に変換する必要がある。
解決策
ActiveRecord::Type::Date
を拡張して年月用の Type を用意し、 YYYY-MM
の文字列を解釈できるようにする。
実装例
年月型。
配置場所は app/models/types
配下にしたが、もっと良さげな場所があるかもしれない。
module Types
class YearMonth < ActiveRecord::Type::Date
def cast_value(value)
date =
if value.is_a?(String) && value.match?(%r{^\d{4}-(?:0[1-9]|1[0-2])$})
Time.zone.parse("#{value}-01").to_date
else
super
end
date&.beginning_of_month
end
def changed_in_place?(raw_old_value, new_value)
cast(raw_old_value) != cast(new_value)
end
end
end
initializer で作成した年月型を読み込む。
Rails.application.reloader.to_prepare do
ActiveRecord::Type.register(:year_month, Types::YearMonth)
end
モデル側では attribute で年月型であることを指定する。
class SampleModel < ApplicationRecord
attribute :sample_month, :year_month
end