Rails3 + ActiveRecord で正しくtimezoneを設定する
アプリケーションのI18N対応をする中で出てくるタイムゾーン問題。今回はRails3環境で
ActiveRecordのTimezoneの設定をどのようにすべきかまとめてみました。
photo credit: Myxi via photo pin cc
Timezoneの問題はOS、DB、Frameworkといろいろ絡みうるので、一つ一つ行きたいと思います。
OSのTimezone
Amazon EC2などだったらそれぞれの地域のタイムゾーンで運用しているケースも
あるかと思いますが、JST(日本標準時)で運用しているところが多いのではないでしょうか。
$ date Tue Jul 3 12:54:53 JST 2012 $ ls -l /etc/localtime lrwxrwxrwx 1 root root 30 Jul 3 10:51 /etc/localtime -> /usr/share/zoneinfo/Asia/Tokyo
DB(MySQL)のTimezone
MySQLの場合、システムのタイムゾーンはローカル時間(JST)に、MySQLのタイムゾーンは
SYSTEM(つまりJST)になっています
$ mysql > show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | JST | | time_zone | SYSTEM | +------------------+--------+
OSのタイムゾーンを変更してDBを再起動するとタイムゾーンがOSにあわせて変更されている様子が分かります
# rm /etc/localtime; ln -s /usr/share/zoneinfo/UTC /etc/localtime # date Tue Jul 3 04:03:05 UTC 2012 # service mysqld restart # mysql > show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | UTC | | time_zone | SYSTEM | +------------------+--------+
Rails3 + ActiveRecordのTimezone
結論からいうとDB自体のタイムゾーンは関係ありません。MySQLのtime_zoneがUTCであれ、
JSTであれActiveRecordで指定したタイムゾーンに変換された時刻が保存されます。
DBのNOW()を呼ぶのではなく、アプリーケーション側でタイムゾーン変換をしてるのでしょうね。
データ保存に利用される設定
- MyApp::Application.config.active_record.default_timezone
- ActiveRecord::Base.default_timezone
$ rails console > MyApp::Application.config.active_record.default_timezone => nil > ActiveRecord::Base.default_timezone => :utc
config/application.rbにて、何もTimezoneの設定をしていない場合、
MyApp::Application.config.active_record.default_timezoneがnilとなり、
ActiveRecord::Base.default_timezoneが:utcとなります。
最終的にはActiveRecord::Base.default_timezoneの値でDBに保存されるため、
この場合は:utcで保存されます。保存に利用するタイムゾーンを変更するには
config/application.rbにて、config.active_record.default_timezoneに
値を設定するとよいでしょう
# config/application.rb config.active_record.default_timezone = :local
config.active_record.default_timezoneは:local もしくは :utcのいずれかを
設定することができます:localを指定するとOSのローカル時間で保存されます
$ rails console > ActiveRecord::Base.default_timezone => :local
config/application.rbを修正したところです。 :localが設定されていることが分かります。
この状態でdatetimeを入力するとDBにはJSTで保存されていることが確認できます。
データの保存時のタイムゾーンはアプリケーションで統一せねばならず、かつその基準はUTCもしくは
OSのローカル時間であろうというとても合理的な設計になっています。
# config/application.rb config.active_record.default_timezone = :utc
データ取得時に利用される設定
- MyApp::Application.config.time_zone
- Time.zone
データ取得時にはTime.zoneの値にしたがってデータが変換されます。
変換されるタイミングは、そのアトリビュートにアクセスしたタイミングになるため、注意してください
(変換時の基準となるDBのタイムゾーンには
MyApp::Application.config.active_record.default_timezoneが利用される)。
Time.zoneのデフォルト値を変更したい時は、config/application.rbのconfig.time_zoneを変更します
# config/application.rb config.time_zone = 'Tokyo'
アクセスするユーザによってタイムゾーンを変更したい場合などは、before_filterなどで
セッション等からユーザ固有の設定タイムゾーンを取り出した上でTime.zoneを上書きするとよいと思います
結論:推奨設定
I18N対応のアプリケーション
以下の設定をした上で、ユーザごとにTime.zoneを設定する
# config/application.rb config.time_zone = 'UTC' config.active_record.default_timezone = :utc
日本のみで利用されるアプリケーション
# config/application.rb config.time_zone = 'Tokyo' config.active_record.default_timezone = :local