Convierta la duración a horas: minutos: segundos (o similar) en Rails 3 o Ruby

115

Tengo la sensación de que hay una forma simple / incorporada de hacer esto, pero no puedo encontrarla.

Tengo una duración (en segundos) en un número entero y quiero mostrarla en un formato amigable.

por ejemplo, 3600 se mostraría como "01:00:00" o "1 hora" o algo así.

Puedo hacerlo con, time_ago_in_words(Time.zone.now+3600)pero se siente como un truco, no hay razón para sumar / restar de la hora actual solo para formatear este valor. ¿Hay duration_in_words()algo o algo?

Gracias

Cydonia
fuente

Respuestas:

83

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

distance_of_time_in_words(3600)
 => "about 1 hour"
Alano
fuente
1
gracias miré eso pero pensé que tenía que proporcionar dos veces ... ¡qué nudo!
Cydonia
1
sí, esos ejemplos son tontos
allan
1
distance_of_time_in_words (FROM_TIME, to_time, ...)
boulder_ruby
1
Además, si desea que esto sea muy específico y no "redondear" la duración, consulte la gema de Radar: github.com/radar/distance_of_time_in_words . Reemplazo directo para distance_of_time_in_wordsy puede obtener el número redondeado pasando vague: truecomo una opción.
Joshua Pinter
194

Resumiendo:

asumiendo que total_seconds = 3600

Opción 1:

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

Opcion 2:

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

Opcion 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"

use Option1 si quiere palabras, Option2 si quiere formato H: M: S, Option3 si quiere formato H: M: S y puede haber más de 24 horas

Lev Lukomsky
fuente
1
El método distance_of_time_in_words es un ActionView Helper, por lo que debe llamarse desde la Vista (no desde el controlador). api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
msdundar
13
Tenga en cuenta que la opción strftime se desbordará a las 24 horas. Si su duración es de 25 horas, se mostrará 01:00:00.
Gabe Martin-Dempesy
2
esto no funciona para tiempos negativos, por ejemplo, -1800 devuelve -1h 30m en lugar de -30m
Arnold Roa
44

El %operador de cuerda de Ruby es demasiado poco apreciado y a menudo olvidado.

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

Dado que t es una duración en segundos, esto emite una cadena separada por dos puntos con relleno de ceros que incluye días. Ejemplo:

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

Encantador.

IAmNaN
fuente
24
Esa es una definición extraña de "encantador".
Marc Bollinger
10
El %dice: "Tome la matriz de parámetros de mi derecha e insértelos en la cadena de formato de mi izquierda". En ese sentido, es similar a printfen C \ C ++ solo que más conciso y puede usarse en una tarea. Sí, a eso lo llamo encantador. Y poderoso. Hay ejemplos que demostrarían mejor esta elocuencia, pero no responderían a la pregunta. Tu snark no es apreciado, por cierto.
IAmNaN
26

Supongo que también podrías hacer algo como:

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

Para formatearlo como desee.

Por cierto, originalmente pensé en usar Time.at () pero parece que la hora de EPOCH en mi Ubuntu es Thu Jan 01 01:00:00 +0100 1970 y no 00:00:00 horas como esperaba, y por lo tanto si lo hago:

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

Me da 1 hora más de la deseada.

Cristóbal Viedma
fuente
ahh, ¿podría hacer algo debido al horario de verano?
Cristobal Viedma
7
El único inconveniente es si se trata de tiempos de más de 24 horas. En ese caso, las horas retrocederán a un número bajo.
metavida
Para tiempos superiores a 24 horas,% j le dará el número de días (siempre que sea menor a 365)
BananaNeil
18

Utilizo esto para mostrar las duraciones de tiempo en mi proyecto Rails:

  1. Agrega un método personalizado a la Integerclase. Puede crear un nuevo archivo llamado pretty_duration.rben la initializerscarpeta:

    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. Llame a seconds.pretty_durationcualquier lugar de su proyecto:

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

Esta respuesta se basa en el Código de Lev Lukomsky

Sheharyar
fuente
Funcionó muy bien. Gracias por esto.
Donn Felker
13

Este usa el divmodmétodo oscuro para dividir y módulo al mismo tiempo, por lo que maneja los Floatsegundos correctamente:

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
fuente
6

El uso Time.utc.strftimefunciona solo para valores cuando el número total de horas es menor que 24 :

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

Para valores mayores, devuelve resultados incorrectos:

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

Sugiero usar el método más simple que encontré para este problema:

  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

Siempre puede ajustar el valor devuelto a sus necesidades.

Igor Springer
fuente
5

Tenga cuidado con la duración superior a un día.

(timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")
Xiao Bin
fuente
Dé un ejemplo de entrada / salida que destaque cómo esto resuelve el caso de la esquina
Cyril Duchon-Doris
4

Una respuesta inspirada en la de Lev Lukomsky que aprovecha ActiveSupport :: Duration y maneja milisegundos (útil para comparar el código)

# 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"

Por supuesto, puede extender esto fácilmente con días, meses, años, etc. utilizando métodos relacionados de ActiveSupport y repitiendo la misma estructura.

Tenga en cuenta que para duraciones demasiado largas, esto puede ser inexacto, ya que la duración de 1 mes no se fija en el número de días, y no estoy seguro de cómo AS: Duration trata eso.

Cyril Duchon-Doris
fuente
2

Solo para tirar mis 2 centavos:

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

Basado en la respuesta de Xiao Bin.

Daniel
fuente
2

Grita a @joshuapinter quién dio la mejor respuesta (en forma de comentario ).

Utilice el reemplazo directo dotiw gema de para obtener más control sobre la precisión de la salida para satisfacer diferentes necesidades:

https://github.com/radar/distance_of_time_in_words

Código de vista de muestra:

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

Dando como resultado algo como esto:

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

ActiveSupport::Duration.build+ inspectte da resultados válidos

 >> ActiveSupport::Duration.build(125557).inspect
 => "1 day, 10 hours, 52 minutes, and 37 seconds"
Sathish
fuente
1
    hours = 3.5456
    value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
    => "03:32"
Luciano Ribas
fuente
5
Agregue algo de contexto a la respuesta.
Paul Dawson