Trova il numero di mesi tra due date in Ruby on Rails


95

Ho due oggetti DateTime di Ruby on Rails. Come trovare il numero di mesi tra di loro? (Tenendo presente che potrebbero appartenere ad anni diversi)


5
È stato interrogato e ha risposto qui: stackoverflow.com/questions/5887756/…
Tim Baas

Grazie per segnalarlo. Ho cercato ma non ci ero inciampato.
phoenixwizard

fare riferimento a questo link stackoverflow.com/questions/1065320/…
sangeethkumar

Preferisco il collegamento in numeri. L'ho fatto con il metodo indicato da Massimiliano Peluso
phoenixwizard il

L'ho dato solo come riferimento .. Grazie comunque ..
sangeethkumar

Risposte:


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

maggiori informazioni su http://www.ruby-forum.com/topic/72120


25
Questa soluzione non considera i giorni. Il numero di mesi tra il 28/4/2000 e il 1/5/2000 non è 1 mese, ma 0.
dgilperez

15
Ho bisogno di quello senza considerare i giorni, quindi questo funziona per me. +1
WattsInABox

1
Solo un'altra variante per questa ottima risposta:(12 * (date2.year - date1.year) + date2.month - date1.month).abs if date1 && date2
Stepan Zakharov

Mostra un numero di mesi errato se proviamo a recuperare mesi tra 2 anni, ad esempio dal 20/05/2017 al 20/05/2018. L'output mostra 1 mese.
Curious Developer

1
@CuriousDeveloper Mostra i mesi corretti cioè 12
ts

40

Una risposta più accurata prenderebbe in considerazione i giorni in lontananza.

Ad esempio, se consideri che la distanza del mese da 28/4/2000e 1/5/2000è 0piuttosto che 1, puoi utilizzare:

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

1
ad esempio per calcolare il numero di volte in cui qualcuno ha ricevuto il suo stipendio essendo sempre al 22 del mese
Matthias,

15

Provaci

((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 (Rinuncio alla formattazione ... non riesco a capirlo)
Toby Joiner

Anche se utilizzi l'oggetto Date, il commento di @ toby-joiner sarà comunque corretto. Il motivo è che <Ottobre> - <Dicembre> è di -2 mesi, ma Time.at (-2months) .month fornisce l'equivalente mensile di -2 (cioè -2% 12 # => 10). Il metodo suggerito funzionerà se i due mesi si susseguono e sono distanti meno di un anno, ma fallirà se i due mesi sono in ordine inverso (ad esempio ottobre - dicembre) o se i due mesi sono distanti più di un anno.
elreimundo

2
Mi piace l'idea alla base, ma non è corretto se l'intervallo di date sta diventando molto lungo. Nel mio esempio di Date.new(2014, 10, 31), Date.new(1997, 4, 30)ho ricevuto 213 invece di 210 mesi. Il motivo è che 1.month.secondsè il numero di secondi in 30 giorni e non i secondi medi in un mese. Downvoting in modo che gli altri rimangano privi di bug.
Joe Eifert,

1
questo non funzionerà se scegli un mese che non è di 30 giorni
dima

2
questo metodo non è molto accurato.
vagabondo

9

Puoi riformulare la domanda come "quanti primi giorni ci sono tra l'inizio dei mesi delle date" e quindi utilizzare le trasformazioni dei dati in stile funzionale:

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

E per tornare alla domanda iniziale, potresti sommare i giorni (come stai facendo con la prima) e prendere la media delle somme.
Joe Eifert,

9

Supponendo che entrambe siano date: ((date2 - date1).to_f / 365 * 12).round semplice.


Soluzione molto bella! Pulito, semplice e funziona persino nel corso degli anni, ovvero l'intervallo di mesi tra febbraio 2018 e dicembre 2017.
jeffdill2

1
Deve prendere in considerazione ore, minuti e secondi ((date2 - date1).to_f / 60 / 60 / 24 / 365 * 12).round
scarver2

1
@ scarver2 Il numero che ottieni dal tuo calcolo è molto diverso. Tutto ciò che stiamo facendo è prendere giorni e convertirli in mesi, non è molto accurato perché presuppone 30.4167 giorni al mese, ma è molto vicino e quindi arrotonda comunque.
Andreykul il

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

Un'altra soluzione che ho trovato (costruita su una soluzione già pubblicata qui) è se vuoi che il risultato includa frazioni di un mese. Ad esempio la distanza è 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
prova a fare il giro con il pavimento se desideri mostrare solo i mesi effettivamente trascorsi
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
Sarebbe utile se potessi approfondire questo? Generalmente su SO le risposte includono una spiegazione su cosa fa il codice e perché funziona come una soluzione
entità singola

1

Avevo bisogno del numero esatto di mesi (compresi i decimali) tra due date e ho scritto il seguente metodo per questo.

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

Se confrontiamo diciamo dal 26 settembre al 26 settembre (stesso giorno) lo calcolo come 1 giorno. Se non ti serve, puoi rimuovere la prima riga nel metodo:period_end = period_end + 1.day

Supera le seguenti specifiche:

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 si affida all'ActiveSupport di Rails
mtrolle

1

Ecco una variante di forzatura bruta:

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 deve essere sempre maggiore di date1


0

Ecco un altro metodo. Ciò aiuterà a calcolare il numero di mesi interi tra due date

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.