【Rails】開始時間(datetime型)と終了時間にバリデーションをつける


実装内容

掲示板に開始時刻と終了時刻を設定し、人を募集するサイトを作成する。終了時間が開始時間より前にならないようにバリデーションを設定する。(※ついでに、開始時刻が現在日時より前にならないバリデーションも付ける)

環境

Ruby 2.5.0
Rails 6.0.3.4

前提

募集板を作成する時、開始時刻と終了時刻を入力できるように設定しておく。

実際のコード

今回は、募集版をboardと置く。
テーブルの内容は↓の通り

XXXX_create_boards.rb
class CreateBoards < ActiveRecord::Migration[6.0]
  def change
    create_table :boards do |t|
    (中略)
      t.datetime :start_time, null: false
      t.datetime :finish_time, null: false

      t.timestamps
    end
  end
end

開始時間を「start_time」カラムに、終了時間を「finish_time」カラムに設定。

※終了時間は「end_time」でも良かったんですが、「end」を入力する度に、インデントが変わる現象が発生してしまい(恐らくエディタの設定)鬱陶しかったので、「finish_time」にしました。

モデル

以下のようなバリデーションを設定しました。
※一部抜粋しています

models/board.rb
  validates :start_time, presence: true
  validates :finish_time, presence: true
  validate :start_finish_check
  validate :start_check

  def start_finish_check
    errors.add(:finish_time, "は開始時刻より遅い時間を選択してください") if self.start_time > self.finish_time
  end

  def start_check
    errors.add(:start_time, "は現在の日時より遅い時間を選択してください") if self.start_time < Time.now
  end

それぞれ、軽く解説いたします。

models/board.rb
  validate :start_finish_check
  validate :start_check

ここで、それぞれの処理を検証しています。validatesではなく、validateになっていることに注意してください。validateになっていると、「You need to supply at least one validation」と言うエラーが出ます。

  def start_finish_check
    errors.add(:finish_time, "は開始時刻より遅い時間を選択してください") if self.start_time > self.finish_time
 #↑ で開始時間と終了時間の比較
  end

  def start_check
    errors.add(:start_time, "は現在の日時より遅い時間を選択してください") if self.start_time < Time.now
 #↑ で開始時間と現在日時の比較
  end

ここで、それぞれの時間を比較しています。ifが真だった場合は、エラー文が追加されます。書くまでもありませんが、不等号>の向きや、ifunlessと書き換えて書くことも可能です。また、selfを書かなくても同様の処理が行われます。
その辺は、お好みで。

時間の取得

今回はDatetime型の比較でしたが、他の型の場合も同様に実装できると思います。
↓が日時取得の参考記事です
rubyで現在日時を取得する
RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い

参考記事

railsでdatetime型の前後入力のバリデーションをかける方法
【バリデーション】rails 今日の日付以降を指定する方法