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

Respuestas:


83

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

distance_of_time_in_words(3600)
 => "about 1 hour"

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


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.


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.


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


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

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.


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

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.


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.


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

2

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

 >> ActiveSupport::Duration.build(125557).inspect
 => "1 day, 10 hours, 52 minutes, and 37 seconds"

1
    hours = 3.5456
    value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
    => "03:32"

5
Agregue algo de contexto a la respuesta.
Paul Dawson
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.