環境
Ruby2.6
Rails5.2
はじめに
Railsでシステム日付を取得する場合は、Ruby標準のTimeメソッド、Dateメソッドは使わずに、Railsで用意されている、TimeWithZoneクラスを使うことになります。
TimeWithZoneクラスはActive Supportの拡張メソッドです。Ruby標準のTimeメソッド、Dateメソッドを使ってシステム日付を取得すると、OSが持っている日付がそのまま取得されるのに対し、TimeWithZoneクラスを使うと、OSのシステム日付がどうであれ、config/application.rbで設定しているタイムゾーンに該当する日付に変換して取得してくれます。
例えば、OSのタイムゾーンがUTC(協定世界時)である場合、Ruby標準のメソッドを使うと、UTCの日付がそのまま取得されます。しかし、TimeWithZoneクラスを利用すると、OSのタイムゾーンがUTCであっても、Railsで下記の設定をしている場合、JST(日本標準時間)に変換して取得してくれます。
config.time_zone='Asia/Tokyo'
OSのタイムゾーンが元々、JSTになっていれば、Ruby標準のメソッドを使っても、TimeWithZoneクラスを使っても、結果的には同じJSTの日付を取得してくれます。Linuxの場合、デフォルトのタイムゾーンはUTCになっています。デフォルトのままで、長年使い続けていることは恐らくないかと思います。大抵の場合、Linuxをインストールした後、コマンドを色々と打ち込んで、CUI操作を覚えている時期のどこかのタイミングで、タイムゾーンをUTCからJSTに変更しているはずだと思います。よって、開発環境においては、Railsでシステム日付を取得するときに、Ruby標準のメソッドを使ったとしても、大抵の場合、問題ないかと思います。
しかし、本番サーバーで動かす段階になると、サーバーのOS環境に依存することなく、JSTでシステム日付を取得するように、Railsで指定しておいた方が、トラブルを未然に回避できるかと思います。考えられることして、サーバーの環境設定は別の人が担当していて、タイムゾーンがどう設定されているか分からないまま、アプリをデプロイしてしまうといった場合です。リスクヘッジの意味合いにおいても、RaisではTimeWithZoneクラスを使っておいた方が無難という言い方はできるかと思います。
ちなみに、TimeWithZoneクラスを使っても、config.time_zone='Asia/Tokyo'を設定しないと、OSのタイムゾーンがJSTになっていたとしても、デフォルトでUTCの日付を取得することになります。
動作確認
Ruby単体の環境で、TimeWithZoneクラスを使う場合は、このように定義する必要があります。
require 'active_support/time'
Time.nowではRubyの標準メソッドを使ってシステム日付を取得していますが、Time.currentでは、RailsのTimeWithZoneクラスを使ってシステム日付を取得しています。TimeWithZoneクラスを使っても、Time.nowと全く同じように取得しています。
puts Time.now # =>2022-10-25 16:56:19 +0900
puts Time.current # =>2022-10-25 16:56:19 +0900
心配しなくても、Time.currentでもTime型で取得されています。
Time.current.class # => Time
何日前、何日後を求めたい場合、Ruby標準のTimeクラスには該当するメソッドが用意されていないため、Dateクラスを使わざるを得ませんでした。TimeWithZoneクラスを使うと、Timeクラスを使って、何日前、何日後を求めることができるようになります。
TimeWithZoneクラスを使うと、今まで、Dateクラスでやっていたことが、Timeクラス一本でほぼほぼ実現できそうなイメージです。かといって、これからは、Dateクラスは使わずに、TimeWithZoneクラス一本に絞って使った方がいいのかと言えば、そういうわけでもありません。
TimeWithZoneクラスを使うメリットは、日付の操作を、Timeクラスで実現できるところにあります。日付と時間とをセットで扱いたい場合は、TimeWithZoneクラスを使うと便利になるかと思います。個人的には、システム日付を取得する以外で、「TimeWithZoneクラス超便利!」と感じたことはなかったりします。「Dateクラスでよくね?」と少しドライだったりします。
取り扱うのはあくまでも日付だけで、時間は扱わないのであれば、Dateクラスを使った方が逆に便利かと思います。日付しか扱わないのに、TimeWithZoneクラスを使うと、00:00:00 +0900という、時間がゴミデータのように、一緒に取得されることとなり、いちいち取り除くのが煩わしくなる場合があるためです。
# TimeWithZoneクラスを使って、何日前、何日後を求める場合です。
puts Time.local(2022,10,26).days_ago(3) # => 2022-10-23 00:00:00 +0900
puts Time.local(2022,10,26).days_since(3) # => 2022-10-29 00:00:00 +0900
# Dateクラスを使って、何日前、何日後を求める場合です。
puts Date.parse("2022/10/26").prev_day(3) # => 2022-10-23
puts Date.parse("2022/10/26").next_day(3) # => 2022-10-29
上記のメソッドを比較して、メソッド名を見て、直感的に何をやっているか分かりやすいのはどちらでしょうか。どちらも同じだと思います。