Counting the Days Since an Arbitrary Date
Problem
You want to see how many days have elapsed since a particular date, or how many remain until a date in the future.
Solution
Subtract the earlier date from the later one. If you're using Time objects, the result will be a floating-point number of seconds, so divide by the number of seconds in a day:
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.
If you're using DateTime objects, the result will be a rational number. You'll probably want to convert it to an integer or floating-point number for display:
require 'date' def advent_calendar(date=DateTime.now) christmas = DateTime.new(date.year, 12, 25) christmas = DateTime.new(date.year+1, 12, 25) if date > christmas difference = (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_calendar(DateTime.new(2006, 12, 24)) # Only 1 day until Christmas. advent_calendar(DateTime.new(2006, 12, 25)) # Today is Christmas. advent_calendar(DateTime.new(2006, 12, 26)) # Only 364 days until Christmas.
Discussion
Since times are stored internally as numbers, subtracting one from another will give you a number. Since both numbers measure the same thing (time elapsed since some "time zero"), that number will actually mean something: it'll be the number of seconds or days that separate the two times on the timeline.
Of course, this works with other time intervals as well. To display a difference in hours, for Time objects divide the difference by the number of seconds in an hour (3,600, or 1.hour if you're using Rails). For DateTime objects, divide by the number of days in an hour (that is, multiply the difference by 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.
You can even use divmod on a time interval to hack it down into smaller and smaller pieces. Once when I was in college, I wrote a script that displayed how much time remained until the finals I should have been studying for. This method gives you a countdown of the days, hours, minutes, and seconds until some scheduled event:
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.now.offset), "the book deadline") # 27 days, 4 hours, 16 minutes, 9 seconds until the book deadline. remaining(DateTime.new(1999, 4, 23, 8, 0, 0, DateTime.now.offset), "the Math 114A final") # 2521 days, 11 hours, 43 minutes, 50 seconds since the Math 114A final.
See Also
- Recipe 3.5, "Doing Date Arithmetic"