Konwertuj czas trwania na godziny: minuty: sekundy (lub podobne) w Rails 3 lub Ruby

115

Mam wrażenie, że jest na to prosty / wbudowany sposób, ale nie mogę go znaleźć.

Mam czas trwania (w sekundach) jako liczbę całkowitą i chcę wyświetlić go w przyjaznym formacie.

np. 3600 zostanie wyświetlone jako „01:00:00” lub „1 godzina” lub coś w tym rodzaju.

Mogę to zrobić, time_ago_in_words(Time.zone.now+3600)ale wydaje mi się, że to trochę hack, nie ma powodu, aby dodawać / odejmować od bieżącego czasu tylko po to, aby sformatować tę wartość. Jest duration_in_words()czy coś?

Dzięki

cydonia
źródło

Odpowiedzi:

83

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

distance_of_time_in_words(3600)
 => "about 1 hour"
allan
źródło
1
dzięki spojrzałem na to, ale pomyślałem, że muszę podać dwa razy .. co za nub!
cydonia
1
tak, te przykłady są głupie
allan
1
distance_of_time_in_words (from_time, to_time, ...)
boulder_ruby
1
Ponadto, jeśli chcesz, aby było to bardzo szczegółowe, a nie „zaokrąglane” czas trwania, sprawdź klejnot Radar: github.com/radar/distance_of_time_in_words . Zastępca w formie zastępczej dla distance_of_time_in_wordsi można uzyskać zaokrąglony numer, przekazując vague: truejako opcję.
Joshua Pinter
194

Podsumowując:

przy założeniu, że total_seconds = 3600

Opcja 1:

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

Opcja 2:

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

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

użyj Opcji 1, jeśli chcesz słów, Opcji 2, jeśli chcesz format H: M: S, Opcji 3, jeśli chcesz formatu H: M: S i może to trwać dłużej niż 24 godziny

Lev Lukomsky
źródło
1
Metoda distance_of_time_in_words jest pomocnikiem ActionView, dlatego musi być wywoływana z widoku (nie kontrolera). api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
msdundar
13
Zauważ, że opcja strftime przepełni się po 24 godzinach. Jeśli czas trwania wynosi 25 godzin, zostanie wyświetlony 01:00:00.
Gabe Martin-Dempesy
2
to nie działa dla czasów ujemnych, na przykład -1800 zwraca -1h 30m zamiast -30m
Arnold Roa,
45

%Operator łańcuchowy Rubiego jest zbyt niedoceniany i często zapomniany.

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

Biorąc pod uwagę, że t jest czasem trwania w sekundach, emitowany jest ciąg znaków rozdzielonych dwukropkami z zerami, w tym dni. Przykład:

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

Śliczny.

IAmNaN
źródło
24
To dziwna definicja „uroczego”.
Marc Bollinger
10
%Mówi, „Take szereg parametrów z mojej prawej i wstawić je w ciągu formatu na mojej lewej stronie.” Pod tym względem jest podobny do printfC \ C ++, tylko bardziej zwięzły i może być używany w przypisaniu. Tak, nazywam to cudownym. I potężny. Są przykłady, które lepiej zademonstrowałyby tę elokwencję, ale nie odpowiedziałyby na pytanie. Twój snark nie jest doceniany, przy okazji.
IAmNaN
26

Myślę, że możesz też zrobić coś takiego:

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

Aby sformatować to, jak chcesz.

BTW, początkowo myślałem o użyciu Time.at (), ale wydaje się, że czas EPOCH na moim Ubuntu to Thu Jan 01 01:00:00 +0100 1970, a nie 00:00:00, jak się spodziewałem, a zatem jeśli to zrobię:

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

Daje mi 1 godzinę więcej niż chciałem.

Cristobal Viedma
źródło
ahh, czy może coś zrobić z powodu czasu letniego?
Cristobal Viedma
7
Jedyny haczyk polega na tym, że masz do czynienia z czasami dłużej niż 24 godziny. W takim przypadku godziny cofną się do niskiej liczby.
metavida
W przypadku czasów dłuższych niż 24 godziny% j poda liczbę dni (o ile jest mniejsza niż 365)
BananaNeil,
18

Używam tego, aby pokazać czasy trwania w moim projekcie Rails:

  1. Dodaj metodę niestandardową do Integerklasy. Możesz utworzyć nowy plik o nazwie pretty_duration.rbw initializersfolderze:

    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. Zadzwoń do seconds.pretty_durationdowolnego miejsca w projekcie:

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

Ta odpowiedź opiera się na Kodeksie Lwa Łukomskiego

Sheharyar
źródło
Działało świetnie. Dziękuję Ci za to.
Donn Felker
13

Ten używa niejasnej divmodmetody dzielenia i modulo w tym samym czasie, więc Floatpoprawnie obsługuje sekundy:

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
źródło
6

Użycie Time.utc.strftimedziała tylko w przypadku wartości, których łączna liczba godzin jest mniejsza niż 24 :

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

W przypadku większych wartości zwraca nieprawidłowe wyniki:

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

Proponuję użyć najprostszej metody, którą znalazłem dla tego problemu:

  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

Zawsze możesz dostosować zwracaną wartość do swoich potrzeb.

Igor Springer
źródło
5

Uważaj na czas trwania dłuższy niż jeden dzień.

(timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")
Xiao Bin
źródło
Podaj przykład wejścia / wyjścia, podkreślając, jak to rozwiązuje problem narożny
Cyril Duchon-Doris
4

Odpowiedź zainspirowana odpowiedzią Lwa Lukomsky'ego wykorzystującą ActiveSupport :: Duration i obsługę milisekund (przydatne w kodzie porównawczym)

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

Oczywiście możesz to łatwo przedłużyć o dni, miesiące, lata itp., Używając powiązanych metod ActiveSupport i powtarzając tę ​​samą strukturę.

Pamiętaj, że w przypadku zbyt długich okresów może to być niedokładne, ponieważ czas trwania 1 miesiąca nie jest określony liczbą dni i nie jestem pewien, jak radzi sobie z tym AS: Duration.

Cyril Duchon-Doris
źródło
2

Wystarczy dorzucić moje 2 centy:

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

Zbudowany z odpowiedzi Xiao Bin.

Daniel
źródło
2

Krzycz do @joshuapinter, który udzielił najlepszej odpowiedzi (w formie komentarza ).

Użyj dotiwklejnotu zastępczego, aby uzyskać większą kontrolę nad dokładnością wyjścia w zależności od różnych potrzeb:

https://github.com/radar/distance_of_time_in_words

Przykładowy kod widoku:

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

W wyniku czegoś takiego:

Logoff after: 1 day, 13 hours, and 20 minutes
Jon Kern
źródło
2

ActiveSupport::Duration.build+ inspectdaje prawidłowe wyniki

 >> ActiveSupport::Duration.build(125557).inspect
 => "1 day, 10 hours, 52 minutes, and 37 seconds"
Sathish
źródło
1
    hours = 3.5456
    value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
    => "03:32"
Luciano Ribas
źródło
5
Dodaj kontekst do odpowiedzi.
Paul Dawson