Convertir la durée en heures: minutes: secondes (ou similaire) dans Rails 3 ou Ruby

115

J'ai le sentiment qu'il existe un moyen simple / intégré de le faire, mais je ne le trouve pas.

J'ai une durée (en secondes) sous forme de nombre entier et je souhaite l'afficher dans un format convivial.

par exemple, 3600 serait affiché comme "01:00:00" ou "1 heure" ou quelque chose.

Je peux le faire avec time_ago_in_words(Time.zone.now+3600)mais cela ressemble un peu à un hack, il n'y a aucune raison d'ajouter / de soustraire de l'heure actuelle juste pour formater cette valeur. Y a-t-il un duration_in_words()ou quelque chose?

Merci

Cydonia
la source

Réponses:

83

Voir: http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html

distance_of_time_in_words(3600)
 => "about 1 hour"
allan
la source
1
merci j'ai regardé ça mais j'ai pensé que je devais fournir deux fois .. quel nœud!
cydonia
1
ouais, ces exemples sont stupides
allan
1
distance_of_time_in_words (from_time, to_time, ...)
boulder_ruby
1
De plus, si vous voulez que ce soit très précis et ne pas "arrondir" la durée, consultez la gemme de Radar: github.com/radar/distance_of_time_in_words . Remplacement instantané pour distance_of_time_in_wordset vous pouvez obtenir le nombre arrondi en le passant vague: trueen option.
Joshua Pinter
194

En résumé:

en admettant que total_seconds = 3600

Option 1:

distance_of_time_in_words(total_seconds) #=> "about 1 hour"

Option 2:

Time.at(total_seconds).utc.strftime("%H:%M:%S") #=> "01:00:00"

Option 3:

seconds = total_seconds % 60
minutes = (total_seconds / 60) % 60
hours = total_seconds / (60 * 60)

format("%02d:%02d:%02d", hours, minutes, seconds) #=> "01:00:00"

utilisez Option1 si vous voulez des mots, Option2 si vous voulez le format H: M: S, Option3 si vous voulez le format H: M: S et il peut y avoir plus de 24 heures

Lev Lukomsky
la source
1
La méthode distance_of_time_in_words est un ActionView Helper, elle doit donc être appelée à partir de la vue (pas du contrôleur). api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
msdundar
13
Notez que l'option strftime débordera à 24 heures. Si votre durée est de 25 heures, il s'affichera 01:00:00.
Gabe Martin-Dempesy
2
cela ne fonctionne pas pour les temps négatifs, par exemple -1800 renvoie -1h 30m au lieu de -30m
Arnold Roa
45

L' %opérateur de chaîne de Ruby est trop méconnu et souvent oublié.

"%02d:%02d:%02d:%02d" % [t/86400, t/3600%24, t/60%60, t%60]

Étant donné que t est une durée en secondes, cela émet une chaîne de caractères séparés par deux-points, complétée par zéro, y compris les jours. Exemple:

t = 123456
"%02d:%02d:%02d:%02d" % [t/86400, t/3600%24, t/60%60, t%60]
=> "01:10:17:36"

Charmant.

IAmNaN
la source
24
C'est une étrange définition de «belle».
Marc Bollinger
10
Le %dit: "Prenez le tableau de paramètres à ma droite et insérez-les dans la chaîne de format à ma gauche." À cet égard, il est similaire à printfC \ C ++ mais plus concis et peut être utilisé dans une affectation. Oui, j'appelle ça charmant. Et puissant. Il existe des exemples qui démontreraient mieux cette éloquence, mais ils ne répondraient pas à la question. Votre snark n'est pas apprécié, d'ailleurs.
IAmNaN
26

Je suppose que vous pourriez aussi faire quelque chose comme:

(Time.mktime(0)+3600).strftime("%H:%M:%S")

Pour le formater comme vous le souhaitez.

BTW, à l'origine, j'ai pensé à utiliser Time.at () mais il semble que l'heure EPOCH sur mon Ubuntu est Thu Jan 01 01:00:00 +0100 1970 et non 00:00:00 heures comme je m'y attendais, et donc si je le fais:

Time.at(3600).strftime("%H:%M:%S")

Me donne 1 heure de plus que ce que je voulais.

