よりシンプルに日付と時刻の表示書式を整えるには...

  • Rails 2.1からの日付と時刻の表示はUTCが標準になり、そのままの環境では以下のように表示されてしまう...。
<%= user.updated_at %>
2008-08-08 04:52:49 UTC
  • 以前に比べれば日本人にとっては分かり易い表示だが、決定的に問題なのは日本の時刻と9時間ずれていること。

タイムゾーン

  • その環境を決めているのは、config/environment.rbの以下の部分。
  • その部分をコメントアウトしてみると...
# ---------- config/environment.rb ----------
Rails::Initializer.run do |config|
...(中略)...
  config.time_zone = 'UTC'
...(中略)...
<%= user.updated_at %>
Fri Aug 08 04:52:49 +0900 2008
  • 書式が以前に戻った感じになり、相変わらず時刻はUTCだ。
# ---------- config/environment.rb ----------
Rails::Initializer.run do |config|
...(中略)...
  config.time_zone = 'Tokyo'
...(中略)...
<%= user.updated_at %>
2008-08-08 13:52:49 +0900
  • おっ、日本の時刻で見易くなった!
  • しかし、時刻表時の項目が多いとリストが横に長〜くなる。無駄に長い気がする。


strftimeで調整する

  • strftimeで「+900」を取り除く。
<%= user.updated_at.strftime("%Y-%m-%d %H:%M:%S") %>
2008-08-08 13:52:49
  • さらに2段表示にしてみる。
<%= user.updated_at.strftime("%Y-%m-%d<br />%H:%M:%S") %>
2008-08-08
13:52:49

  • これで横幅は節約できるようになった!

ヘルパメソッドにする

  • strftimeを繰り返すことになるのでヘルパに定義してみる。
# ---------- app/helpers/application_helper.rb ----------
module ApplicationHelper
...(中略)...
  def simple_time(time)
    time.strftime("%Y-%m-%d<br />%H:%M:%S")
  end
...(中略)...
  • これで以下のように書ける。
<%= simple_time(user.updated_at) %>
2008-08-08
13:52:49

to_formatted_sメソッドを利用する

  • 以前は上記で終わっていたが、最近、もっとシンプルな技を覚えた。
<%= user.updated_at.to_s(:db) %>
2008-08-08 13:52:49
  • .to_s(:db)は.strftime("%Y-%m-%d %H:%M:%S")と同じ結果をもたらす。
  • そして、以下のようにすることで、.to_s(フォーマット名)に独自のフォーマットを定義できる。
# ---------- config/environment.rb ----------
...(中略)...
Rails::Initializer.run do |config|
...(中略)...
end

# to_s(独自フォーマット)の定義
Time::DATE_FORMATS[:simple] = "%Y-%m-%d<br />%H:%M:%S"
  • これで以下のように書けた!
<%= user.updated_at.to_s(:simple) %>
2008-08-08
13:52:49


左から順番にメソッドが実行されるところが、Rubyらしく、読み易くて好き。

APIマニュアルによると...

# ---------- config/initializers/time_formats.rb ----------
Time::DATE_FORMATS[:simple] = "%Y-%m-%d<br />%H:%M:%S"
  • 最初から、以下の書式が定義されているようだ。
DATE_FORMATS = { :db           => "%Y-%m-%d %H:%M:%S", 
                 :number       => "%Y%m%d%H%M%S", 
                 :time         => "%H:%M", 
                 :short        => "%d %b %H:%M", 
                 :long         => "%B %d, %Y %H:%M", 
                 :long_ordinal => lambda { |time| time.strftime("%B #{time.day.ordinalize}, %Y %H:%M") }, 
                 :rfc822       => "%a, %d %b %Y %H:%M:%S %z" }
http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Time/Conversions.html#M000278
  • lambdaを利用すれば、かなり自由な設定も可能だ!

タイムゾーンを設定する時、気になったこと

  • Rails 2.1からはマイグレーションのバージョン管理にUTCを数字の羅列に変換したものを利用している。config.time_zone = 'Tokyo'としたら、バージョン管理の基準も変わってしまうのでは?
  • コントローラーを限定してタイムゾーンを指定することも可能。
    • 以下ではUsersControllerから描画する時だけ、タイムゾーンが東京になる。
# ---------- app/controllers/users_controller.rb ----------
class UsersController < ApplicationController
   Time.zone = 'Tokyo'
...(中略)...

nilの問題

嬉しくなって.to_s(:simple)を使いまくっていると、問題発生!

  • DBに日付や時刻でなくnilが保存されている場合、エラーが発生してしまった...。
>> nil.to_s(:simple)
ArgumentError: wrong number of arguments (1 for 0)
  • そのような場合、to_s(:simple)のフォーマット定義だけではどうしようもなく、利用する時にnilのチェックが必要になる。
<%= user.updated_at && user.updated_at.to_s(:simple) %>
  • 一方、ヘルパメソッドsimple_timeなら、以下のようnilのチェックも簡単にラップすることができる。
# ---------- app/helpers/application_helper.rb ----------
module ApplicationHelper
...(中略)...
  def simple_time(time)
    time && time.strftime("%Y-%m-%d<br /><br />%H:%M:%S")
  end
...(中略)...
  • でも、どうしてもnilチェックなしで、to_s(:simple)を使いたい!nil.to_s(:simple)がto_sと同じ挙動をしてくれればいいのだが...。
>> nil.to_s
=> ""
  • それを実現するために、手っ取り早くapplication_helper.rbで以下のようにやってみたが...。
# ---------- app/helpers/application_helper.rb ----------
module ApplicationHelper
...(中略)...
end

class NilClass
  def to_s(*args)
    ""
  end
end
  • 一応、出来たが...。
>> nil.to_s(:simple)
=> ""


果たして、こんなことして良いのかな?という思い。どこかに悪い影響が出なければ良いのだが...。