Trouver le nombre de mois entre deux dates dans Ruby on Rails


95

J'ai deux objets Ruby on Rails DateTime. Comment trouver le nombre de mois entre eux? (En gardant à l'esprit qu'ils peuvent appartenir à des années différentes)


5
A été interrogé et répondu ici: stackoverflow.com/questions/5887756/…
Tim Baas

merci de l'avoir signalé. J'ai cherché mais je ne suis pas tombé dessus.
phoenixwizard


Je veux plutôt le lien en chiffres. Je l'ai fait par la méthode référée par Massimiliano Peluso
phoenixwizard

Je l'ai juste donné pour votre référence .. Merci
quand même

Réponses:


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

plus d'informations sur http://www.ruby-forum.com/topic/72120


25
Cette solution ne tient pas compte des jours. Le nombre de mois entre le 28/4/2000 et le 1/5/2000 n'est pas de 1 mois, mais de 0.
dgilperez

15
J'ai besoin de celui sans tenir compte des jours, donc cela fonctionne pour moi. +1
WattsInABox

1
Juste une autre variante pour cette excellente réponse:(12 * (date2.year - date1.year) + date2.month - date1.month).abs if date1 && date2
Stepan Zakharov

Son nombre de mois incorrect est affiché si nous essayons de récupérer des mois entre 2 ans, c'est-à-dire du 20/05/2017 au 20/05/2018. La sortie indique 1 mois.
Curious Developer

1
@CuriousDeveloper Il montre les mois corrects ie 12
ts

40

Une réponse plus précise prendrait en compte les jours au loin.

Par exemple, si vous considérez que la distance mensuelle de 28/4/2000et 1/5/2000est 0plutôt que 1, vous pouvez utiliser:

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

1
par exemple, pour calculer le nombre de fois qu'une personne a reçu son salaire, toujours au 22 des mois
Matthias

15

Essayez de

((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 (J'abandonne le formatage ... je ne peux pas le comprendre)
Toby Joiner

Même si vous utilisez l'objet Date, le commentaire de @ toby-joiner sera toujours correct. La raison en est que <Octobre> - <Décembre> est de -2 mois, mais Time.at (-2months) .month donne l'équivalent du mois de -2 (soit -2% 12 # => 10). La méthode que vous suggérez fonctionnera si les deux mois se succèdent et sont espacés de moins d'un an, mais échouera si les deux mois sont dans l'ordre inverse (par exemple, octobre - décembre) ou si les deux mois sont séparés de plus d'un an.
elreimundo

2
J'aime l'idée derrière cela, mais c'est incorrect si le datpan devient vraiment long. Dans mon exemple, Date.new(2014, 10, 31), Date.new(1997, 4, 30)j'ai reçu 213 mois au lieu de 210. La raison en est que 1.month.secondsc'est le nombre de secondes dans 30 jours et non la moyenne des secondes dans un mois. Downvoter pour que les autres restent sans bug.
Joe Eifert

1
cela ne fonctionnera pas si vous choisissez un mois qui n'est pas de 30 jours
dima

2
cette méthode n'est pas très précise.
vagabond

9

Vous pouvez reformuler la question comme "combien de premiers jours y a-t-il entre le début des mois des dates", puis utiliser des transformations de données de style fonctionnel:

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

Et pour revenir à la question initiale, vous pouvez additionner les jours (comme vous le faites avec la première) et prendre la moyenne des sommes.
Joe Eifert

9

En supposant que les deux sont des dates: ((date2 - date1).to_f / 365 * 12).round simple.


Très belle solution! Propre, simple et fonctionne même sur plusieurs années - c'est-à-dire la plage de mois entre février 2018 et décembre 2017.
jeffdill2

1
Doit prendre en compte les heures, minutes et secondes ((date2 - date1).to_f / 60 / 60 / 24 / 365 * 12).round
scarver2

1
@ scarver2 Le nombre que vous obtenez de votre calcul est bien loin. Tout ce que nous faisons, c'est de prendre des jours et de les convertir en mois, ce n'est pas super précis car cela suppose 30,4167 jours par mois, mais c'est très proche et puis ça s'arrondit quand même.
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

Une autre solution que j'ai trouvée (basée sur une solution déjà publiée ici) est si vous souhaitez que le résultat inclue des fractions de mois. Par exemple, la distance est 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
essayez de raplacer le tour avec le sol si vous souhaitez afficher uniquement les mois réellement écoulés
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
Ce serait utile si vous pouviez élaborer là-dessus? Généralement sur SO, les réponses incluent une explication sur ce que fait le code et pourquoi il fonctionne comme une solution
Entité unique

1

J'avais besoin du nombre exact de mois (y compris les décimales) entre deux dates et j'ai écrit la méthode suivante pour cela.

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

Si vous comparez, disons du 26 septembre au 26 septembre (même jour), je le calcule comme 1 jour. Si vous n'en avez pas besoin, vous pouvez supprimer la première ligne de la méthode:period_end = period_end + 1.day

Il passe les spécifications suivantes:

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

btw il s'appuie sur ActiveSupport de Rails
mtrolle

1

Voici une variante de forçage brutal:

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 doit être toujours supérieur à date1


0

Voici une autre méthode. Cela aidera à calculer le nombre de mois entiers entre deux dates

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.