Converti la durata in ore: minuti: secondi (o simili) in Rails 3 o Ruby


115

Ho la sensazione che ci sia un modo semplice / integrato per farlo, ma non riesco a trovarlo.

Ho una durata (in secondi) in un numero intero e voglio visualizzarla in un formato amichevole.

es. 3600 verrebbe visualizzato come "01:00:00" o "1 ora" o qualcosa del genere.

Posso farlo, time_ago_in_words(Time.zone.now+3600)ma sembra un po 'un trucco, non c'è motivo di aggiungere / sottrarre dall'ora corrente solo per formattare questo valore. C'è un duration_in_words()o qualcosa?

Grazie

Risposte:


83

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

distance_of_time_in_words(3600)
 => "about 1 hour"

1
grazie l'ho guardato ma ho pensato di doverlo fornire due volte .. che botta!
cydonia

1
sì, quegli esempi sono sciocchi
allan

1
distance_of_time_in_words (from_time, to_time, ...)
boulder_ruby

1
Inoltre, se vuoi che sia molto specifico e non "tondo" la durata, controlla la gemma di Radar: github.com/radar/distance_of_time_in_words . Sostituzione drop-in per distance_of_time_in_wordse puoi ottenere il numero arrotondato passando vague: truecome opzione.
Joshua Pinter

194

Riassumendo:

supponendo che total_seconds = 3600

Opzione 1:

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

Opzione 2:

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

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

uso Option1 se si desidera parole Option2 se si vuole H: M: formato S, Option3 se si vuole H: M: S formato e non ci può essere più di 24 ore


1
Il metodo distance_of_time_in_words è un ActionView Helper, quindi deve essere chiamato dalla View (non dal controller). api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
msdundar

13
Nota che l'opzione strftime andrà in overflow a 24 ore. Se la durata è di 25 ore, verrà visualizzato 01:00:00.
Gabe Martin-Dempesy

2
questo non funziona per tempi negativi, ad esempio -1800 restituisce -1h 30m invece di -30m
Arnold Roa

45

L' %operatore di stringa di Ruby è troppo poco apprezzato e spesso dimenticato.

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

Dato che t è una durata in secondi, genera una stringa separata da due punti con riempimento di zero che include i giorni. Esempio:

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

Bello.


24
Questa è una strana definizione di "adorabile".
Marc Bollinger

10
La %dice: "Prendete l'array di parametri da mia destra e inserirle nella stringa di formato alla mia sinistra." Sotto questo aspetto, è simile a printfin C \ C ++ solo più conciso e può essere utilizzato in un assegnamento. Sì, lo chiamo adorabile. E potente. Ci sono esempi che dimostrerebbero meglio questa eloquenza, ma non risponderebbero alla domanda. Il tuo snark non è apprezzato, btw.
IAmNaN

26

Immagino che potresti fare anche qualcosa del tipo:

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

Per formattarlo come desideri.

A proposito, inizialmente pensavo di usare Time.at () ma sembra che l'ora di EPOCH sul mio Ubuntu sia Thu Jan 01 01:00:00 +0100 1970 e non 00:00:00 come mi aspettavo, e quindi se lo faccio:

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

Mi dà 1 ora in più di quanto desiderato.


ahh, potrebbe fare qualcosa a causa dell'ora legale?
Cristobal Viedma

7
L'unico problema è se hai a che fare con tempi superiori a 24 ore. In tal caso le ore torneranno a un numero basso.
metavida

Per tempi superiori a 24 ore,% j ti darà il numero di giorni (a patto che sia inferiore a 365)
BananaNeil

18

Lo uso per mostrare le durate temporali nel mio progetto Rails:

  1. Aggiungi un metodo personalizzato alla Integerclasse. Puoi creare un nuovo file chiamato pretty_duration.rbnella initializerscartella:

    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. Chiama seconds.pretty_durationovunque nel tuo progetto:

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

Questa risposta si basa sul codice di Lev Lukomsky


Ha funzionato alla grande. Grazie per questo.
Donn Felker

13

Questo utilizza il divmodmetodo oscuro per dividere e modulo allo stesso tempo, quindi gestisce Floatcorrettamente i secondi:

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

L'utilizzo Time.utc.strftimefunziona solo per i valori quando il numero totale di ore è inferiore a 24 :

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

Per valori maggiori restituisce risultati errati:

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

Suggerisco di utilizzare il metodo più semplice che ho trovato per questo 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

Puoi sempre adattare il valore restituito alle tue esigenze.


5

Stai attento con la durata più lunga di un giorno.

(timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")

Fornisci un esempio di input / output che evidenzia come questo risolve il caso d'angolo
Cyril Duchon-Doris

4

Una risposta ispirata a quella di Lev Lukomsky che sfrutta ActiveSupport :: Duration e la gestione dei millisecondi (utile per il codice di benchmark)

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

Ovviamente puoi estenderlo facilmente con giorni, mesi, anni, ecc. Utilizzando i metodi ActiveSupport correlati e ripetendo la stessa struttura.

Tieni presente che per durate troppo lunghe, questo potrebbe essere impreciso poiché la durata di 1 mese non è fissata in numero di giorni e non sono sicuro di come AS: Duration lo gestisca.


2

Solo per buttare i miei 2 centesimi:

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

Costruito sulla risposta di Xiao Bin.


2

Grida a @joshuapinter che ha dato la risposta migliore (sotto forma di commento ).

Usa il sostituto drop-in dotiw gemma per ottenere un maggiore controllo sulla precisione dell'output per soddisfare le diverse esigenze:

https://github.com/radar/distance_of_time_in_words

Codice di visualizzazione di esempio:

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

Risultato in qualcosa di simile:

Logoff after: 1 day, 13 hours, and 20 minutes

2

ActiveSupport::Duration.build+ inspectti dà risultati validi

 >> 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
Aggiungi un po 'di contesto alla risposta.
Paul Dawson
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.