Rails вычисляет диапазон дат в месяцах

Как рассчитать разницу двух дат в месяцах? Кроме того, если это имеет значение, я работаю с объектами Date, а не DateTime. Кроме того, некоторые варианты округления могут быть хорошими, поэтому я могу контролировать, хочу ли я округлять или уменьшать частичные месяцы.

спасибо!

8 ответов


вычитание одной даты или DateTime из другого даст количество дней как дробь, но это может быть оценено как Float или Fixnum по мере необходимости.

например:

(Date.today - Date.today.advance(:months => -3)).to_f
# => 89.0

между сегодняшним днем и той же календарной датой три месяца назад было 89.0 дней. Если вы работаете с 30-дневными месяцами, или 30.4375, как они в среднем, вы заканчиваете с 2.92 месяцами, прошедшими между тогда и сейчас, или округленными до ближайшего целого числа, 3.

Если вы хотите вычислить точное количество календарных месяцев, что сложнее, но может быть сделано.


Это должно дать o.к. приближение:

Date1 - Date2 = difference_in_days
(difference_in_days/30).round = difference_in_months

что-то вроде этого более читаемо, чем выяснение секунд, и даст вам фактическую разницу в календаре:

# 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

Если вам нужно разработать разницу на основе дней, а также, вы можете просто следовать шаблону


нам нужно было что-то в этом роде, но включая частичные месяцы. Таким образом, 1/31 до 2/1 все равно даст 2 месяца. Может помочь!

def self.month_count(range)
  12 * (range.end.year - range.begin.year) + range.end.month - range.begin.month
end

этот ответ опаздывает на вечеринку, основывается на предыдущих ответах и, вероятно, может быть написан более кратко, однако он дает календарную разницу между двумя датами с учетом дней.

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


Как насчет этой практики?

current_date = start_date

while current_date < end_date
  # something  
  current_date = current_date.next_month
end

мне нужно точное количество месяцев (включая дроби) между двумя датами и написал следующий метод для этого.

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

Если сравнивать, скажем, 26 сентября с 26 сентября (в тот же день), я вычисляю его как 1 день. Если вам это не нужно, вы можете удалить первую строку в методе:period_end = period_end + 1.day

он проходит следующие спецификации:

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

он полагается на ActiveSupport Rails.