Znajdź liczbę miesięcy między dwiema datami w Ruby on Rails


95

Mam dwa obiekty DateTime Ruby on Rails. Jak znaleźć liczbę miesięcy między nimi? (Pamiętając, że mogą należeć do różnych lat)


5
Zostałem zapytany i odpowiedział tutaj: stackoverflow.com/questions/5887756/…
Tim Baas,

dzięki za wskazanie tego. Szukałem, ale nie natknąłem się na to.
phoenixwizard


Wolę link w liczbach. Zrobiłem to metodą, o której wspomniał Massimiliano Peluso
phoenixwizard

Po prostu dałem to w celach informacyjnych .. W każdym razie dzięki ..
sangeethkumar

Odpowiedzi:


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

więcej informacji na http://www.ruby-forum.com/topic/72120


25
To rozwiązanie nie uwzględnia dni. Liczba miesięcy między 28.04.2000 a 1/5/2000 to nie 1 miesiąc, ale 0.
dgilperez

15
Potrzebuję tego bez uwzględnienia dni, więc to działa dla mnie. +1
WattsInABox

1
Kolejny wariant tej wspaniałej odpowiedzi:(12 * (date2.year - date1.year) + date2.month - date1.month).abs if date1 && date2
Stepan Zakharov

Wyświetla niepoprawną liczbę miesięcy, jeśli spróbujemy pobrać miesiące od 2 lat, tj. 2017-05-20 do 2018-05-20. Wynik pokazuje 1 miesiąc.
Ciekawy programista

1
@CuriousDeveloper Pokazuje prawidłowe miesiące, tj. 12
ts

40

Dokładniejsza odpowiedź uwzględniłaby dni w oddali.

Na przykład, jeśli uważasz, że odległość miesięczna od 28/4/2000i 1/5/2000wynosi 0raczej niż 1, możesz użyć:

(date2.year - date1.year) * 12 + date2.month - date1.month - (date2.day >= date1.day ? 0 : 1)

1
np. do wyliczenia, ile razy ktoś otrzymywał pensję zawsze w 22. dniu miesiąca
Matthias

15

Spróbuj

((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 (Rezygnuję z formatowania ... nie mogę tego rozgryźć)
Toby Joiner,

Nawet jeśli użyjesz obiektu Date, komentarz @ toby-joiner będzie nadal poprawny. Powodem jest to, że <Październik> - <December> to -2 miesiące, ale Time.at (-2miesiące) .month daje miesięczny odpowiednik -2 (tj. -2% 12 # => 10). Twoja sugerowana metoda zadziała, jeśli te dwa miesiące następują po sobie i są od siebie mniej niż rok, ale zakończy się niepowodzeniem, jeśli te dwa miesiące będą miały odwrotną kolejność (np. Październik - grudzień) lub jeśli odstęp między dwoma miesiącami przekracza rok.
elreimundo

2
Podoba mi się idea, która się za tym kryje, ale jest to niepoprawne, jeśli datapan jest naprawdę długi. W moim przykładzie Date.new(2014, 10, 31), Date.new(1997, 4, 30)otrzymałem 213 zamiast 210 miesięcy. Powodem tego jest 1.month.secondsliczba sekund w 30 dni, a nie średnia sekund w miesiącu. Głosowanie w dół, aby inni pozostali wolni od błędów.
Joe Eifert

1
to nie zadziała, jeśli wybierzesz miesiąc inny niż 30 dni
dima

2
ta metoda nie jest zbyt dokładna.
włóczęga

9

Możesz przeformułować pytanie jako „ile pierwszych dni upływa między początkami miesięcy dat”, a następnie użyć transformacji danych w stylu funkcjonalnym:

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

Wracając do pierwotnego pytania, możesz zsumować dni (tak jak w przypadku pierwszego) i wziąć średnią z tych sum.
Joe Eifert

9

Zakładając, że obie daty są ((date2 - date1).to_f / 365 * 12).round proste.


Bardzo fajne rozwiązanie! Czysty, prosty, a nawet działa przez lata - tj. Zakres miesięcy od lutego 2018 r. Do grudnia 2017 r.
jeffdill2

1
((date2 - date1).to_f / 60 / 60 / 24 / 365 * 12).round
Musi brać

1
@ scarver2 Liczba, którą otrzymujesz z obliczeń, jest daleko idąca. Wszystko, co robimy, to dni i przeliczanie ich na miesiące, nie jest to super dokładne, ponieważ zakłada 30,4167 dni w miesiącu, ale jest bardzo blisko, a potem i tak zaokrągla.
Andreykul

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

Inne rozwiązanie, które znalazłem (zbudowane na podstawie rozwiązania zamieszczonego już tutaj) jest przeznaczone, jeśli chcesz, aby wynik obejmował ułamki miesiąca. Na przykład odległość to 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
spróbuj okrążać podłogę, jeśli chcesz pokazać tylko faktycznie minęły miesiące
Matthias

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
Byłoby przydatne, gdybyś mógł to rozwinąć? Ogólnie w odpowiedziach na SO zawiera wyjaśnienie, co robi kod i dlaczego działa jako rozwiązanie
Single Entity

1

Potrzebowałem dokładnej liczby miesięcy (w tym liczb dziesiętnych) między dwiema datami i napisałem dla niej następującą metodę.

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

Porównując powiedzmy 26 września do 26 września (tego samego dnia), obliczam to jako 1 dzień. Jeśli tego nie potrzebujesz, możesz usunąć pierwszą linię w metodzie:period_end = period_end + 1.day

Spełnia następujące specyfikacje:

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

przy okazji opiera się na ActiveSupport Rails
mtrolle

1

Oto wariant brutalnego wymuszania:

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 musi być zawsze większe niż date1


0

Oto inna metoda. Pomoże to obliczyć liczbę pełnych miesięcy między dwiema datami

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

Rozwiązanie w każdym przypadku

(date1..date2).map { |date| date.strftime('%m.%Y') }.uniq.size
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.