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个数字和单位(如果报告日期和小时,则无需告知多少分钟))
答案 0 :(得分:36)
这是实现此目的的一种快速而简单的方法。设置秒,分钟,小时和天的预定义测量值。然后根据数字的大小,输出与这些单位相应的字符串。我们将扩展Numeric
,以便您可以在任何数字类(Fixnum
,Bignum
或您的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
答案 1 :(得分:4)
查看Rails DateHelper.distance_of_time_in_words方法。它会给你一个很好的起点。尽管装载了魔法数字,但这种方法应该适合你。
答案 2 :(得分:3)
有一颗宝石https://rubygems.org/gems/time_diff
这给出了散列
的区别答案 3 :(得分:3)
为那个https://rubygems.org/gems/time_difference - Ruby的时差宝石尝试一个ruby gem 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
答案 4 :(得分:2)
时差是一个漂亮的打印字符串:
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]
答案 5 :(得分:1)
在Michael Richard's answer的基础上,这里是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
答案 6 :(得分:1)
我无法承受在这里提出通用解决方案 - 尽管:有一年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<<stepper % div
stepper/=div
else
results << stepper
end
end
}
e= results.empty? ? 0 : results.count-1
mt= e>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<<stepper % div
stepper/=div
else
results << stepper
end
end
}
e= results.empty? ? 0 : results.count-1
mt= e>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
答案 7 :(得分:1)
我在这里[从那里复制]的脚本日志中使用了一个替代实现:
How to generate a human readable time range using 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(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'
或者你可能只对无关紧要的秒段部分感兴趣(也展示了另一种方法):
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
希望有所帮助。
答案 8 :(得分:0)
作为替代方案,您可以这样做:
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"
答案 9 :(得分:0)
我带来了这个解决方案
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天的差异,这不会起作用(在这种情况下,您需要添加条件)
答案 10 :(得分:0)
我知道这是一个特定于Ruby的问题,但是如果您像我一样找到它,则在Rails上下文中寻找解决方案,
请考虑使用ActiveSupport::Duration
,尤其是inspect
和parts
方法。
例如:
start = Time.now
# => 2020-02-15 19:12:50.128638
ActiveSupport::Duration.build(Time.now - start).inspect
# => "1 minute and 52.477355 seconds"
ActiveSupport::Duration.build(Time.now - start).parts
# => {:minutes=>1, :seconds=>55.457125}
如果您对inpect
的输出不满意,则可以尝试基于parts
哈希来实现自己的逻辑。
P.S。如果您有一个Plain Ruby项目,请不要忘记可以使用ActiveSupport
as a standalone library。
答案 11 :(得分:-1)
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