Reputation: 2638
How do I calculate the difference of two dates in months? Also, incase it makes a difference, I am working with Date objects, not DateTime. Also, some rounding options might be nice so I can control if I want to round up or down on partial months.
Thanks!
Upvotes: 11
Views: 17524
Reputation: 2284
I needed the exact number of months (including decimals) between two dates and wrote the following method for it.
def months_difference(period_start, period_end)
period_end = period_end + 1.day
months = (period_end.year - period_start.year) * 12 + period_end.month - period_start.month - (period_end.day >= period_start.day ? 0 : 1)
remains = period_end - (period_start + months.month)
(months + remains/period_end.end_of_month.day).to_f.round(2)
end
If comparing let's say September 26th to September 26th (same day) I calculate it as 1 day. If you don't need that you can remove the first line in the method: period_end = period_end + 1.day
It passes the following specs:
expect(months_difference(Date.new(2017, 8, 1), Date.new(2017, 8, 31))).to eq 1.0
expect(months_difference(Date.new(2017, 8, 1), Date.new(2017, 8, 30))).to eq 0.97
expect(months_difference(Date.new(2017, 8, 1), Date.new(2017, 10, 31))).to eq 3.0
# Overlapping february (28 days) still counts Feb as a full month
expect(months_difference(Date.new(2017, 1, 1), Date.new(2017, 3, 31))).to eq 3.0
expect(months_difference(Date.new(2017, 2, 10), Date.new(2017, 3, 9))).to eq 1.0
# Leap year
expect(months_difference(Date.new(2016, 2, 1), Date.new(2016, 2, 29))).to eq 1.0
It relies on Rails' ActiveSupport.
Upvotes: 0
Reputation: 321
This answer is late to the party, builds on previous answers, and could probably be written more concisely, however, it does give the calendar difference between two dates taking days into account.
def difference_in_months(start_date, today)
date_to_months(today) - date_to_months(start_date) + adjustment_for_days(start_date, today)
end
def date_to_months(date)
date.year * 12 + date.month
end
def adjustment_for_days(earlier_date, later_date)
if later_date.day == earlier_date.day
0
elsif later_date.day > earlier_date.day
1
else
-1
end
end
Upvotes: 1
Reputation: 479
how about this practice?
current_date = start_date
while current_date < end_date
# something
current_date = current_date.next_month
end
Upvotes: 0
Reputation: 665
We needed something along these lines, but inclusive of partial months. So 1/31 to 2/1 would still yield 2 months. Might help!
def self.month_count(range)
12 * (range.end.year - range.begin.year) + range.end.month - range.begin.month
end
Upvotes: 5
Reputation: 7340
Something like this is more readable than figuring out seconds, and will give you the actual calendar difference:
# Calculate differnce between two dates in months
# Produces b - a
def month_difference(a, b)
difference = 0.0
if a.year != b.year
difference += 12 * (b.year - a.year)
end
difference + b.month - a.month
end
If you need to work out the difference based on days as well, you can just follow the pattern
Upvotes: 5
Reputation: 211540
Subtracting one Date or DateTime from another will yield the number of days as a fraction, but this can be evaluated as a Float
or Fixnum
as required.
For instance:
(Date.today - Date.today.advance(:months => -3)).to_f
# => 89.0
There were 89.0 days between today and the same calendar date three months ago. If you work this using 30-day months, or 30.4375 as they are on average, you end up with 2.92 months elapsed between then and now, or rounded up to the nearest integer, 3.
If you want to compute the precise number of calendar months, that is trickier, but can be done.
Upvotes: 11
Reputation: 18463
This should give an o.k. approximation:
Date1 - Date2 = difference_in_days
(difference_in_days/30).round = difference_in_months
Upvotes: 4