Ruby può stampare facilmente la differenza di orario (durata)?

Ruby può fare qualcosa del genere?

irb(main):001:0> start = Time.now => Thu Nov 05 01:02:54 -0800 2009 irb(main):002:0> Time.now - start => 25.239 irb(main):003:0> (Time.now - start).duration => "25 seconds" 

(il metodo duration non esiste ora) … e allo stesso modo, rapporto

 23 minutes and 35 seconds 1 hour and 33 minutes 2 days and 3 hours 

(o riporta l’intera durata, fino a quanti secondi, oppure riporta fino a 2 numeri e unità (se viene indicato giorno e ora, non è necessario indicare quanti minuti))

Ecco un modo rapido e semplice per implementare questo. Imposta misurazioni predefinite per secondi, minuti, ore e giorni. Quindi, in base alla dimensione del numero, emettere la stringa appropriata con quelle unità. Estenderemo Numeric modo da poter invocare il metodo su qualsiasi class numerica ( Fixnum , Bignum o nel tuo caso Float ).

 class Numeric def duration secs = self.to_int mins = secs / 60 hours = mins / 60 days = hours / 24 if days > 0 "#{days} days and #{hours % 24} hours" elsif hours > 0 "#{hours} hours and #{mins % 60} minutes" elsif mins > 0 "#{mins} minutes and #{secs % 60} seconds" elsif secs >= 0 "#{secs} seconds" end end end 

C’è una gem disponibile https://rubygems.org/gems/time_diff

Che dà la differenza in un hash

Prova una gem ruby per quella https://rubygems.org/gems/time_difference – gem della differenza di tempo per la Ruby Documentation su https://github.com/tmlee/time_difference

 start_time = Time.new(2013,1) end_time = Time.new(2014,1) TimeDifference.between(start_time, end_time).in_years => 1.0 

Dai un’occhiata al metodo Rails DateHelper.distance_of_time_in_words . Ti darà un ottimo punto di partenza. Nonostante sia carico di numeri magici, l’approccio dovrebbe funzionare per te.

Differenza di tempo una stringa abbastanza stampata:

  class Numeric def duration rest, secs = self.divmod( 60 ) # self is the time difference t2 - t1 rest, mins = rest.divmod( 60 ) days, hours = rest.divmod( 24 ) # the above can be factored out as: # days, hours, mins, secs = self.duration_as_arr # # this is not so great, because it could include zero values: # self.duration_as_arr.zip ['Days','Hours','Minutes','Seconds']).flatten.join ' ' result = [] result << "#{days} Days" if days > 0 result << "#{hours} Hours" if hours > 0 result << "#{mins} Minutes" if mins > 0 result << "#{secs} Seconds" if secs > 0 return result.join(' ') end end 

Differenza di tempo come matrice:

  class Numeric def duration_as_arr rest, secs = self.divmod( 60 ) rest, mins = rest.divmod( 60 ) days, hours = rest.divmod( 24 ) [days, hours, mins, secs] end end 

Esempio:

  x = 1209801.079257 x.duration => "14 Days 3 Minutes 21.079257000004873 Seconds" x.duration_as_arr => [14, 0, 3, 21.079257000004873] 

Basandosi sulla risposta di Michael Richard , ecco un sostituto per il blocco if che ottiene la pluralizzazione inglese giusta, e non dirà cose come “14 giorni e 0 ore”:

 if days > 0 hour_remainder = hours % 24 if hour_remainder > 0 hour_str = hour_remainder == 1 ? 'hour' : 'hours' "#{days} days and #{hour_remainder} #{hour_str}" elsif days == 1 "#{days} day" else "#{days} days" end elsif hours > 0 min_remainder = mins % 60 if min_remainder > 0 min_str = min_remainder == 1 ? 'minute' : 'minutes' "#{hours} hours and #{min_remainder} #{min_str}" elsif hours == 1 "#{hours} hour" else "#{hours} hours" end elsif mins > 0 sec_remainder = secs % 60 if sec_remainder > 0 sec_str = sec_remainder == 1 ? 'second' : 'seconds' "#{mins} minutes and #{sec_remainder} #{sec_str}" elsif minutes == 1 "#{mins} minute" else "#{mins} minutes" end elsif secs == 1 "#{secs} second" elsif secs >= 0 "#{secs} seconds" end 

