Найдите количество месяцев между двумя датами в Ruby on Rails

95

У меня есть два объекта DateTime Ruby on Rails. Как узнать количество месяцев между ними? (Имея в виду, что они могут принадлежать разным годам)

волшебник-феникс
источник
5
Был задан вопрос и ответил здесь: stackoverflow.com/questions/5887756/…
Тим Баас,
спасибо, что указали на это. Я искал, но не наткнулся.
phoenixwizard
обратитесь к этой ссылке stackoverflow.com/questions/1065320/…
sangeethkumar
Я предпочитаю ссылку в цифрах. Я сделал это методом,
описанным
Просто я дал это для вашей справки .. В любом случае спасибо ..
sangeethkumar

Ответы:

179
(date2.year * 12 + date2.month) - (date1.year * 12 + date1.month)

подробнее на http://www.ruby-forum.com/topic/72120

Массимилиано Пелузо
источник
25
Это решение не учитывает дни. Количество месяцев между 28.04.2000 и 05.01.2000 равно не 1 месяцу, а 0.
dgilperez
15
Мне нужна одна без учета дней, так что это работает для меня. +1
WattsInABox
1
Еще один вариант этого отличного ответа:(12 * (date2.year - date1.year) + date2.month - date1.month).abs if date1 && date2
Степан Захаров
Он показывает неправильное количество месяцев, если мы пытаемся получить месяцы между 2 годами, то есть с 2017-05-20 по 2018-05-20. Вывод показывает 1 месяц.
Любопытный разработчик
1
@CuriousDeveloper Это показывает корректные месяцев , то есть 12
т.с.
40

Более точный ответ - это дни на расстоянии.

Например, если вы считаете, что расстояние в месяц от 28/4/2000и 1/5/2000, 0а не 1, то вы можете использовать:

(date2.year - date1.year) * 12 + date2.month - date1.month - (date2.day >= date1.day ? 0 : 1)
Джилперез
источник
1
например, для подсчета количества раз, когда кто-то получал свою зарплату, всегда приходилось на 22-е число месяцев
Маттиас
15

Попробуйте

((date2.to_time - date1.to_time)/1.month.second).to_i
узловатый
источник
irb>Time.at("2014-10-01".to_time - "2014-12-01".to_time).month => 10 (Я отказываюсь от форматирования ... не могу понять)
Тоби Джойнер
Даже если вы используете объект Date, комментарий @toby-joiner все равно будет правильным. Причина в том, что <октябрь> - <декабрь> составляет -2 месяца, но Time.at (-2 месяца) .month дает месяц, эквивалентный -2 (т.е. -2% 12 # => 10). Предлагаемый вами метод будет работать, если два месяца следуют один за другим и с интервалом менее года, но он не будет работать, если два месяца расположены в обратном порядке (например, октябрь - декабрь) или если два месяца разнесены более чем на год.
elreimundo 09
2
Мне нравится идея, лежащая в основе этого, но это неверно, если срок становится очень длинным. В моем примере Date.new(2014, 10, 31), Date.new(1997, 4, 30)я получил 213 вместо 210 месяцев. Причина в том, что 1.month.secondsколичество секунд в 30 днях, а не среднее количество секунд в месяц. Голосование против, чтобы другие остались без ошибок.
Джо Эйферт
1
это не будет работать , если вы выбираете месяц , что не 30 дней
дима
2
этот метод не очень точен.
vagabond
9

Вы можете перефразировать вопрос, как «сколько первых дней проходит между началом месяцев дат», а затем использовать преобразования данных в функциональном стиле:

(date1.beginning_of_month...date2.beginning_of_month).select { |date| date.day == 1 }.size
shock_one
источник
И чтобы вернуться к исходному вопросу, вы можете просуммировать дни (как вы делаете с первым) и взять среднее значение сумм.
Джо Эйферт
9

Предполагая, что оба являются датами: ((date2 - date1).to_f / 365 * 12).round просто.

Андрейкуль
источник
Очень красивое решение! Чисто, просто и даже работает по годам, то есть в диапазоне месяцев с февраля 2018 года по декабрь 2017 года.
jeffdill2
1
Необходимо учитывать часы, минуты и секунды ((date2 - date1).to_f / 60 / 60 / 24 / 365 * 12).round
scarver2 02
1
@ scarver2 Число, полученное вами в результате расчетов, неверно. Все, что мы делаем, это берем дни и преобразуем их в месяцы, это не очень точно, потому что предполагает 30,4167 дней в месяц, но очень близко, а затем все равно округляется.
Андрейкул 02
3
start_date = Date.today
end_date   = Date.today+90
months = (end_date.month+end_date.year*12) - (start_date.month+start_date.year*12)

//months = 3
Анкит Варшней
источник
2

Другое решение, которое я нашел (построенное на уже опубликованном здесь решении), предназначено для тех случаев, когда вы хотите, чтобы результат включал доли месяца. Например расстояние 1.2 months.

((date2.to_time - date1.to_time)/1.month.second).round(1) #Tenth of a month Ex: 1.2
((date2.to_time - date1.to_time)/1.month.second).round(2) #Hundreth, ex: 1.23 months
etc...
Зак
источник
1
попробуйте поиграть с полом, если хотите показать только фактически прошедшие месяцы
Маттиас
2
def difference_in_months(date1, date2)
  month_count = (date2.year == date1.year) ? (date2.month - date1.month) : (12 - date1.month + date2.month)
  month_count = (date2.year == date1.year) ? (month_count + 1) : (((date2.year - date1.year - 1 ) * 12) + (month_count + 1))
  month_count
end
Угур Йылмаз
источник
1
Было бы полезно, если бы вы могли подробнее рассказать об этом? Обычно ответы SO включают объяснение того, что делает код и почему он работает как решение
Single Entity
1

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

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
mtrolle
источник
кстати, он полагается на ActiveSupport Rails
mtrolle
1

Вот вариант перебора:

date1 = '2016-01-05'.to_date
date2 = '2017-02-27'.to_date
months = 0

months += 1 while (date2 << (count+1)) >= date1
puts months # => 13

date2 всегда должно быть больше, чем date1

оле
источник
0

Вот еще один способ. Это поможет рассчитать количество полных месяцев между двумя датами.

def months_difference(date_time_start, date_time_end)
  curr_months = 0
  while (date_time_start + curr_months.months) < date_time_end
    curr_months += 1
  end
  curr_months -= 1 if (date_time_start + curr_months.months) > date_time_end
  curr_months.negative? ? 0 : curr_months
end
Дхануш Балачандран
источник
0

Решение для любых случаев

(date1..date2).map { |date| date.strftime('%m.%Y') }.uniq.size
Николай Чумин
источник