Tenho dois objetos DateTime do Ruby on Rails. Como saber o número de meses entre eles? (Tendo em mente que eles podem pertencer a anos diferentes)
Tenho dois objetos DateTime do Ruby on Rails. Como saber o número de meses entre eles? (Tendo em mente que eles podem pertencer a anos diferentes)
Respostas:
(date2.year * 12 + date2.month) - (date1.year * 12 + date1.month)
mais informações em http://www.ruby-forum.com/topic/72120
(12 * (date2.year - date1.year) + date2.month - date1.month).abs if date1 && date2
Uma resposta mais precisa consideraria dias distantes.
Por exemplo, se você considerar que a distância do mês de 28/4/2000
e 1/5/2000
é em 0
vez de 1
, então você pode usar:
(date2.year - date1.year) * 12 + date2.month - date1.month - (date2.day >= date1.day ? 0 : 1)
Experimente
((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
(Desisto de formatação ... não consigo descobrir)
Date.new(2014, 10, 31), Date.new(1997, 4, 30)
, recebi 213 em vez de 210 meses. A razão para isso é que 1.month.seconds
é o número de segundos em 30 dias e não a média de segundos em um mês. Downvoting para que outros fiquem livres de erros.
Você pode reformular a pergunta como "quantos primeiros dias existem entre o início dos meses das datas" e, em seguida, usar transformações de dados de estilo funcional:
(date1.beginning_of_month...date2.beginning_of_month).select { |date| date.day == 1 }.size
Supondo que ambos sejam datas:
((date2 - date1).to_f / 365 * 12).round
simples.
((date2 - date1).to_f / 60 / 60 / 24 / 365 * 12).round
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
Outra solução que encontrei (construída a partir de uma solução já postada aqui) é se você quiser que o resultado inclua frações de um mês. Por exemplo, a distância é 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...
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
Eu precisava do número exato de meses (incluindo decimais) entre duas datas e escrevi o seguinte método para isso.
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 comparar, digamos de 26 de setembro a 26 de setembro (mesmo dia), calculo como 1 dia. Se você não precisa disso, pode remover a primeira linha do método:period_end = period_end + 1.day
Ele passa nas seguintes especificações:
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
Aqui está outro método. Isso ajudará a calcular o número de meses inteiros entre duas datas
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