Знайдіть кількість місяців між двома датами в Ruby on Rails


95

У мене є два об’єкти DateTime Ruby on Rails. Як знайти кількість місяців між ними? (Маючи на увазі, що вони можуть належати до різних років)


5
Тут було допитано та відповідено: stackoverflow.com/questions/5887756/…
Тім Баас

дякую, що вказали на це. Я шукав, але не натрапив на це.
phoenixwizard


Я швидше хочу посилання в цифрах. Я зробив це методом, зазначеним Массіміліано Пелусо
Феніксмайстер

Тільки я дав його для довідки .. У будь-якому випадку дякую ..
sangeethkumar

Відповіді:


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

докладніше на http://www.ruby-forum.com/topic/72120


25
Це рішення не враховує дні. Кількість місяців між 28/4/2000 і 1/5/2000 - це не 1 місяць, а 0.
dgilperez

15
Мені потрібен той, не враховуючи днів, тому це працює для мене. +1
WattsInABox

1
Ще один варіант цієї чудової відповіді:(12 * (date2.year - date1.year) + date2.month - date1.month).abs if date1 && date2
Степан Захаров

Це відображає неправильне число місяців, якщо ми намагаємося вибрати місяці між 2 роками, тобто з 20.05 по 20.05.2018. Результат показує 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місяці) .місяць дає місячний еквівалент -2 (тобто -2% 12 # => 10). Запропонований вами метод спрацює, якщо два місяці йдуть один за одним і з інтервалом менше року, але він не вдасться, якщо два місяці знаходяться в зворотному порядку (наприклад, жовтень - грудень) або якщо два місяці відрізняються більше року.
elreimundo

2
Мені подобається ідея, яка стоїть за нею, але вона неправильна, якщо термін дії стає справді довгим. У моєму прикладі Date.new(2014, 10, 31), Date.new(1997, 4, 30)я отримав 213 замість 210 місяців. Причиною цього є 1.month.secondsкількість секунд за 30 днів, а не середня кількість секунд за місяць. Проголосував за те, щоб інші залишались без помилок.
Джо Ейферт,

1
це не спрацює, якщо ви виберете місяць, який не становить 30 днів
Діма

2
цей метод не дуже точний.
бродяга

9

Ви можете переформулювати запитання як "скільки перших днів між початками місяців дат", а потім скористатися перетвореннями даних у функціональному стилі:

(date1.beginning_of_month...date2.beginning_of_month).select { |date| date.day == 1 }.size

А щоб повернутися до початкового питання, ви можете підсумувати дні (як ви робите з першим) і взяти середнє значення сум.
Джо Ейферт,

9

Якщо припустити, що обидва дати - ((date2 - date1).to_f / 365 * 12).round прості.


Дуже приємне рішення! Чистий, простий і навіть працює протягом багатьох років - тобто діапазон місяців між лютим 2018 року та груднем 2017 року.
jeffdill2

1
Потрібно брати до уваги години, хвилини та секунди ((date2 - date1).to_f / 60 / 60 / 24 / 365 * 12).round
scarver2

1
@ scarver2 Число, яке ви отримуєте з вашого розрахунку, дуже далеке. Все, що ми робимо, - це брати дні і переводити їх у місяці, це не надто точно, оскільки передбачає 30,4167 днів на місяць, але це дуже близько, а потім все одно округляється.
Андрейкул


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

До речі, він покладається на 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.