2010-11-09 6 views
23

Ich versuche, den besten Weg zu finden, die folgende AusgabeWie man einen Menschen lesbaren Zeitbereich zu erzeugen, Rubin mit auf Schienen

<name> job took 30 seconds 
<name> job took 1 minute and 20 seconds 
<name> job took 30 minutes and 1 second 
<name> job took 3 hours and 2 minutes 

Ich begann diesen Code

def time_range_details 
    time = (self.created_at..self.updated_at).count 
    sync_time = case time 
    when 0..60 then "#{time} secs"  
    else "#{time/60} minunte(s) and #{time-min*60} seconds" 
    end 
end 

Gibt es eine zu erzeugen effizienterer Weg, dies zu tun. Es scheint viel redundanter Code für etwas sehr Einfaches zu sein.

Eine andere Verwendung für diese ist:

<title> was posted 20 seconds ago 
<title> was posted 2 hours ago 

Der Code für dieses ähnlich ist, aber stattdessen i Time.now:

def time_since_posted 
    time = (self.created_at..Time.now).count 
    ... 
    ... 
end 

Antwort

50

Wenn Sie brauchen etwas mehr "präzise" als distance_of_time_in_words Sie etwas in dieser Richtung schreiben:

def humanize secs 
    [[60, :seconds], [60, :minutes], [24, :hours], [1000, :days]].map{ |count, name| 
    if secs > 0 
     secs, n = secs.divmod(count) 
     "#{n.to_i} #{name}" 
    end 
    }.compact.reverse.join(' ') 
end 

p humanize 1234 
#=>"20 minutes 34 seconds" 
p humanize 12345 
#=>"3 hours 25 minutes 45 seconds" 
p humanize 123456 
#=>"1 days 10 hours 17 minutes 36 seconds" 
p humanize(Time.now - Time.local(2010,11,5)) 
#=>"4 days 18 hours 24 minutes 7 seconds" 

Oh , eine Bemerkung zu deinem c ode:

(self.created_at..self.updated_at).count 

ist wirklich schlechte Möglichkeit, den Unterschied zu bekommen. Verwenden Sie einfach:

self.updated_at - self.created_at 
+0

nur neugierig, warum ist es schlecht? (self.created_at..self.updated_at) .count danke für die saubere Antwort! – csanz

+2

@csanz: '.count' durchläuft in diesem Fall ein Array jeder Sekunde zwischen den beiden Zeitstempeln und zählt sie. Als ob Sie das Ergebnis des Ausdrucks '100-50' berechnen würden, indem Sie tatsächlich alle Zahlen zwischen 50 und 100 zählen. –

+1

Ich bevorzuge die DateHelper-Methoden. Wenn Sie sich bemühen, es in Englisch zu konvertieren, dann möchten Sie wahrscheinlich nicht Tage und Sekunden kombinieren. Es ist eine künstliche Präzision. –

23

Es gibt zwei Methoden in DateHelper, dass Sie geben können, worauf Sie wollen:

  1. time_ago_in_words

    time_ago_in_words(1234.seconds.from_now) #=> "21 minutes" 
    
    time_ago_in_words(12345.seconds.ago)  #=> "about 3 hours" 
    
  2. distance_of_time_in_words

    distance_of_time_in_words(Time.now, 1234.seconds.from_now) #=> "21 minutes" 
    
    distance_of_time_in_words(Time.now, 12345.seconds.ago)  #=> "about 3 hours" 
    
0

Es gibt ein Problem mit distance_of_time_in_words wenn u es 1 Stunde passieren ll 30 min es werde zurückkehren ca. 2 Stunden

Einfach Helfer hinzufügen:

PERIODS = { 
    'day' => 86400, 
    'hour' => 3600, 
    'minute' => 60 
    } 


def formatted_time(total) 
    return 'now' if total.zero? 

    PERIODS.map do |name, span| 
    next if span > total 
    amount, total = total.divmod(span) 
    pluralize(amount, name) 
    end.compact.to_sentence 
end 

Übergeben Sie einfach Ihre Daten in Sekunden.

1

Wenn Sie erhebliche Zeiträume in den Sekunden bis Tage Bereich angezeigt werden soll, wäre eine Alternative (da sie nicht die beste Leistung hat):

def human_duration(secs, significant_only = true) 
    n = secs.round 
    parts = [60, 60, 24, 0].map{|d| next n if d.zero?; n, r = n.divmod d; r}. 
    reverse.zip(%w(d h m s)).drop_while{|n, u| n.zero? } 
    if significant_only 
    parts = parts[0..1] # no rounding, sorry 
    parts << '0' if parts.empty? 
    end 
    parts.flatten.join 
end 
start = Time.now 
# perform job 
puts "Elapsed time: #{human_duration(Time.now - start)}" 

human_duration(0.3) == '0' 
human_duration(0.5) == '1s' 
human_duration(60) == '1m0s' 
human_duration(4200) == '1h10m' 
human_duration(3600*24) == '1d0h' 
human_duration(3600*24 + 3*60*60) == '1d3h' 
human_duration(3600*24 + 3*60*60 + 59*60) == '1d3h' # simple code, doesn't round 
human_duration(3600*24 + 3*60*60 + 59*60, false) == '1d3h59m0s' 

Alternativ können Sie auch nur daran interessiert sein Strippen der Sekunden-Teil, wenn es nicht wichtig ist (auch einen anderen Ansatz demonstrierend):

def human_duration(duration_in_seconds) 
    n = duration_in_seconds.round 
    parts = [] 
    [60, 60, 24].each{|d| n, r = n.divmod d; parts << r; break if n.zero?} 
    parts << n unless n.zero? 
    pairs = parts.reverse.zip(%w(d h m s)[-parts.size..-1]) 
    pairs.pop if pairs.size > 2 # do not report seconds when irrelevant 
    pairs.flatten.join 
end 

Hoffe, dass hilft.

Verwandte Themen