2011-05-04 9 views
11

¿Cómo calculo la diferencia de dos fechas en meses? Además, en caso de que haga una diferencia, estoy trabajando con objetos Date, no con DateTime. Además, algunas opciones de redondeo pueden ser útiles, así que puedo controlar si quiero redondear hacia arriba o hacia abajo en meses parciales.Los rieles calculan el intervalo de fechas en meses

Gracias!

+0

Me gusta este, pero suena un poco tedioso codificar. ._. – omninonsense

+0

DateTime y Date son algo intercambiables usando los métodos 'DateTime # to_date' y' Date # to_datetime'. – tadman

+0

Quizás, [una pregunta similar sobre años] (http://stackoverflow.com/questions/1904097/how-to-calculate-how-many-years-passed-since-a-given-date-in-ruby) podría ¿ayudarte? –

Respuesta

9

restar una fecha o DateTime de otro rendirá el número de días como una fracción, pero esto puede ser evaluado como un Float o Fixnum según se requiera.

Por ejemplo:

(Date.today - Date.today.advance(:months => -3)).to_f 
# => 89.0 

Hubo 89,0 días entre hoy y la misma fecha del calendario hace tres meses. Si trabaja esto usando 30 días, o 30.4375 como promedio, terminará con 2.92 meses transcurridos entre ese momento y ahora, o redondeado al número entero más cercano, 3.

Si desea calcular el número exacto de meses calendario, eso es más complicado, pero se puede hacer.

+0

meses calendario sería la preferencia ... pero si es demasiado complicado utilizaré el método de divide por 30 (o 30.44). – brettish

+0

sabes qué, después de pensarlo un poco más y de cómo trataría de implementar los meses calendario, decidí que no valía la pena. Usaré tu enfoque. – brettish

5

Esto debería dar un visto bueno aproximación:

Date1 - Date2 = difference_in_days 
(difference_in_days/30).round = difference_in_months 
+0

Sí, podría tener que hacer eso si el enfoque del mes calendario es demasiado complicado para valer la pena. – brettish

+1

Es posible que desee cambiar 'ronda' a 'piso' para obtener un resultado más preciso – yalestar

5

Algo como esto es más fácil de leer que averiguar segundos, y le dará la diferencia de calendario real:

# Calculate differnce between two dates in months 
# Produces b - a 
def month_difference(a, b) 
    difference = 0.0 
    if a.year != b.year 
     difference += 12 * (b.year - a.year) 
    end 
    difference + b.month - a.month 
end 

Si es necesario trabajar la diferencia basada en días así, sólo puede seguir el patrón

+0

¡Guau, gracias! ¡Es un pequeño pedazo de código útil! Parece que puedes acortar cosas reemplazando las primeras 4 líneas del método con 'difference = 12 * (b.year - a.year) .abs' (con .abs así que no importa en qué orden son las fechas especificado en) – brettish

+0

@brettish Mantendría los abs en el exterior de la función, ya que de lo contrario la función descartará la información de dirección. También creo que tendrías que hacer los abdominales al final para asegurarte de que la diferencia mensual añadida a la diferencia anual sea correcta cuando a.year> b.año pero a.mesimo Glenjamin

+1

Hay una versión más limpia de esto [aquí] (http://stackoverflow.com/questions/9428605/find-number-of-months-between-two-dates-in-ruby-on-rails): (date2.year * 12 + fecha2.mes) - (fecha1.año * 12 + fecha1.mes) –

5

Necesitamos algo en esta línea, pero incluye meses parciales. Así que 1/31 a 2/1 aún rendiría 2 meses. ¡Podría ayudar!

def self.month_count(range) 
    12 * (range.end.year - range.begin.year) + range.end.month - range.begin.month 
end 
0

cómo esta práctica?

current_date = start_date 

while current_date < end_date 
    # something 
    current_date = current_date.next_month 
end 
1

Esta respuesta es tarde a la fiesta, se basa en las respuestas anteriores, y probablemente se podría escribir de manera más concisa, sin embargo, sí da la diferencia entre dos fechas del calendario, teniendo en cuenta días.

def difference_in_months(start_date, today) 
    date_to_months(today) - date_to_months(start_date) + adjustment_for_days(start_date, today) 
end 

def date_to_months(date) 
    date.year * 12 + date.month 
end 

def adjustment_for_days(earlier_date, later_date) 
    if later_date.day == earlier_date.day 
    0 
    elsif later_date.day > earlier_date.day 
    1 
    else 
    -1 
    end 
end 
0

Necesité el número exacto de meses (incluyendo decimales) entre dos fechas y escribí el siguiente método para ello.

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 lo comparamos digamos del 26 de septiembre al 26 de septiembre (el mismo día) lo calculo como 1 día. Si no es necesario que usted puede quitar la primera línea en el método: period_end = period_end + 1.day

Pasa a las siguientes especificaciones:

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 

Se basa en ActiveSupport Rails.

Cuestiones relacionadas