以下の内容はhttps://su-kun1899.hatenablog.com/entry/2025/07/07/080000より取得しました。


Rails で年月のみの値を管理する #rails

目的

月次の集計結果や、職歴の開始年月など、日付が意味を持たず年月のみだけを管理したいケースがある。

そういった場合にどう実装するか。

方針

  • 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])$})
          # YYYY-MM 形式の文字列の場合は日付を追加したうえで日付に変換する
          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 してから比較するようにしておく
      cast(raw_old_value) != cast(new_value)
    end
  end
end

initializer で作成した年月型を読み込む。

# app/config/initializers/types.rb
Rails.application.reloader.to_prepare do
  ActiveRecord::Type.register(:year_month, Types::YearMonth)
end

モデル側では attribute で年月型であることを指定する。

class SampleModel < ApplicationRecord
  attribute :sample_month, :year_month
end



以上の内容はhttps://su-kun1899.hatenablog.com/entry/2025/07/07/080000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14