Rails3系のtimezoneの扱いに関するメモ

  • 136
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

ローカルタイムに関する設定

Rails3系からデータベースにはUTCで保存、取り出して扱う際に必要なタイムゾーンに変更するというのが基本的な考え方の様子。

タイムゾーンの設定はapplication.rbにて行えるが、主に関係する設定が2つある。

  • config.active_record.default_timezone
  • config.time_zone

config.active_record.default_timezone

データベースに保存する際のタイムゾーンを指定するディレクティブ。先ほど書いたようにRails3からはデータベースに時刻を保存する場合は基本的にUTCで保存するようになったみたいで、これをローカルタイムで保存するには以下のように設定すれば良い。

config/application.rb
config.active_record.default_timezone = :local

config.time_zone

こちらが色々悩ましそうな感じで今確認出来たところだとActiveSupport::TimeZoneクラスの出力に影響を与える様子。試しに何も設定しない場合とtime_zoneをtokyoにした場合で色々出力してみると

設定しない場合

Time.zone # => (GMT+00:00) UTC
Time.zone.now # Mon, 24 Sep 2012 09:19:35 UTC +00:00 

Tokyoにした場合

config/application.rb
config.time_zone = 'Tokyo' 
Time.zone # => (GMT+09:00) Tokyo
Time.zone.now #=> Mon, 24 Sep 2012 18:16:43 JST +09:00

とこの様に出力が変更されていることが確認できた。注意が必要なのはActiveSupport::TimeZoneを使うにはTime.zoneで処理を行う必要がありTime.nowのようにTimeで直接処理すると設定に関係なくローカルタイムで表示されてしまう点。

Time / Time.zoneの違い

Time.class      # => Class (常にローカル扱い)
Time.zone.class # => ActiveSupport::TimeZone (config.time_zoneの設定に依存) 

ということでユーザによってタイムゾーンを変える必要があるアプリケーションは
* config.time_zone = 'Tokyo' を設定する
* Time.zoneを常に使う (追記: ActiveRecordと併用する場合はto_s(:db)でUTCに変換 => DB登録時に更に時刻の差分を補正と2重に処理が走ってしまうので注意)

更に追記、config.time_zoneを変更してしまうとActiveRecordを利用する際に
* オブジェクトを生成したタイミングでタイムゾーンをUTCに合わせようとする
* Arelなどwhereをする際にカラムの値で指定する場合などでは合わせようとしない
という違いが発生してしまう。

オブジェクトを生成した場合の挙動
created_at = "2012-09-01 00:00:00"
@example = Example.new(:created_at => created_at)
@example.created_at # => 2012-08-31-15:00:00
Arelを利用する場合の挙動
created_at = "2012-09-01 00:00:00"
Example.where(:created_at => created_at).to_sql # => SELECT `examples`.* FROM `examples`  WHERE `examples`.`created_at` = '2012-09-01 00:00:00

なので、結果的には何も設定せずにユーザ毎に自前で処理するのが良いかもしれないです…

常にローカルタイムで良いアプリケーションは
* config.active_record.default_timezone = :localを設定する
* config.time_zone = 'Tokyo'

って感じが良いのだろうか?

Tips

データベース用のフォーマット変換する

Time.zone.to_s(:db)は使ってしまうと事故が起きそう…

Time.zone.now.to_s(:db) # UTCに自動的に変更される
Time.zone.now.strftime("%Y-%m-%d %H:%M:%S") # ローカルタイムのまま
Time.now.to_s(:db)      # ローカルタイムのまま