Finden Sie die Anzahl der Monate zwischen zwei Daten in Ruby on Rails


95

Ich habe zwei Ruby on Rails DateTime-Objekte. Wie finde ich die Anzahl der Monate zwischen ihnen? (Denken Sie daran, dass sie zu verschiedenen Jahren gehören könnten)


5
Wurde hier befragt und beantwortet: stackoverflow.com/questions/5887756/…
Tim Baas

Vielen Dank für den Hinweis. Ich suchte, war aber nicht darauf gestoßen.
Phoenixwizard


Ich möchte lieber den Link in Zahlen. Ich habe es nach der Methode von Massimiliano Peluso
Phoenixwizard

Nur ich gab es als Referenz ..
Trotzdem

Antworten:


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

Weitere Informationen unter http://www.ruby-forum.com/topic/72120


25
Diese Lösung berücksichtigt keine Tage. Die Anzahl der Monate zwischen 28/4/2000 und 1/5/2000 beträgt nicht 1 Monat, sondern 0.
dgilperez

15
Ich brauche die ohne Rücksicht auf Tage, damit das für mich funktioniert. +1
WattsInABox

1
Nur eine weitere Variante für diese großartige Antwort:(12 * (date2.year - date1.year) + date2.month - date1.month).abs if date1 && date2
Stepan Zakharov

Es zeigt eine falsche Anzahl von Monaten an, wenn wir versuchen, Monate zwischen 2 Jahren abzurufen, dh 2017-05-20 bis 2018-05-20. Die Ausgabe zeigt 1 Monat.
Neugieriger Entwickler

1
@CuriousDeveloper Es zeigt korrekte Monate, dh 12
ts

39

Eine genauere Antwort würde Tage in der Ferne berücksichtigen.

Zum Beispiel, wenn Sie , dass der Monat Abstand betrachten aus 28/4/2000und 1/5/2000ist 0nicht 1, dann können Sie:

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

1
zB für die Berechnung der Häufigkeit, mit der jemand sein Gehalt erhalten hat, immer am 22. des Monats
Matthias

15

Probieren Sie es aus

((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 (Ich gebe die Formatierung auf ... kann es nicht herausfinden)
Toby Joiner

Selbst wenn Sie das Date-Objekt verwenden, ist der Kommentar von @ toby-joiner immer noch korrekt. Der Grund ist, dass <Oktober> - <Dezember> -2 Monate beträgt, Time.at (-2 Monate) .Monat jedoch das Monatsäquivalent von -2 ergibt (dh -2% 12 # => 10). Ihre vorgeschlagene Methode funktioniert, wenn die beiden Monate aufeinander folgen und weniger als ein Jahr voneinander entfernt sind. Sie schlägt jedoch fehl, wenn die beiden Monate in umgekehrter Reihenfolge (z. B. Oktober - Dezember) liegen oder wenn die beiden Monate mehr als ein Jahr voneinander entfernt sind.
Elreimundo

2
Ich mag die Idee dahinter, aber es ist falsch, wenn die Zeitspanne wirklich lang wird. In meinem Beispiel Date.new(2014, 10, 31), Date.new(1997, 4, 30)erhielt ich 213 statt 210 Monate. Der Grund dafür ist, dass 1.month.secondsdie Anzahl der Sekunden in 30 Tagen und nicht die durchschnittlichen Sekunden in einem Monat sind. Downvoting, damit andere fehlerfrei bleiben.
Joe Eifert

1
Dies wird nicht funktionieren, wenn Sie einen Monat wählen, der nicht 30 Tage ist
Dima

2
Diese Methode ist nicht sehr genau.
Vagabund

9

Sie können die Frage wie folgt umformulieren: "Wie viele erste Tage liegen zwischen den Anfängen der Monate der Daten?" Und dann Datentransformationen im funktionalen Stil verwenden:

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

Und um zur ursprünglichen Frage zurückzukehren, können Sie die Tage (wie bei der ersten) summieren und den Durchschnitt der Summen ermitteln.
Joe Eifert

9

Angenommen, beide sind Daten: ((date2 - date1).to_f / 365 * 12).round einfach.


Sehr schöne Lösung! Sauber, einfach und funktioniert sogar über Jahre hinweg - dh den Zeitraum zwischen Februar 2018 und Dezember 2017.
Jeffdill2

1
((date2 - date1).to_f / 60 / 60 / 24 / 365 * 12).round
Muss

1
@ scarver2 Die Zahl, die Sie aus Ihrer Berechnung erhalten, ist weit entfernt. Alles, was wir tun, ist, Tage zu nehmen und sie in Monate umzuwandeln. Es ist nicht sehr genau, da es 30.4167 Tage pro Monat voraussetzt, aber es ist sehr eng und rundet sich trotzdem.
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

Eine andere Lösung, die ich gefunden habe (basierend auf einer hier bereits veröffentlichten Lösung), ist für den Fall, dass das Ergebnis Bruchteile eines Monats enthalten soll. Zum Beispiel ist die Entfernung 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
Versuchen Sie, eine Runde mit dem Boden zu machen, wenn Sie nur die tatsächlich verstrichenen Monate anzeigen möchten
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
Es wäre nützlich, wenn Sie dies näher erläutern könnten. Im Allgemeinen enthalten SO-Antworten eine Erklärung darüber, was der Code tut und warum er als Lösung funktioniert
Single Entity

1

Ich brauchte die genaue Anzahl von Monaten (einschließlich Dezimalstellen) zwischen zwei Daten und schrieb die folgende Methode dafür.

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

Wenn wir den 26. September mit dem 26. September (am selben Tag) vergleichen, berechne ich ihn als 1 Tag. Wenn Sie das nicht benötigen, können Sie die erste Zeile in der Methode entfernen:period_end = period_end + 1.day

Es besteht die folgenden Spezifikationen:

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

Übrigens stützt es sich auf Rails 'ActiveSupport
mtrolle

1

Hier ist eine Brute-Forcing-Variante:

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 muss immer größer sein als date1


0

Hier ist eine andere Methode. Dies hilft bei der Berechnung der Anzahl ganzer Monate zwischen zwei Daten

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.