Ruby可以随时打印时间差(持续时间)吗?

Ruby可以做这样的事吗?

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" 

(持续时间方法现在不存在)……同样,报告

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

(报告整个持续时间,最多可报告多少秒,或报告最多2个数字和单位(如果报告日期和小时,则无需告知多少分钟))

这是实现此目的的一种快速而简单的方法。 设置秒,分钟,小时和天的预定义测量值。 然后根据数字的大小,输出与这些单位相应的字符串。 我们将扩展Numeric以便您可以在任何数字类( FixnumBignum或您的案例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 

看看Rails DateHelper.distance_of_time_in_words方法。 它会给你一个很好的起点。 尽管装载了魔法数字,但这种方法应该对你有用。

有一个gem可用https://rubygems.org/gems/time_diff

这给出了哈希的差异

在https://github.com/tmlee/time_difference上为https://rubygems.org/gems/time_difference – Ruby文档的Time Difference gem尝试一个ruby gem。

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

时差是一个漂亮的打印字符串:

  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 

作为数组的时差:

  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 

例:

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

以迈克尔·理查德的答案为基础 ,这里取代了能够使英语多元化正确的if块,并且不会说“14天0小时”之类的事情:

 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 

我无法承受在这里提出通用解决方案 – 尽管:有一年365天?

另外我在转换self.to_int时放了一个abs

 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 

和翻译

 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 

作为替代方案,您可以这样做:

 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" 

我在这里的脚本日志中使用[从那里复制]时给出了替代实现:

如何使用ruby on rails生成人类可读的时间范围

如果您想在几秒到几天的范围内显示重要的持续时间,则可以选择(因为它不必执行最佳):

 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' 

或者,您可能只对无关紧要的秒段部分感兴趣(同时演示另一种方法):

 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 

希望有所帮助。

我带来了这个解决方案

 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 

请注意,这不适用于超过60天的差异(在这种情况下,您需要添加条件)

 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 

然后你可以按照你想要的方式打印出来,

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