Non potevo sopportare di mettere una soluzione generica qui – sebbene: abbia un anno 365 giorni?

Aggiungo un abs durante la conversione di self.to_int

 class Numeric def duration steps=[60, 60, 24, 365,0] names=[:seconds, :minutes, :hours, :days, :years] results=[] stepper = self.to_int.abs steps.each { |div| if stepper>0 if div>0 results<0 ? results[e-1] : 0 et=results[e] || 0 et.to_s+" "+names[e].to_s + (mt>0 ? " "+mt.to_s+" "+names[e-1].to_s : '') end end 

e con la traduzione

 class Numeric def i18n_duration steps=[60, 60, 24, 365,0] names=[:seconds, :minutes, :hours, :days, :years] results=[] stepper = self.to_int.abs steps.each { |div| if stepper>0 if div>0 results<0 ? results[e-1] : 0 et=results[e] || 0 I18n.t("datetime.distance_in_words.x_#{names[e]}", count: et) + (mt>0 ? " "+I18n.t("datetime.distance_in_words.x_#{names[e-1]}", count: mt):'') end end 

In alternativa puoi farlo:

 start = DateTime.now.strftime('%Q').to_i / 1000.0 #=> 1409163050.088 sleep 3.5 #=> 3 now_ms = DateTime.now.strftime('%Q').to_i / 1000.0 #=> 1409163053.606 '%.3f' % (now_ms - start) #=> "3.518" 

Ho dato un’implementazione alternativa per questo quando usato nei log di script qui [copiati da lì]:

Come generare un intervallo di tempo leggibile dall’uomo usando il ruby su rotaie

Se vuoi mostrare durate significative nell’intervallo tra i secondi e i giorni, un’alternativa sarebbe (dato che non deve dare il meglio):

 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(dhms)).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' 

In alternativa potresti essere interessato solo a rimuovere la parte dei secondi quando non ha importanza (dimostrando anche un altro approccio):

 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(dhms)[-parts.size..-1]) pairs.pop if pairs.size > 2 # do not report seconds when irrelevant pairs.flatten.join end 

Spero possa aiutare.

Sono venuto con questa soluzione

 def formate_duration(started, ended) differences = ["secs", "mins", "hours", "days"].reduce([[nil, [(ended - started).round, nil]]]) do |acc, unit| mod = unit == "hours" ? 24 : 60 # will return [ over, unit_value ] # over is than used for calculation in next step # accumulator is in format [["unit" [ over, value]]] and we are interesting in latest over in each step # => This is why main diff is in this place in accumulator entry = acc[acc.size-1][1][0].divmod(mod) acc << [ unit, entry ] end # this will do string conversion and reverse in one step str = differences.drop(1).reduce("") do |acc, current| if (current[1][1] > 0) "#{current[1][1]} #{current[0]} #{acc}" else acc end end str.empty? ? "now" : str end 

Tieni presente che questo non funzionerà per differenze maggiori di 60 giorni (in tal caso dovrai aggiungere condizioni)

 time_difference = current_time - old_time def seconds_fraction_to_time(time_difference) days = hours = mins = 0 mins = (seconds / 60).to_i seconds = (seconds % 60 ).to_i hours = (mins / 60).to_i mins = (mins % 60).to_i days = (hours / 24).to_i hours = (hours % 24).to_i return [days,hours,mins,seconds] end 

allora puoi stampare tutto ciò che desideri,

vale a dire

 if(days > 0) return "#{days} Days #{hours} Hours" else return "#{hours} Hours #{mins} Minutes" end