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)

phoenixwizard
la source
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
référez-vous à ce lien stackoverflow.com/questions/1065320/…
sangeethkumar
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

Massimiliano Peluso
la source
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)
dgilperez
la source
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
knotito
la source
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
shock_one
la source
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.

Andreykul
la source
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
Ankit Varshney
la source
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...
Zack
la source
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
Uğur Yılmaz
la source
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
mtrolle
la source
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

ole
la source
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
Dhanush Balachandran
la source