73 Stimmen

Wie rundet man in Ruby eine Zeit auf die nächsten 15 Minuten ab?

Gibt es eine einfache Möglichkeit, eine Zeit auf die nächsten 15 Minuten abzurunden?

Das ist es, was ich derzeit tue. Gibt es einen einfacheren Weg, dies zu tun?

t = Time.new
rounded_t = Time.local(t.year, t.month, t.day, t.hour, t.min/15*15)

11voto

Chuck Punkte 228137

Da Ruby die Arithmetik (in Sekunden) auf Times erlaubt, können Sie dies einfach tun:

t = Time.new
rounded_t = t-t.sec-t.min%15*60

8voto

Timo Punkte 3163

Vorwort

Es gibt hier eine ganze Reihe von Lösungen, und ich begann, mich über ihre Effizienz zu wundern (obwohl Effizienz bei diesem Problem wahrscheinlich nicht der wichtigste Aspekt ist). Ich habe einige von hier übernommen und ein paar eigene hinzugefügt. (N.B.: Obwohl der Auftraggeber darum bat, auf die nächsten 15 Minuten abzurunden, habe ich meine Vergleiche und Stichproben mit nur 1 Minute / 60 Sekunden durchgeführt, um einfachere Stichproben zu erhalten).

Einrichtung

Benchmark.bmbm do |x|
  x.report("to_f, /, floor, * and Time.at") { 1_000_000.times { Time.at((Time.now.to_f / 60).floor * 60) } }
  x.report("to_i, /, * and Time.at") { 1_000_000.times { Time.at((Time.now.to_i / 60) * 60) } }
  x.report("to_i, %, - and Time.at") { 1_000_000.times { t = Time.now.to_i; Time.at(t - (t % 60)) } }
  x.report("to_i, %, seconds and -") { 1_000_000.times { t = Time.now; t - (t.to_i % 60).seconds } }
  x.report("to_i, % and -") { 1_000_000.times { t = Time.now; t - (t.to_i % 60) } }
end

Ergebnisse

Rehearsal -----------------------------------------------------------------
to_f, /, floor, * and Time.at   4.380000   0.010000   4.390000 (  4.393235)
to_i, /, * and Time.at          3.270000   0.010000   3.280000 (  3.277615)
to_i, %, - and Time.at          3.220000   0.020000   3.240000 (  3.233176)
to_i, %, seconds and -         10.860000   0.020000  10.880000 ( 10.893103)
to_i, % and -                   4.450000   0.010000   4.460000 (  4.460001)
------------------------------------------------------- total: 26.250000sec

                                    user     system      total        real
to_f, /, floor, * and Time.at   4.400000   0.020000   4.420000 (  4.419075)
to_i, /, * and Time.at          3.220000   0.000000   3.220000 (  3.226546)
to_i, %, - and Time.at          3.270000   0.020000   3.290000 (  3.275769)
to_i, %, seconds and -         10.910000   0.010000  10.920000 ( 10.924287)
to_i, % and -                   4.500000   0.010000   4.510000 (  4.513809)

Analyse der Ergebnisse

Was ist davon zu halten? Nun, die Dinge können auf Ihrer Hardware schneller oder langsamer funktionieren, also nehmen Sie meinen Computer nicht beim Wort. Wie Sie sehen, macht es auch keinen großen Unterschied, welche Methode Sie verwenden, was die Rechenleistung angeht, es sei denn, wir führen diese Operationen im Umfang von Millionen von Operationen durch (beachten Sie jedoch, dass beispielsweise die meisten Cloud-Computing-Lösungen nur sehr wenig Rechenleistung bieten, so dass die Millionen in solchen Umgebungen vielleicht Hunderte oder Zehntausende sind).

Langsamste, aber wahrscheinlich die am besten lesbare Lösung

In diesem Sinne eindeutig der langsamste von ihnen t = Time.now; t - (t.to_i % 60).seconds kann gerechtfertigt sein, weil die .seconds dort so cool sind.

Nicht so langsam und fast genauso gut lesbare Lösung

