LoginSignup
301
239

Railsタイムゾーンまとめ

Last updated at Posted at 2018-06-11

以下の4個の設定を考慮する必要がある。

  1. rubyプロセスのタイムゾーン
  2. config.time_zone
  3. config.active_record.default_timezone
  4. DBサーバ(postgres等)のタイムゾーン

1. rubyプロセスのタイムゾーン

  • OSのタイムゾーン(/etc/localtime)または環境変数TZにより決まる。
  • Time.nowDate.todayに影響する。

2. config.time_zone

  • ActiveSupport::TimeWithZoneに影響する。
    • ActiveRecordの日時のフィールド(created_atなど)はこのクラスなので、影響を受ける。
    • Time.current Time.zone.now Time.zone.local もこのクラスなので影響を受ける。

Railsのデフォルトでは"UTC"である。

3. config.active_record.default_timezone

  • DBに読み書きする時刻に影響する。
    • DBに書かれている時刻をどのタイムゾーンとして解釈するか。
    • 時刻をDBに書き込むとき、どのタイムゾーンに変換して書き込むか。

postgresの場合、Railsのmigrationでdatetime型を指定するとtimestamp without time zone型のカラムになる。この型は名前の通りタイムゾーン情報を持たない 1。したがってこのカラムに2018-06-11 01:23:45という値が入っていても、これがどこのタイムゾーンにおける時刻なのかはどこにも記録されていない。そこでRailsはconfig.active_record.default_timezoneの指定に従ってこの時刻を解釈する。:localの場合はrubyプロセスのタイムゾーンであると解釈し、:utcの場合はUTCつまり2018-06-11 01:23:45+00:00と解釈する。

Railsのデフォルトでは:utcである。

4. DBサーバ(postgres等)のタイムゾーン

  • postgresの場合はshow timezoneで確認できる。
  • SELECT now()などに影響する。

おすすめ設定

(A) 1〜4すべてをJSTで統一
(B) 1, 2(Ruby側)はJSTにし、3, 4(DB側)はUTCにする
(C) 1〜4すべてをUTCで統一

日本国内でだけ開発・運用するなら(A)がおすすめ。
初期設定の手間こそかかるものの、一度設定してしまえば時刻データを見たとき頭の中で日本時間に変換する必要がなくなるので非常に楽である。アプリケーションにおいて時刻の重要度が高ければ高いほどそのメリットも大きくなる。

世間的には(B)もかなり一般的なようである。
Railsのデフォルトはconfig.time_zone = "UTC"だし、DBサーバもAWS RDSやDockerなどではUTCがデフォルトになっている場合が多い。なので設定の手間を大幅に省ける。
(B)にする場合、RailsからのみDBを読み書きするなら特に何も意識しなくても問題は出ないが、Rails以外のフレームワークやSQLを使って読み書きする場合は、時刻をUTCに変換する必要があるか?を常に意識する必要がある。

グローバルなアプリケーションでは(C)が一般的なようである。
参考:https://blog.studysapuri.jp/entry/2016/12/05/090000

(A)すべてをJSTで統一する場合

ruby (OS) のタイムゾーン

ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

DBサーバ(postgres)

-- 現在のセッションのみタイムゾーンを変更するには
set time zone 'Asia/Tokyo';

-- 永続的にデータベースのタイムゾーンを変更するには
ALTER DATABASE d1 SET TIMEZONE TO 'Asia/Tokyo';

-- 永続化するにはpostgresql.confに
-- timezone = 'Japan'
-- と書いて再起動する。

-- 参考: https://zenn.dev/team_zenn/articles/postgresql-timestamp

Rails

config/application.rb
    config.time_zone = 'Tokyo'
    config.active_record.default_timezone = :local

コーディング規約

Rails上ではTimeクラスの代りにTimeWithZoneクラスを使うのがよいと言われている(参考:Rails 時刻処理では、Time.current, Time.zone.local を使う)。

非推奨 推奨
Time TimeWithZone
Time.now Time.zone.now (Time.currentでも同じ)
Time.parse('2007-02-10 15:30:45') Time.zone.parse('2007-02-10 15:30:45')
Time.at(1170361845) Time.zone.at(1170361845)

TimeとTimeWithZoneの違い

TimeはUTCとOSのタイムゾーンの2種類しか扱えない。TimeWithZoneは複数のタイムゾーン間を相互変換できる。

[2] pry(main)> a = Time.current               # 現在の日本時間
=> Tue, 12 Jun 2018 06:41:57 JST +09:00
[3] pry(main)> a.in_time_zone('Moscow')       # モスクワ時間に変換(日本との時差は6時間)
=> Tue, 12 Jun 2018 00:41:57 MSK +03:00
[4] pry(main)> a.in_time_zone('Moscow').in_time_zone('Hawaii')  # それをさらにハワイ時間に変換
=> Mon, 11 Jun 2018 11:41:57 HST -10:00
  1. postgresにはtimestamp with time zoneというデータ型もあるが、これは名前から推測されるのと違って、内部的にはUTCで保存され、入力時にどのタイムゾーンであったかは保存されない。2019-05-24T12:00:21+09:00のような値を入れたとき、timestamp without time zone型だと+09:00部分が無視されるのに対し、timestamp with time zone型だと+09:00部分も解釈されてUTCに変換されてから保存される。取り出すときのタイムゾーンはpostgresセッションのタイムゾーンになる。入れたときどのタイムゾーン表記だったかの情報は保存されない。

301
239
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
301
239