Railsの日付項目にドハマリした件

RailsでDBのマイグレーションファイルを作成するとtimeとdatetimeがあります。timeは時刻を持つため項目ですが日付情報を持つことができません。しかし、プログラム中からInsertを投げても、”2000年1月1日”として登録されてエラーになりません。

私の場合のドハマリ例

私の場合はdatetimeとすべきだったところをtimeとしていたため、画面から”2019/01/01 5:00″や”2019/01/01″を登録しても、”2000/01/01″になってしまう事例に頭を悩ませていました。すでに修正した部分があるので、下記には、time項目に日付付きデータを登録した場合の挙動を示します。

発生する事象

TimeDetail.create(
     ymd:        Time.zone.parse("2019/06/01")
   , user_id:    1
   , time_from:  Time.zone.parse("2019/06/01 09:00")
   , time_to:    Time.zone.parse("2019/06/01 18:00")
   , exclusion:  Time.parse("2019/06/01 01:00")
   , time_result:Time.parse("2019/06/01 08:00")
 )

コンソールで1行取得した結果

<TimeDetail
    id:          4
  , ymd:         "2019-06-01"
  , user_id:     1
  , time_from:   "2019-06-01 00:00:00"
  , time_to:     "2019-06-01 09:00:00"
  , exclusion:   "2000-01-01 01:00:00"
  , time_result: "2000-01-01 08:00:00"
  , created_at:  "2019-11-03 15:40:43"
  , updated_at:  "2019-11-03 15:40:43"
>

exclusionとtime_resultは「time」でマイグレーションした項目です。登録時には2019/6/1 1:00と2019/6/1 8:00で、エラーになっていません。しかし登録データを見てみると、2000/1/1になっています。明確に「時刻だけを持つ」ケースではtimeでマイグレーションしてもよいですが、日付とともに時刻を持つ場合にはdatetimeとする必要があります。

Rails上の型

下記のように試してみました。マイグレーション時がtime、datetimeであるかを問わず、Rails上の方はActiveSupport::TimeWithZoneとなっているようです。そのためRails上までは「日時」で持っているのかもしれませんが、DBに登録以降、日付が切り捨てられてしまっていると思われます。
>> TimeDetail.last.id.class
=> Integer
>> TimeDetail.last.ymd.class
=> Date
>> TimeDetail.last.user_id.class
=> Integer
>> TimeDetail.last.time_from.class
=> ActiveSupport::TimeWithZone
>> TimeDetail.last.time_to.class
=> ActiveSupport::TimeWithZone
>> TimeDetail.last.exclusion.class
=> ActiveSupport::TimeWithZone
>> TimeDetail.last.time_result.class
=> ActiveSupport::TimeWithZone
>> TimeDetail.last.created_at.class
=> ActiveSupport::TimeWithZone
>> TimeDetail.last.updated_at.class
=> ActiveSupport::TimeWithZone

time項目への登録例

見直し後もtimeでよさそうな項目はあったので、登録処理部分のサンプルとして残しておきます。
TimeDetail.create(
    ymd:        Time.zone.parse("2019/05/01")
  , user_id:    1
  , time_from:  Time.zone.parse("2019/05/01 09:00")
  , time_to:    Time.zone.parse("2019/05/01 18:00")
  , exclusion:  Time.parse("01:00")
  , time_result:Time.parse("08:00")
)

Insert部分のトレース

INSERT INTO "time_details" (
    "ymd"
  , "user_id"
  , "time_from"
  , "time_to"
  , "exclusion"
  , "time_result"
  , "created_at"
  , "updated_at"
) VALUES (?  , ?  , ?  , ?  , ?  , ?  , ?  , ?)  [
    ["ymd",         "2019-05-01"]
  , ["user_id",     1]
  , ["time_from",   "2019-05-01 00:00:00"]
  , ["time_to",     "2019-05-01 09:00:00"]
  , ["exclusion",   "2000-01-01 01:00:00"]
  , ["time_result", "2000-01-01 08:00:00"]
  , ["created_at",  "2019-11-03 09:02:54.160615"]
  , ["updated_at",  "2019-11-03 09:02:54.160615"]
]
コンソールで1行取得した結果
<TimeDetail
    id:          4
  , ymd:         "2019-06-01"
  , user_id:     1
  , time_from:   "2019-06-01 00:00:00"
  , time_to:     "2019-06-01 09:00:00"
  , exclusion:   "2000-01-01 01:00:00"
  , time_result: "2000-01-01 08:00:00"
  , created_at:  "2019-11-03 15:40:43"
  , updated_at:  "2019-11-03 15:40:43"
>