Ruby
Rails

日付の差分を「○年△ヶ月」で取得する in Rails

More than 3 years have passed since last update.

ある期間の開始日時が分かっていて、現在まで何年何ヶ月以上経過しているのかの差分を取って欲しい、みたいな要件が Rails でありました。

はじめに考えた

Rails にはdistance_of_time_in_words_to_nowというまさになヘルパーメソッドがあるのでそれの利用を検討したんですが、このメソッドは「1分」「3分」「5分」みたいに近い時間は細かく扱ってくれるけど、期間が長くなると「約1ヶ月」「約2年」みたいに結構マルっと丸めてくれるので用途に合いませんでした。ということで自前で実装することにしました。

日付の差分を「○年△ヶ月」で取得する

  def distance_of_time_in_words_to_now(from_time)
    distance_of_time_in_words(from_time, Time.now)
  end

  def distance_of_time_in_words(from_time, to_time)
    from_time = from_time.to_date if from_time.respond_to?(:to_date)
    to_time = to_time.to_date if to_time.respond_to?(:to_date)
    years = 0
    months = 0
    loop {
      from = from_time + years.year + months.month
      if    (to_time - (from + 1.year))  >= 0 then years  += 1
      elsif (to_time - (from + 1.month)) >= 0 then months += 1
      else break
      end
    }
    distance = ''
    distance += "#{years}年 " if years != 0
    distance += "#{months}ヶ月"
  end

from を「1年」or「1ヶ月」ずつズラしていって「○年△ヶ月」を計算するようにしました。もう少し書き足せば「○年△ヶ月□日」とかもイケそうですね。

まとめ

こういうときの悩みどころは「1年」や「1ヶ月」の扱いだと思います。「1年 = 366日」の年もあれば「1ヶ月 = 28日」の月もあるわけで、どうやってその辺をうまく「1年」「1ヶ月」として扱うかということ。
自前で「?年?月だからこの月は 28日」とか「?年だから 366日」とか実装することを考えると・・・いや考えたくないですね。今回は ActiveSupport にその辺を任せることでうまく実装できました。ありがとう ActiveSupport。