Rails でActiveRecord::Baseを継承したモデルのなかで普通に日付の属性にアクセスるるとその型はActiveSupport::TimeWithZoneになる。
今日から数えてその日時が何日後なのかあるいは何日前なのかといったことを表示するためにはActionViewのdistance_of_time_in_words_to_nowを利用すれば実現できます。
ローカルの時間を許容するのであれば、サーバーに負担をかけないためにローカルで動作するtimeago.jsやlivestamp.jsを利用するのもオススメです。
今回問題になったのはある日付からある日付までの時間を計算する必要があったことです。
まず、サーバーになるべく負担をかけないようにローカルで動作するlivestamp.js ( v1.1.1 ) を選択しました。
※livestamp.jsをどのように使うかの説明は割愛します。
このライブラリ(他のもそうですが)ある日付と現在の時間を計算して表示することを目的としていますので、上記の問題にはそぐわなかったわけです。
ちょっとトリッキーな方法で解決しました。
helpers/application_helper.rbなどに
[ror]def between_date_livestamp(from_date, to_date)
(Time.zone.now – (to_date – from_date)).to_time.iso8601
end [/ror]
などとしておいてviewで
[ror]
<span data-livestamp="<%= between_date_livestamp(@a_model.start_date, @a_model.end_date) %>"></span>[/ror]
のようにします。
between_date_livestampメソッドで行っていることは
比較したい二つの日時の「差」を出しておいて、
現在日時からその分離れている架空の日時を返すというものです。
逆にいうとその架空の日時は現在日時から比較したい二つの日時と同じ分だけ離れているので、
本日からどれくらい離れているのかを計算するlivestamp.jsの値として利用できるわけです。
デメリット
・サーバー側で計算したときの「今日」とローカルでlivestamp.jsが計算した「今日」では少なくとも通信時間分のズレがあります。厳密さが必要なときは利用できません。
・ せっかくサーバーに負担をかけぬようlivestamp.jsを使うのに、結局サーバーでの計算が必要。
・htmlの内容に架空の(何の意味もない)日付が記述されてしまう。
追記:(2012/09/17)
上記にバグ(というか見落とし)ありました。
livestampは時間がたつと更新されます。「今の時間」は刻々と代わっていくけれども、終了日時は変わらないのでおかしいことになります。
livestamp.jsの中身を見てみるとpauseというメソッドがあるのですが、グローバルらしいので使えません。
あんまりライブラリはいじりたくないので、かわりにdestroyメソッドを使ってなんとかしました。
[javascript]
$(‘.live-stamp-freeze’).each(function() {
var text;
$(this).livestamp($(this).text());
text = $(this).text();
$(this).livestamp(‘destroy’);
$(this).text(text);
)};
[/javascript]
[ror]
<span class=".live-stamp-freeze"><%= between_date_livestamp(@a_model.start_date, @a_model.end_date) %></span>[/ror]
一度livestampで値を出してからその値を変数に入れておいてlivestampをdestroyします。それから再度textメソッドを使って再設定しています。 かなり強引ですが。。