2009-02-19 12 views
48

? Tengo un código que hace un seguimiento del tiempo para los empleados. Crea un contador para mostrarle al empleado cuánto tiempo hace que se registraron.¿Cómo obtengo la cantidad de segundos entre dos DateTimes en Ruby on Rails

Este es el código actual:

start_time = Time.parse(self.settings.first_clock_in) 
    total_seconds = Time.now - start_time 
    hours = (total_seconds/ 3600).to_i 
    minutes = ((total_seconds % 3600)/60).to_i 
    seconds = ((total_seconds % 3600) % 60).to_i 

Esto funciona bien. Pero como Time está limitado al rango de 1970-2038, estamos intentando reemplazar todos los usos de Time con DateTimes. No puedo descifrar cómo obtener la cantidad de segundos entre dos DateTimes. Restarlos produce un Rational que no sé cómo interpretar, mientras que restar Times produce la diferencia en segundos.

Respuesta

64

Restando dos DateTime devuelve el tiempo transcurrido en días, por lo que sólo podían hacer:

elapsed_seconds = ((end_time - start_time) * 24 * 60 * 60).to_i 
+0

Ah, sabía que lo devolvía en días, no sabía que esto también incluiría fracciones de un día. Gracias. – Tilendor

+4

Esto no funciona correctamente en caso de un segundo bisiesto (https://en.wikipedia.org/wiki/Leap_second). –

+31

Solo una nota para cualquier persona que pueda estar confundida por esto. Sí, restando dos 'DateTime's devuelve el tiempo transcurrido en días. Sin embargo, en Rails, un atributo de modelo que migre como 'datetime' puede ser en realidad un' ActiveSupport :: TimeWithZone', y restando dos de esos retornos de tiempo transcurrido en * segundos *. – evanrmurphy

24

Puede convertirlos en flotadores con to_f, aunque esto provocará la pérdida habitual de precisión asociada a los flotadores. Si solo estás transfiriendo a un entero por segundos enteros, no debería ser lo suficientemente grande como para ser una preocupación.

Los resultados se expresan en segundos:

>> end_time.to_f - start_time.to_f 
=> 7.39954495429993 

>> (end_time.to_f - start_time.to_f).to_i 
=> 7 

De lo contrario, se podría contemplar la utilización de to_formatted_s en el objeto DateTime y ver si se puede convencer a la salida en algo la clase Decimal aceptará, o simplemente formatearlo como planifique el tiempo de Unix como una cadena y llame al to_i en eso.

+0

Gracias Lucas, la conversión .to_f es grande! Funciona independientemente de la clase Date: o bien es DateTime o ActiveSupport :: TimeWithZone. – yaru

+0

NoMethodError: método indefinido 'to_f 'para # jameshfisher

45

O, más legible:

diff = datetime_1 - datetime_2 
diff * 1.days # => difference in seconds; requires Ruby on Rails 

Nota, lo que usted o algunos otros buscadores puede ser realmente buscando es la siguiente:

diff = datetime_1 - datetime_2 
Date.day_fraction_to_time(diff) # => [h, m, s, frac_s] 
+0

Esto no parece funcionar conmigo para (created_at y updated_at). Mis dos números son '2016-08-07 20: 33: 54.863568' y' 2016-08-07 20: 33: 54.788684', pero la diferencia es 6469.9776. Posgres dice que esos campos son 'marcas de tiempo sin zona horaria', si eso hace la diferencia. – CHawk

+1

NoMethodError: método indefinido 'days 'for 1: Fixnum –

+1

tenga en cuenta que .days es un método de rieles y no está disponible en ruby ​​sin él. – sakurashinken

6

estoy usando rubí 2.1.4 y para mí la siguiente trabajé

Time.now - Time.new(2014,11,05,17,30,0) 

me dio la diferencia de tiempo en segundos

referencia: ruby doc

+1

OP se refiere al objeto "DateTime" no a un objeto "Time". –

5

Otros confían incorrectamente en fracciones o funciones auxiliares. Es mucho más simple que eso. DateTime en sí mismo es un número entero debajo. Esta es la forma Ruby:

stop.to_i - start.to_i 

Ejemplo:

start = Time.now 
=> 2016-06-21 14:55:36 -0700 
stop = start + 5.seconds 
=> 2016-06-21 14:55:41 -0700 
stop.to_i - start.to_i 
=> 5 
0

por qué no utilizar el construido en "s".He aquí un ejemplo:

t1 = Time.now.sec 

elapsed_t = Time.now.sec - t1 

puts "it took me : #{elapsed_t} seconds to do something that is useful \n" 
+0

El método sec devolverá el componente de segundos de cada vez, excluyendo minutos, horas, etc. ... En su lugar podría usar, p. to_i para trabajar con un total de segundos desde el epoc. –

0

Definir una función de Ruby como este,

def time_diff(start_time, end_time) 
    seconds_diff = (start_time - end_time).to_i.abs 

    days = seconds_diff/86400 
    seconds_diff -= days * 86400 

    hours = seconds_diff/3600 
    seconds_diff -= hours * 3600 

    minutes = seconds_diff/60 
    seconds_diff -= minutes * 60 

    seconds = seconds_diff 

    "#{days} Days #{hours} Hrs #{minutes} Min #{seconds} Sec" 
end 

y llamar a esta función,

time_diff(Time.now, Time.now-4.days-2.hours-1.minutes-53.seconds)