Da sie aber eigentlich gar nicht benötigt wird und die Operation mehr als doppelt so teuer macht wie ohne sie, muss ich sagen, dass meine Wahl auf die t = Time.now; t - (t.to_i % 60) . Meiner Meinung nach ist sie schnell genug und millionenfach besser lesbar als alle anderen hier vorgestellten Lösungen. Deshalb denke ich, dass es die beste Lösung für Ihre gelegentlichen Bodenbelagsbedürfnisse ist, obwohl es deutlich langsamer ist als die drei anderen.

Unbeholfen und nicht besonders langsam oder schnell

Die meistgewählte Lösung auf dieser Seite Time.at((Time.now.to_f / 60).floor * 60) ist die langsamste aller Lösungen auf dieser Seite (vor dieser Antwort) und deutlich langsamer als die ersten beiden Lösungen. Die Verwendung von Fließkommazahlen, nur um die Dezimalstellen wegzudrücken, erscheint ebenfalls sehr unlogisch. Für das Runden wäre das in Ordnung, aber das Abrunden klingt für mich wie "Aufrunden". Wenn überhaupt, könnte das Gegenstück dazu das Aufrunden oder "Aufstocken" sein, was in etwa so aussehen würde t = Time.now; t - (60 - t.to_i % 60) % 60 ou Time.at((Time.now.to_f / 60).ceil * 60) . Das doppelte Modulo, das die to_i-Lösung hier benötigt, sieht etwas unschön aus, so dass ich hier die ceil-Methode bevorzugen würde, auch wenn sie deutlich schneller ist. (Benchmarks sind ganz am Ende dieses Beitrags angefügt)

Für diejenigen, die es eilig haben

Die beiden besten Ergebnisse im Test waren zwei to_i-Varianten, die leicht unterschiedliche Kombinationen von Operationen verwenden und dann Integer zurück in Time-Objekte konvertieren, wobei die Unterschiede so unbedeutend sind, dass man nicht wirklich einen Gewinner ausmachen kann. Wenn Sie es eilig haben, sollten Sie diese Varianten verwenden:

Time.at((Time.now.to_i / 60) * 60) 
t = Time.now.to_i; Time.at(t - (t % 60))

Einrichtung zum Aufrunden von Benchmarks / Obergrenzen

Benchmark.bmbm do |x|
  x.report("to_f, /, ceil, * and Time.at") { 1_000_000.times { Time.at((Time.now.to_f / 60).ceil * 60) } }
  x.report("to_i, %, -, %, + and Time.at") { 1_000_000.times { t = Time.now; t + (60 - t.to_i % 60) % 60 } }
end

Ergebnisse für das Aufrunden von Benchmarks / Obergrenzen

Rehearsal ----------------------------------------------------------------
to_f, /, ceil, * and Time.at   4.410000   0.040000   4.450000 (  4.446320)
to_i, %, -, %, + and Time.at   3.910000   0.020000   3.930000 (  3.939048)
------------------------------------------------------- total: 8.380000sec

                                   user     system      total        real
to_f, /, ceil, * and Time.at   4.420000   0.030000   4.450000 (  4.454173)
to_i, %, -, %, + and Time.at   3.860000   0.010000   3.870000 (  3.884866)

6voto

Shalmanese Punkte 5164

Das könnten Sie tun:

Time.at(t.to_i/(15*60)*(15*60))

4voto

David Lowenfels Punkte 917
# this is an extension of Ryan McGeary's solution, specifically for Rails.
# Note the use of utc, which is necessary to keep Rails time zone stuff happy.
# put this in config/initializers/time_extensions

require 'rubygems'
require 'active_support'

module TimeExtensions
  %w[ round floor ceil ].each do |_method|
    define_method _method do |*args|
      seconds = args.first || 60
      Time.at((self.to_f / seconds).send(_method) * seconds).utc
    end
  end
end

Time.send :include, TimeExtensions

3voto

denishaskin Punkte 3045

Chucks Antwort ist zwar elegant, bringt Sie aber in Schwierigkeiten, wenn Sie versuchen, auf diese Weise abgeleitete Werte zu vergleichen; die Usecs werden nicht auf Null gesetzt.

Mit der Antwort von Shalmanese ist das erledigt, oder Chucks Antwort kann wie folgt abgeändert werden:

t = Time.new
truncated_t = Time.at(t.to_i - t.sec - t.min % 15 * 60)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X