ハトネコエ Web がくしゅうちょう

プログラミングやサーバー・Web制作、チームマネジメントなど得た技術のまとめ

rubocop-rails で to_time が怒られなかったので調査した

1. オプションで指定しない限り to_time は怒られない

コードレビューをしていたら

"2020-01-01 10:00".to_time

のようなコードがあり、
「そこは .in_time_zone の方がいいですよ」とコメントしようとして、「あれ?」と思いました。

なんで「 Time.nowTime.zone.now にしなさい」と
いつもは怒ってくれる rubocop-rails が反応してくれていないのでしょう?

そこでドキュメントを確認すると、
Rails/DateAllowToTimefalse の設定に変更しない限り、
to_time の使用を怒ることはない
とわかります。

2. to_time を使うことの問題

これは少し不思議です。
to_time を使ってしまっては、 config.time_zone で設定している
Railsアプリケーション側のタイムゾーンでなく、システム側(OS側)のタイムゾーンを見に行ってしまいます。

root@9f373dcf6715:/app# export TZ=America/Los_Angeles
root@9f373dcf6715:/app# bundle exec rails c

[1] pry(main)> Time.now
=> 2022-06-15 08:23:08.607609413 -0700
[2] pry(main)> Time.zone.now
=> Thu, 16 Jun 2022 00:23:16.122905467 JST +09:00

[3] pry(main)> "2020-06-01 10:00".to_time
=> 2020-06-01 10:00:00 -0700
[4] pry(main)> "2020-06-01 10:00".in_time_zone
=> Mon, 01 Jun 2020 10:00:00.000000000 JST +09:00

[5] pry(main)> Date.current.to_time
=> 2022-06-16 00:00:00 -0700
[6] pry(main)> Date.current.in_time_zone
=> Thu, 16 Jun 2022 00:00:00.000000000 JST +09:00

Rails/Date の cop は、
Date.today でなく Date.current を使え」だとか、
Time.now でなく Time.zone.now を使え」だとか、
システム側のタイムゾーンでなくアプリケーション側のタイムゾーンを使うよう統一を求めるものです。

であれば、 to_time は使うべきでないはずです。

3. 原因は DateTime にあり

ここでドキュメントに戻りましょう。

AllowToTime is true by default to prevent false positive on DateTime object.

とあります。
DateTimeオブジェクトにおける誤検知を防ぐため」……? どういうことでしょう。

その答えはこちらの issue にあります。

https://github.com/rubocop/rubocop-rails/issues/288

要約すると、DateTimeクラスのときに、
すでにアプリケーション側のタイムゾーン情報を持つことが出来て、
そのタイムゾーン情報は to_time しても引き継がれるから問題ないはずだ、という主張です。

[1] pry(main)> DateTime.current
=> Thu, 16 Jun 2022 00:50:18 +0900
[2] pry(main)> DateTime.current.to_time
=> 2022-06-16 00:50:23.70027614 +0900

なるほど、たしかにこのことを考えると、
to_time メソッドの使用を必ず怒るという挙動だと、余計なおせっかいになってしまうことがありますね。

# ちなみに DateTime.now はシステム側のタイムゾーンを使ってしまう
[3] pry(main)> DateTime.now
=> Wed, 15 Jun 2022 08:50:45 -0700
[4] pry(main)> DateTime.now.to_time
=> 2022-06-15 08:50:52.038531225 -0700

4. しかし DateTime は deprecated

……なのですが、2020年9月の Ruby のコミット
https://github.com/ruby/date/commit/58ca6e6a3ee20c72a77266e0f74920b12a06ee9d にて、
DateTime は deprecated(非推奨) になりました。

話の発端: https://bugs.ruby-lang.org/issues/15712

というわけなので、もう AllowToTimefalse をデフォルトにしてもいいのではと思いまして、
issue を作っておきました。
https://github.com/rubocop/rubocop-rails/issues/715

すぐには変わらないと思うので、
ひとまずは .rubocop.yml の設定を付け足すことで対応したいと思います。

原因がわかってスッキリしました。