Rails とタイムゾーン

Published: 2023/4/19


Time クラスのタイムゾーン、 TimeWithZone のタイムゾーン、 DB に読み書きする datetime のタイムゾーンがある。 DB 自身も now() などでタイムゾーンを持っていたりもする。 それらの個人的なまとめ。

Time クラスのタイムゾーン

Ruby が提供する Time クラスは、 unix time 的なやつ。 タイムゾーンを考慮できて、 utc か OS タイムゾーンのどちらかをオブジェクトに設定可能。 タイムゾーンは hour など、時刻の一部を切り出すメソッドに影響する。

Time.now.hour
# => 12
Time.now.utc.hour
# => 3

ActiveSupport::TimeWithZone

Time オブジェクトのようなノリで、任意のタイムゾーンを扱えるようにした拡張的なクラス。 これに加えて、 Rails は config.time_zone を設定できるようにしていて、ここにタイムゾーンを指定すると、 TimeWithZone まわりのデフォルトのタイムゾーンが、その指定されたものになるようになる。

# 例: 以下は、 OS は JST で、
#
#     config.time_zone = 'Eastern Time (US & Canada)'
#
# した場合の挙動。


Time.now
# => 2023-04-19 13:07:56.213691 +0900
Time.current
# => Wed, 19 Apr 2023 00:07:58.307798000 EDT -04:00

Rails 中で unix time 的な時刻を扱いたいときには、 Time よりもこちらの TimeWithZone を利用する方が、一般的に推奨されている。

DB に読み書きする際のタイムゾーン

DB と時刻に関するデータをやりとりする際には、特定の UTC 時刻をどう表すかと、それをどう ruby に変換するかが問題になる。

ruby 側の Dto としては、これまで見てきたとおり、 Time も TimeWithZone も UTC 時刻を情報として持っているので、そのいずれかが指定されれば、 ActiveRecord はよろしく動ける。

DB 側では、DB として DateTime (without timezone) でデータを持っているか、 unix time でデータを持っているかで話が変わってくる。 (補足: created_at などは DateTime without timezone がデフォルト)

ここで、 rails は設定として、 config.activerecord.default_timezone:local:utc で指定できるようになっている。 DateTime だった場合には、この default_timezone の datetime が DB に記録されているものとして ActiveRecord はクエリなどを組み立てる。 (メソッドの内部で unix time を構築する際に Time.local を使うか Time.utc を使うかのフラグである。)

これによって datetime も timestamp も unix time として取り扱え、 ActiveRecord はオブジェクトの読み出しの際には TimeWithZone が利用される。

おまけ: DB のタイムゾーン

DB も、 DB が動いているサーバーないしグローバル設定としての timezone を考慮して挙動が変わったりする関数(NOW())だったりデータ型だったりがある場合があるので、それはそれで注意が必要。


Tags: rails

関連記事