【Rails】日時の文字列をTime.zone.parseしてDBのレコードと一致させる方法

# Time.zoneが一致しなくて検索できなかった問題

筆者がRailsのアプリを開発している時に、
日付を一致させる時にひと悶着あったので、備忘録として残しておきます。

## 今回使用する例

**productsテーブル**

| id | date (datetime型) |
| ---- | ---- |
| 1 | 2020-11-01 15:00:00 |
| 2 | 2020-11-02 00:00:00 |
DBの時刻は`UTC`とします。

# 問題

下記の例題で、`true` or `false`の値と、なぜそれを返す理由が説明できるでしょうか？
RailsアプリのTime.zoneは次の状態だとします。

```.js
[*] pry(#<ProductsController>)> Time.zone
=> #<ActiveSupport::TimeZone:0x000055cc45b31408
 @name="Tokyo",
 @tzinfo=#<TZInfo::DataTimezone: Asia/Tokyo>,
 @utc_offset=nil>
```


## 例題

```.rb
[1] pry(#<ProductsController>)> 
Product.find(1).date == "2020-11-01 15:00:00"

[2] pry(#<ProductsController>)> 
Product.find(1).date == "2020-11-02 00:00:00"

[3] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-01 15:00:00")

[4] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-02 00:00:00")

[5] pry(#<ProductsController>)> 
Product.find(2).date == "2020-11-01 15:00:00"

[6] pry(#<ProductsController>)> 
Product.find(2).date == "2020-11-02 00:00:00"

[7] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-01 15:00:00")

[8] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-02 00:00:00")

[9] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-02 09:00:00")
```

# 回答

```rb
[1] pry(#<ProductsController>)> 
Product.find(1).date == "2020-11-01 15:00:00"
=> true
# 文字列で検索した場合、DBのレコードと一致するものを検索します。
# つまり、2020-11-01 15:00:00という値が一致するのでtrueとなります。

[2] pry(#<ProductsController>)> 
Product.find(1).date == "2020-11-02 00:00:00"
=> false
# 純粋な文字列を比較してるので、
# 2020-11-01 15:00:00 == 2020-11-02 00:00:00はfalseとなります。

[3] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-01 15:00:00")
=> false
# 今回のRailsアプリのTime.zoneはTokyoでした。
# Time.zone.parseとは、文字通りアプリのタイムゾーンに合わせて変換することです。
# UTCとTokyoの時間は9時間ずれてる（Tokyoが遅れてる）
# DBにsaveする時、2020-11-02 00:00:00 ➡️ 2020-11-01 15:00:00に変換されます。
# RailsがDBからレコードを取り出す時、+9時間して返します。

[4] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-02 00:00:00")
=> true
# 上記で説明したとおり、Railsのタイムゾーンと同じ値を検索&parseしたので、trueを返します。

[5] pry(#<ProductsController>)> 
Product.find(2).date == "2020-11-01 15:00:00"
=> false
# 文字列で検索した場合、DBと一致する値を返すため

[6] pry(#<ProductsController>)> 
Product.find(2).date == "2020-11-02 00:00:00"
=> true
# 文字列で検索した場合、DBと一致する値を返すため

[7] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-01 15:00:00")
=> false
# parseした後はDBの値 - 9 されるためfalse
# parseした後は、2020-11-01 06:00:00になる

[8] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-02 00:00:00")
=> false 
# parseした後はDBの値 - 9 されるためfalse
# parseした後は、2020-11-01 15:00:00になる

[9] pry(#<ProductsController>)> 
Product.find(1).date == Time.zone.parse("2020-11-02 09:00:00")
=> true
# parseした後は、2020-11-02 00:00:00になるのでtrue
```

# 言いたかったこと
今回のRailsのTime.zoneはTokyoなので、
バックエンドのデータに-9時間してsaveしてる。

それに合わせて、Time.zoneをTokyoにparseしたら、
欲しかった情報を取得できました！

# 参考

ようやく：こうしきよめ
https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html

助けられたteratail
https://teratail.com/questions/148819

RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い
https://qiita.com/jnchito/items/cae89ee43c30f5d6fa2c

## おまけ
ふと疑問に思ったので、
ransackのコード見たら、Time.zone.parseしてました。

```.rb
def cast_to_time(val)
  if val.is_a?(Array)
    Time.zone.local(*val) rescue nil
  else
    unless val.acts_like?(:time)
      val = val.is_a?(String) ? Time.zone.parse(val) : val.to_time rescue val
    end
    val.in_time_zone rescue nil
  end
end
```