Konvertieren Sie die Dauer in Rails 3 oder Ruby in Stunden: Minuten: Sekunden (oder ähnliches)

114

Ich habe das Gefühl, dass es einen einfachen / eingebauten Weg gibt, dies zu tun, aber ich kann ihn nicht finden.

Ich habe eine Dauer (in Sekunden) in einer Ganzzahl und möchte sie in einem benutzerfreundlichen Format anzeigen.

zB 3600 würde als "01:00:00" oder "1 Stunde" oder so angezeigt.

Ich kann es damit machen, time_ago_in_words(Time.zone.now+3600)aber das fühlt sich wie ein Hack an. Es gibt keinen Grund, die aktuelle Zeit zu addieren / subtrahieren, nur um diesen Wert zu formatieren. Gibt es ein duration_in_words()oder etwas?

Vielen Dank

Cydonia
quelle

Antworten:

82

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

distance_of_time_in_words(3600)
 => "about 1 hour"
Allan
quelle
1
danke ich habe mir das angeschaut aber dachte ich müsste zweimal liefern .. was für ein nub!
Cydonia
1
Ja, diese Beispiele sind dumm
Allan
1
distance_of_time_in_words (from_time, to_time, ...)
boulder_ruby
1
Wenn Sie möchten, dass dies sehr spezifisch ist und nicht die Dauer "rund", sehen Sie sich Radars Juwel an: github.com/radar/distance_of_time_in_words . Drop-In-Ersatz für distance_of_time_in_wordsund Sie können die gerundete Zahl erhalten, indem Sie vague: trueals Option übergeben.
Joshua Pinter
194

Zusammenfassen:

vorausgesetzt, dass 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"

Verwendung Option1 , wenn Sie Wörter wollen, Option2 , wenn Sie H: M: S - Format, Option3 wenn Sie H: M: S - Format , und es kann mehr als 24 Stunden sein

Lev Lukomsky
quelle
1
Die Methode distance_of_time_in_words ist ein ActionView-Helfer und muss daher aus der Ansicht (nicht vom Controller) aufgerufen werden. api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
msdundar
13
Beachten Sie, dass die Strftime-Option nach 24 Stunden überläuft. Wenn Ihre Dauer 25 Stunden beträgt, wird sie angezeigt 01:00:00.
Gabe Martin-Dempesy
2
Dies funktioniert nicht für negative Zeiten, zum Beispiel gibt -1800 -1h 30m statt -30m zurück
Arnold Roa
43

Rubys String- %Operator wird zu wenig geschätzt und oft vergessen.

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

Wenn t eine Dauer in Sekunden ist, wird eine durch Nullen aufgefüllte, durch Doppelpunkte getrennte Zeichenfolge einschließlich Tagen ausgegeben. Beispiel:

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

Schön.

IAmNaN
quelle
24
Das ist eine seltsame Definition von "schön".
Marc Bollinger
10
Der %sagt: "Nehmen Sie das Array von Parametern von rechts und fügen Sie sie in die Formatzeichenfolge auf meiner linken Seite ein." Insofern ist es ähnlich wie printfin C \ C ++ nur prägnanter und kann in einer Zuordnung verwendet werden. Ja, das nenne ich schön. Und mächtig. Es gibt Beispiele, die diese Beredsamkeit besser demonstrieren würden, aber sie würden die Frage nicht beantworten. Ihr Snark wird übrigens nicht geschätzt.
IAmNaN
26

Ich denke du könntest auch so etwas machen wie:

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

So formatieren Sie es nach Ihren Wünschen.

Übrigens, ursprünglich habe ich daran gedacht, Time.at () zu verwenden, aber anscheinend ist die EPOCH-Zeit auf meinem Ubuntu Do 01. Januar 01:00:00 +0100 1970 und nicht 00:00:00 Stunden, wie ich erwartet hatte, und daher, wenn ich dies tue:

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

Gibt mir 1 Stunde mehr als gewünscht.

Cristobal Viedma
quelle
ahh, könnte wegen der Sommerzeit etwas zu tun sein?
Cristobal Viedma
7
Der einzige Haken ist, wenn Sie mit Zeiten von mehr als 24 Stunden zu tun haben. In diesem Fall werden die Stunden auf einen niedrigen Wert zurückgesetzt.
Metavida
Für Zeiten von mehr als 24 Stunden gibt% j die Anzahl der Tage an (solange es weniger als 365 ist)
BananaNeil
17

Ich benutze dies, um die Zeitdauer in meinem Rails-Projekt anzuzeigen:

  1. Fügen Sie der IntegerKlasse eine benutzerdefinierte Methode hinzu . Sie können eine neue Datei erstellen, die pretty_duration.rbim initializersOrdner aufgerufen wird :

    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. Rufen Sie seconds.pretty_durationüberall in Ihrem Projekt an:

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

Diese Antwort baut auf Lev Lukomskys Code auf

Sheharyar
quelle
Hat super funktioniert. Danke dafür.
Donn Felker
13

Dieser verwendet die obskure divmodMethode, um gleichzeitig zu teilen und zu modulieren, sodass FloatSekunden richtig behandelt werden:

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

Die Verwendung Time.utc.strftimefunktioniert nur für Werte, wenn die Gesamtstundenzahl weniger als 24 beträgt :

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

Bei höheren Werten werden falsche Ergebnisse zurückgegeben:

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

Ich schlage vor, die einfachste Methode zu verwenden, die ich für dieses Problem gefunden habe:

  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

Sie können den zurückgegebenen Wert jederzeit an Ihre Bedürfnisse anpassen.

Igor Springer
quelle
5

Seien Sie vorsichtig mit einer Dauer von mehr als einem Tag.

(timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")
Xiao Bin
quelle
Geben Sie ein Beispiel für Eingabe / Ausgabe, das hervorhebt, wie dies den
Eckfall
4

Eine Antwort, die von Lev Lukomskys Antwort inspiriert wurde, die ActiveSupport :: Duration nutzt und Millisekunden verarbeitet (nützlich für das Benchmarking von 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"

Natürlich können Sie dies problemlos mit Tagen, Monaten, Jahren usw. erweitern, indem Sie verwandte ActiveSupport-Methoden verwenden und dieselbe Struktur wiederholen.

Denken Sie daran, dass dies für zu lange Zeiträume möglicherweise ungenau ist, da die Dauer von 1 Monat nicht in Tagen festgelegt ist, und ich bin mir nicht sicher, wie AS: Duration damit umgeht.

Cyril Duchon-Doris
quelle
2

Nur um meine 2 Cent einzubringen:

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

Gebaut aus Xiao Bins Antwort.

Daniel
quelle
2

Shout out zu @joshuapinter , die die beste Antwort gab (in Form eines Kommentars ).

Verwenden Sie den Drop-In-Ersatz dotiw , um mehr Kontrolle über die Genauigkeit der Ausgabe zu erhalten, um den unterschiedlichen Anforderungen gerecht zu werden:

https://github.com/radar/distance_of_time_in_words

Beispielansichtscode:

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

Das Ergebnis ist so etwas:

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

ActiveSupport::Duration.build+ inspectgibt Ihnen gültige Ergebnisse

 >> ActiveSupport::Duration.build(125557).inspect
 => "1 day, 10 hours, 52 minutes, and 37 seconds"
Sathish
quelle
1
    hours = 3.5456
    value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
    => "03:32"
Luciano Ribas
quelle
5
Bitte fügen Sie der Antwort einen Kontext hinzu.
Paul Dawson