レシピ1.6 任意の日付からの経過日数

問題

特定の日付から何日が経過したか、あるいは未来の
日付まであと何日残っているか知りたい。


解決
 新しい方お日付から古い方の日付を引く。Timeオブジェクトを使用している場合、結果は浮動小数点で表された秒数になるため、それを1日の秒数で割る。

def last_modified(file)
 t1 = File.stat(file).ctime
 t2 = Time.now
 elapsed = (t2-t1)/(60*60*24)
 puts "#{file} was last modified #{elapsed} days ago."
end

last_modified("/etc/passwd")
# /etc/passwd was last modified 125.873605469919 days ago.
last_modified("/home/leonardr/")
# /home/leonardr/ was last modified 0.113293513796296 days ago.

DateTimeオブジェクトを使用している場合、結果は有理数となる。これを表示するには、恐らく整数、または浮動小数点に変換することになるだろう。

require 'date'
def advent_calendar(date=DateTime.now)
 christmas = DatetTime.new(date.year, 12, 25)
 christmas = DateTime.new(date.year+1, 12, 25) if date > christmas
 differnce = (christmas-date).to_i
 if difference == 0
  puts "Today is Christmas."
 else
  puts "Only #{difference} day#{"s" unless difference==1} until Christmas."
 end
end

advent_calencar(DateTime.new(2006, 12, 24))
# Only 1 day until Christmas.
advent_calender(DateTime.new(2006, 12, 25))
# Today is Christmas.
advent_calendar(DateTime.new(2006, 12, 26))
# Only 364 days until Christmas.

解説
 時間の内部表現は数値なので、ある時間から別の時間を引くと数値が得られる。両方の数値は同じ物「(0時)からの経過時間」として評価されるため、その数値は実際に何かを意味するはずだ。すなわち、時系列上で2つの時間を区切る秒数または日数となる。
 もちろん、これは他の時間間隔にもあてはまる。時間差を時単位で表示するとしたら、Timeオブジェクトの場合は、時間差を1時間の秒数(3,600またはRailを使用する場合は1.hour)で割る。DateTimeオブジェクトの場合は、1時間あたりの日数で割る(つまり、時間差に24を掛ける。)

sent = DateTime.new(2006, 10, 4, 3, 15)
received = DateTime.new(2006, 10, 5, 16, 33)
elapsed = (received-sent) * 24
puts "You responded to my email #{elapsed.to_f} hours after I sent it."
#You responded to my email 37.3 hours after I sent it.

時間の間隔にdivmodを使用して、更に細かく区切ることも出来る。筆者は大学生だった頃期末試験までに残された勉強時間を表示するスクリプトを書いた。このメソッドは、予定されたイベントまでの日数、時間数、糞数、秒数をカウントダウンする。

require 'date'
def remaining(date, event)
 intervals = [["day", 1], ["hour", 24], ["minute", 60], ["second", 60]]
 elapsed = DateTime.now -date
 tense = elapsed > 0 ? "since" : "until"
 interval = 1.0
 parts = intervals.collect do |name, new_interval|
  interval /= new_interval
  number, elapsed = elapsed.abs.divmod(interval)
 "#{number.to_i} #{name}#{"s" unless number ==1}"
 end
 puts "#{parts.join(", ")} #{tense} #{event}."
end

remaining(DateTime.new(2006, 4, 15, 0, 0, 0, DateTime.new.offset),
                "the book deadline")
# 27 days,  4 hours, 16minutes, 9 secondsuntil the book deadline.
remaining(DateTime.new(1999, 4, 23, 8, 0, 0, DateTime.now.offset),
                 "the Math 114A final")
# 2521 days, 11 hours, 43minutes, 50 seconds since the Math 114A final.

参照
・レシピ1.5