ja ja ja nb ja en ja ja en ja ja ja en ja gu ja ja ja ja en ja ja en ja en ja ja it ja ja en ja ja en ja ja ja en ja en ja ja ja ja ja ja ja en ja ja gu ja en ja ja ja en en nl ja sv ja ja nb ja ja ru ja ja en ja ja en ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja cv es es ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja
Rails では、DB の Time 型 を扱うことができます。これは日付を必要とせず、時間だけを格納したいときに使います。
定義方法は、他の型と同じく add_column メソッドなどで time を指定するだけです。
Ruby や Rails には、時間のみを扱うクラスはありません。DB の Time 型は Ruby の Time オブジェクトに変換されます。Time オブジェクトは年月日の情報を持っています。その際、タイムゾーンは utc (正確には ActiveRecord::Base.default_timezone で設定されたタイムゾーン) として扱われます。
lunch_time カラムに 12:00 が格納されていた場合、次のような Time オブジェクトが返ります。
日本向けのアプリケーションであれば、 Rails のタイムゾーンを次のように Tokyo に設定するケースはとても多いと思います。この場合、UTC と JST の違いで苦しむことになります。
time_field メソッドで input タグを作りフォームを submit すると、'12:00' のような文字列がサーバに投げられます。
で DB には 12:00 が格納されます。
user.lunch_time #=> 2000-01-01 12:00:00 UTC
先程も書きましたが、Time オブジェクトは年月日の情報を含むので、JSTとの時間を比較するには工夫が必要です。現在時刻が、特定の User のランチタイムより前であるかを調べるには次のようにするとよいでしょう。
t = user.lunch_time t.to_s(:time) > Time.zone.now.to_s(:time)
to_s(:time) とすると、タイムゾーンにかかわらず単純に時間だけが文字列として出力されます。しかしダサいですね…><
時間だけを扱うクラスを提供する gem もあります。これを使うともう少しマシな感じになるかもしれません。
現在時刻以降にランチタイムを設定した User を取得したい場合、次のように書くと思ったとおりに動きません。
User.where('lunch_time >= ?', Time.zone.now)
理由は Time.zone.now が JST から UTC に変換されてからクエリが実行されるからです。
例えば今が日本時間の 11:00 であれば、次のような SQL が発行されるでしょう。
SELECT "users".* FROM "users" WHERE (lunch_time > '2015-05-28 2:00:00.000000')
これでは、2時より後にランチタイムを設定しているユーザを取得してしまいます(※)。想定している挙動と違いますね。これをうまく扱うにはどうしたらよいでしょうか。
※ PostgreSQL で試した場合は、日付の箇所は無視されるようです。sqlite は time 型でも日付が入るようで、結果は異なります。
次のようにするとうまく動きます。
User.where('lunch_time >= ?', Time.zone.now.to_s(:time))
次のようなクエリが発行されます。
SELECT "users".* FROM "users" WHERE (lunch_time > '11:00')
結局 to_s(:time) に頼ってしまいました。もっと綺麗に書く方法があるかもしれませんが、とりあえずはこれでうまく動きます。
Rails 5 ではこうなる
さて、time 型の微妙さは伝わりましたでしょうか。嬉しい事に、Rails 5 では time 型の挙動を変更することができるようになるようです。
Rails 5.0 で time 型をタイムゾーン対応に設定できる
Rails 5.1 で time 型はデフォルトでタイムゾーン対応になる
設定で非対応にもできる
現在の rails の master ブランチを利用し、time 型のカラムを持つ User モデルで User.new などとすると次の warning メッセージが表示されます。
Time columns will become time zone aware in Rails 5.1. This still causes `String`s to be parsed as if they were in `Time.zone`, and `Time`s to be converted to `Time.zone`. To keep the old behavior, you must add the following to your initializer: config.active_record.time_zone_aware_types = [:datetime] To silence this deprecation warning, add the following: config.active_record.time_zone_aware_types << :time
config.active_record.time_zone_aware_types に設定した型のカラムはタイムゾーン対応になるようです。
試しに config/application.rb に次の行を追加してみます。
config.time_zone = 'Tokyo' config.active_record.time_zone_aware_types = [:datetime, :time]
time 型を入力すると、JST の時刻になっているのがわかります。
user.lunch_time = '12:00' user.lunch_time #=> Sat, 01 Jan 2000 12:00:00 JST +09:00
DB には UTC 時間である "03:00:00" が格納されます。
というわけで、Rails 5.0 からは楽に time 型を取り扱えるようになりそうです。
Time columns should support time zone aware attributes by sgrif · Pull Request #15726 · rails/rails
willnet 2015-06-12 06:37
Rails 4.2 で postgresql を使う場合の注意事項
Rails 4.2 では、DB の string 型が255文字制限ではなくなりま…
Rails 4.1.0 で新しく導入された便利メソッド
Rails(ActiveSupport) は標準クラスを拡張した便利メソッド群を…
Rails 3.0 リリースノート和訳 - 後編
お待たせしました。後編です。一週間で書くつもりが一ヶ月かか…
Railsのタイムゾーン雑感
Railsの2.1あたりからタイムゾーンのサポートがされるようにな…
今週のEdge Rails
Riding Rails: This Week in Edge Rails上記の記事を意訳したの…
« find_by_sql したレコードで preload する… ゆるデブ合宿で島根に行ってきた »
id:willnet はてなブログPro
べんりで楽しいwebサービスをたくさん作りたい
読者です 読者をやめる 読者になる 読者になる
@netwillnetをフォロー
失敗したテストのログだけを出力するぞ
iCARE Dev Meetup #34で登壇しました
`Thread.current[:hoge]`はスレッドローカル変数を参照していると思いきや実際はファイバーローカル変数だった