Cristobal Viedma
la source
ahh, quelque chose à faire à cause de l'heure d'été?
Cristobal Viedma
7
Le seul hic, c'est si vous avez affaire à plus de 24 heures. Dans ce cas, les heures reviendront à un nombre bas.
metavida
Pour des durées supérieures à 24 heures,% j vous donnera le nombre de jours (tant qu'il est inférieur à 365)
BananaNeil
18

J'utilise ceci pour afficher les durées de temps dans mon projet Rails:

  1. Ajoutez une méthode personnalisée à la Integerclasse. Vous pouvez créer un nouveau fichier appelé pretty_duration.rbdans le initializersdossier:

    class Integer
        def pretty_duration
            parse_string = 
                if self < 3600
                    '%M:%S'
                else
                    '%H:%M:%S'
                end
    
            Time.at(self).utc.strftime(parse_string)
        end
    end
  2. Appelez seconds.pretty_durationn'importe où dans votre projet:

    275.pretty_duration     # => "04:35"
    9823.pretty_duration    # => "02:43:43"

Cette réponse s'appuie sur le code de Lev Lukomsky

Sheharyar
la source
A très bien fonctionné. Merci pour ça.
Donn Felker
13

Celui-ci utilise la divmodméthode obscure pour diviser et modulo en même temps, donc il gère Floatcorrectement les secondes:

def duration(seconds)
  minutes, seconds = seconds.divmod(60)
  hours, minutes = minutes.divmod(60)
  days, hours = hours.divmod(24)

  "#{days.to_s.rjust(3)}d #{hours.to_s.rjust(2)}h #{minutes.to_s.rjust(2)}m #{seconds}s"
end
Brent Royal-Gordon
la source
6

L'utilisation Time.utc.strftimene fonctionne que pour les valeurs lorsque le nombre total d'heures est inférieur à 24 :

2.2.2 :004 > Time.at(60 * 60).utc.strftime('%H h %M m')
=> "01 h 00 m"

Pour des valeurs supérieures, il renvoie des résultats incorrects:

2.2.2 :006 > Time.at(60 * 60 * 24).utc.strftime('%H h %M m')
 => "00 h 00 m"

Je suggère d'utiliser la méthode la plus simple que j'ai trouvée pour ce problème:

  def formatted_duration total_seconds
    hours = total_seconds / (60 * 60)
    minutes = (total_seconds / 60) % 60
    seconds = total_seconds % 60
    "#{ hours } h #{ minutes } m #{ seconds } s"
  end

Vous pouvez toujours ajuster la valeur renvoyée à vos besoins.

Igor Springer
la source
5

Soyez prudent avec la durée supérieure à un jour.

(timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")
Xiao Bin
la source
Donnez un exemple d'entrée / sortie mettant en évidence la façon dont cela résout le cas d'angle
Cyril Duchon-Doris
4

Une réponse inspirée de celle de Lev Lukomsky tirant parti d'ActiveSupport :: Duration, et de la gestion des millisecondes (utile pour comparer le code)

# duration in ms modulus number of ms in one second
milliseconds = duration.in_milliseconds % 1.second.in_milliseconds

# duration in seconds modulus number of seconds in one minute
seconds = (duration / 1.second) % (1.minute / 1.second)

# duration in minutes modulus number of minutes in one hour
minutes = (duration / 1.minute) % (1.hour / 1.minute)

# duration in hours modulus number of hours in one day
hours = (duration / 1.hour) % (1.day / 1.hour)

format("%02d:%02d:%02d:%03d", hours, minutes, seconds, milliseconds) #=> "12:05:00:001"

Bien sûr, vous pouvez facilement prolonger cela avec des jours, des mois, des années, etc. en utilisant les méthodes ActiveSupport associées et en répétant la même structure.

Gardez à l'esprit que pour des durées trop longues, cela peut être inexact car la durée d'un mois n'est pas fixée en nombre de jours, et je ne sais pas comment AS: Duration gère cela.

Cyril Duchon-Doris
la source
2

Juste pour jeter mes 2 cents:

Time.at(i).utc.strftime((i < 3600) ? '%-M minutes and %-S seconds' : '%-H hours, %-M minutes, and %-S seconds')

Construit à partir de la réponse de Xiao Bin.

Daniel
la source
2

Criez à @joshuapinter qui a donné la meilleure réponse (sous la forme d' un commentaire ).

Utilisez la dotiwgemme de remplacement pour mieux contrôler la précision de la sortie afin de répondre à différents besoins:

https://github.com/radar/distance_of_time_in_words

Exemple de code de vue:

%label
  Logoff after:
  - expire_in = distance_of_time_in_words(Time.now, Time.now + user.custom_timeout.minutes, :only => [:minutes, :hours, :days])
  = expire_in

Résultat de quelque chose comme ceci:

Logoff after: 1 day, 13 hours, and 20 minutes
Jon Kern
la source
2

ActiveSupport::Duration.build+ inspectvous donne des résultats valides

 >> ActiveSupport::Duration.build(125557).inspect
 => "1 day, 10 hours, 52 minutes, and 37 seconds"
Sathish
la source
1
    hours = 3.5456
    value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
    => "03:32"
Luciano Ribas
la source
5
Veuillez ajouter un peu de contexte à la réponse.
Paul Dawson