背景とやったこと
Railsのアプリで、こんな感じのフォームを作っていました。
実施日をdate_field
で選択し、開始時間・終了時間をそれぞれtime_select
で選択します。
viewファイルの中身はこんな感じです。データ型は、全てのフィールドでDateTime
型でした。
.form-group
= f.label :date, "実施日"
= f.date_field :date
.form-group
= f.label :begin_at, "開始時間"
= f.time_select :begin_at
.form-group
= f.label :closed_at, "終了時間"
= f.time_select :closed_at
起こっていた問題
このフォームから、データを送信すると、次のようになりました。
#<Activity:0x00007fc97030fe30
id: 1,
date: Fri, 11 Sep 2020 00:00:00 JST +09:00,
begin_at: Mon, 1 Jan 0001 00:10:00 JST +09:00,
closed_at: Mon, 1 Jan 0001 00:20:00 JST +09:00
time_took: 600,
created_at: Fri, 04 Sep 2020 13:53:01 JST +09:00,
updated_at: Fri, 04 Sep 2020 13:53:01 JST +09:00>
…はい??
何で、西暦1年の1月1日の時間になっているのでしょうか....。
いろいろ調べたところ、これはRailsの仕様でしょうがないようです。
こちらの記事のように時間だけを入力させる方法もあるにはありました。
(ただし、begin_at
、closed_at
のデータ型はTime型にする必要がありそうです)
ただ、今回は、time_took
という別カラムで「かかった時間」を計算していたり、今後これらの数字を分析に使うことが予想されていたりと、
色々とbegin_at
、closed_at
の値を使い回しそうだったので、正確に日時を表すよう、time_select
フォームの入力値に、date
フィールドで入力した年・月・日を入れることにしました。
やったこと
モデルに以下のようなメソットを定義し、それをbefore_save
コールバックで呼び出します。
class Activity < ApplicationRecord
before_save :set_the_day_implement
private
def set_the_day_implement
year = self.date.year
month = self.date.month
day = self.date.day
self.begin_at = self.begin_at.change(year: year, month: month, day: day)
self.closed_at = self.closed_at.change(year: year, month: month, day: day)
end
end
コードの解説です。self.date.year
のかたちで、フォームから入力した「実施日(date
)」の年をそれぞれ取り出すことができます。month, dayもそれぞれ同様です。
# dateフィールドの値が '2020-09-04'の場合
self.date.year
#=> 2020
self.date.month
#=> 9
self.date.day
#=> 4
また、self.begin_at
、self.closed_at
の値には、それぞれ以下のような形でアクセスできます。
self.begin_at
#=> Mon, 1 Jan 0001 00:10:00 JST +09:00
self.begin_at.year
#=> 1
self.begin_at.month
#=> 1
self.begin_at.day
#=> 1
self.begin_at.change(year: 2020, month: 9, day: 4)
#=> Fri, 4 Sep 2020 00:10:00 JST +09:00
ただし、最後にself.begin_at
に、self.begin_at.change(引数)
で定義した内容を代入しないと、カラムの値は更新されません。(←ここに気づかなくて結構ハマった)
# dateフィールドの値が '2020-09-04'の場合
year = self.date.year
month = self.date.month
day = self.date.day
self.begin_at.change(year: year, month: month, day: day)
#=> Fri, 4 Sep 2020 00:10:00 JST +09:00
self.begin_at
#=> Mon, 1 Jan 0001 00:10:00 JST +09:00
# 西暦1年に戻っている
self.begin_at = self.begin_at.change(year: year, month: month, day: day)
self.begin_at
#=> Fri, 4 Sep 2020 00:10:00 JST +09:00
# 代入するとカラムの値が更新される
さて、これで無事欲しいデータを手に入れることができました。
最終的にビューで表示されるのはこんなデータです。(カッコ内の数字は、別のコールバックで作成した「かかった時間」です。)
今後活用が予想されるデータなので、正しい日付のデータがきちんと入って満足なのでした
引き続きお仕事頑張っていきます♪