2009-02-19 11 views
48

J'ai du code qui fait le suivi du temps pour les employés. Il crée un compteur pour montrer à l'employé combien de temps il a été chronométré.Comment obtenir le nombre de secondes entre deux DateTimes dans Ruby on Rails

Voici le code actuel:

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 

Cela fonctionne très bien. Mais parce que le temps est limité à la plage de 1970 - 2038, nous essayons de remplacer toutes les utilisations de temps avec DateTimes. Je ne peux pas comprendre comment obtenir le nombre de secondes entre deux DateTimes. En les soustrayant, on obtient un rationnel que je ne sais pas interpréter, alors que soustraire le temps donne la différence en secondes.

Répondre

64

Soustraire deux DateTimes retourne le temps écoulé en quelques jours, vous pouvez donc faire:

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

Ah, je savais que ça l'avait renvoyé en quelques jours, je ne savais pas que cela incluait également des fractions de jours. Merci. – Tilendor

+4

Cela ne fonctionne pas correctement en cas de seconde Leap (https://en.wikipedia.org/wiki/Leap_second). –

+31

Juste une note à quelqu'un d'autre qui peut être confus par cela. Oui, en soustrayant deux 'DateTime's renvoie le temps écoulé en jours. Cependant, dans Rails, un attribut de modèle que vous migrez en tant que 'datetime' peut en fait être un' ActiveSupport :: TimeWithZone', et en soustrayant deux de ces temps écoulés en * secondes *. – evanrmurphy

24

Vous pouvez les convertir en flotteurs avec to_f, bien que cela entraîne la perte de précision habituelle associée aux flotteurs. Si vous lancez un nombre entier de secondes entières, il ne devrait pas être assez gros pour vous inquiéter.

Les résultats sont en quelques secondes:

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

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

Sinon, vous pourriez envisager d'utiliser to_formatted_s sur l'objet DateTime et de voir si vous pouvez amadouer la sortie en quelque chose de la classe Decimal acceptera, ou le formatage tout comme Unix sous forme de chaîne et en appelant to_i sur cela.

+0

Merci Luc, la conversion .to_f est grand! Cela fonctionne indépendamment de la classe Date: soit DateTime, soit ActiveSupport :: TimeWithZone. – yaru

+0

NoMethodError: méthode non définie 'to_f 'pour # jameshfisher

45

Ou, plus lisiblement:

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

Notez ce que vous ou d'autres chercheurs pourrait vraiment être recherche est la suivante:

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

Cela ne semble pas fonctionner avec moi pour (created_at et updated_at). Mes deux numéros sont «2016-08-07 20: 33: 54.863568» et «2016-08-07 20: 33: 54.788684», mais la différence est 6469.9776. Posgres dit que ces champs sont des «horodateurs sans fuseau horaire», si cela fait une différence. – CHawk

+1

NoMethodError: méthode non définie 'jours 'pour 1: Fixnum –

+1

Notez que .days est une méthode de rails et n'est pas disponible dans ruby ​​sans elle. – sakurashinken

6

J'utilise rubis 2.1.4 et pour moi qui suit travaillé

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

m'a donné la différence de temps en secondes

Référence: ruby doc

+1

OP fait référence à l'objet "DateTime" pas un objet "Time". –

5

D'autres utilisent de manière incorrecte des fractions ou des fonctions auxiliaires. C'est beaucoup plus simple que ça. DateTime lui-même est un nombre entier inférieur. Voici la façon dont Ruby:

stop.to_i - start.to_i 

Exemple:

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

pourquoi ne pas utiliser le construit en "sec".Voici un exemple:

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

La méthode sec retournera le composant secondes de chaque fois, à l'exclusion des minutes, des heures, etc ... A la place, on pourrait utiliser par exemple. to_i pour travailler avec total secondes depuis l'epoc. –

0

Définir une fonction Ruby comme celui-ci,

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 

Et Appelez cette fonction,

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