在Ruby中如何将时间舍入到最接近的15分钟?
有没有一种简单的方法把时间缩短到最接近的15分钟 这就是我目前正在做的。有更简单的方法吗在Ruby中如何将时间舍入到最接近的15分钟?,ruby,Ruby,有没有一种简单的方法把时间缩短到最接近的15分钟 这就是我目前正在做的。有更简单的方法吗 t = Time.new rounded_t = Time.local(t.year, t.month, t.day, t.hour, t.min/15*15) 你可以做: Time.at(t.to_i/(15*60)*(15*60)) 我对ruby的语法不是很熟悉,但是您可以使用模运算将其四舍五入到最接近的15分钟。(即x-(x模15))。我猜语法应该是这样的 t.min - ( t.min % 15
t = Time.new
rounded_t = Time.local(t.year, t.month, t.day, t.hour, t.min/15*15)
你可以做:
Time.at(t.to_i/(15*60)*(15*60))
我对ruby的语法不是很熟悉,但是您可以使用模运算将其四舍五入到最接近的15分钟。(即x-(x模15))。我猜语法应该是这样的
t.min - ( t.min % 15)
这将使您的一组可能值为0、15、30和45。假设0您当前的评估使用
min / 15 * 15
只是截断了最小值,所以
15 => 15
16 => 15
..
29 => 15
30 => 30
这不是“四舍五入”
你可以用一种不好的方式来近似四舍五入
(( min + 7.5 ) / 15).to_i * 15
或者,使用内部构件:
( min.to_f / 15 ).round * 15
你说的是“向下四舍五入”,所以我不确定你实际上是在找那一圈还是地板,但这里有两个代码。我认为,如果您在Time类中添加round_off
和floor
方法,这样的内容读起来非常好。附加的好处是,您可以更轻松地按任何时间分区进行取整
require 'active_support/core_ext/numeric' # from gem 'activesupport'
class Time
# Time#round already exists with different meaning in Ruby 1.9
def round_off(seconds = 60)
Time.at((self.to_f / seconds).round * seconds).utc
end
def floor(seconds = 60)
Time.at((self.to_f / seconds).floor * seconds).utc
end
end
t = Time.now # => Thu Jan 15 21:26:36 -0500 2009
t.round_off(15.minutes) # => Thu Jan 15 21:30:00 -0500 2009
t.floor(15.minutes) # => Thu Jan 15 21:15:00 -0500 2009
注意:ActiveSupport仅对pretty
15.minutes
参数是必需的。如果您不想要这种依赖关系,请改用15*60
。因为Ruby允许计算时间(以秒为单位),所以您可以这样做:
t = Time.new
rounded_t = t-t.sec-t.min%15*60
我想我会发布另一个解决方案,提供向上和向下舍入到给定的最接近的秒数。哦,这不会像其他解决方案那样改变时区
class Time
def round(sec=1)
down = self - (self.to_i % sec)
up = down + sec
difference_down = self - down
difference_up = up - self
if (difference_down < difference_up)
return down
else
return up
end
end
end
t = Time.now # => Mon Nov 15 10:18:29 +0200 2010
t.round(15.minutes) # => Mon Nov 15 10:15:00 +0200 2010
t.round(20.minutes) # => Mon Nov 15 10:20:00 +0200 2010
t.round(60.minutes) # => Mon Nov 15 10:00:00 +0200 2010
上课时间
def循环(秒=1)
向下=自我-(自我到i%秒)
上升=下降+秒
差分下降=自下降
差异=向上-自我
if(差分向下<差分向上)
返回
其他的
返回
结束
结束
结束
t=Time.now#=>2010年11月15日星期一10:18:29+0200
t、 一轮(15分钟)#=>2010年11月15日星期一10:15:00+0200
t、 一轮(20分钟)#=>2010年11月15日星期一10:20:00+0200
t、 一轮(60分钟)#=>2010年11月15日星期一10:00:00+0200
ActiveSupport在x.minutes功能的示例中使用。你可以用15*60代替
基于此解决方案,可以轻松实现floor和ceil方法。Chuck的回答虽然优雅,但如果您尝试比较以这种方式导出的值,将给您带来麻烦;USEC没有归零 Shalmanese的答案解决了这个问题,Chuck的答案可以修改为:
t = Time.new
truncated_t = Time.at(t.to_i - t.sec - t.min % 15 * 60)
我找到了一个非常可读的解决方案 这将把你的时间四舍五入到最后一轮的15分钟。您可以将
15.minutes
更改为每个可能的时间刻度
Time.at(Time.now.to_i - (Time.now.to_i % 15.minutes))
前言
这里有很多解决方案,我开始怀疑它们的效率(效率可能不是这个问题中最重要的方面)。我从这里拿了一些,并投了我自己的一些。(注意,虽然OP询问将四舍五入到最接近的15分钟,但为了更简单的样本,我仅用1分钟/60秒进行了比较和采样)
安装程序
结果
分析结果 怎么理解呢?嗯,在你们的硬件上,这个东西可能工作得更快或更慢,所以不要相信我的电脑。正如你所看到的,另一件事是,除非我们在数百万次操作的规模上进行这些操作,否则就处理能力而言,使用哪种方法不会有多大区别(不过,请注意,例如,大多数云计算解决方案提供的处理能力非常低,因此在此类环境中,数百万可能是数百或数万) 最慢,但可能是最可读的解决方案 从这个意义上讲,显然使用最慢的时间
t=Time.now;t-(t.to_i%60).seconds
是合理的,因为.seconds在那里很酷
不是那么慢,而且几乎是可读的解决方案
然而,由于实际上根本不需要它,并且使操作的成本比没有它时高出一倍,我不得不说我的选择是t=Time.now;t-(t.to_I%60)
。在我看来,它的速度足够快,可读性比这里介绍的任何其他解决方案都要高百万倍。这就是为什么我认为它是满足您休闲地板需求的最佳解决方案,尽管它比其他三种解决方案慢得多
笨拙的,不特别慢或快的
本页投票最多的解决方案Time.at((Time.now.to_f/60).floor*60)
是本页所有解决方案中投票速度最慢的(在此答案之前)而且比前两种解决方案慢得多。使用浮点数仅仅是为了将小数舍入似乎也很不合逻辑。对于舍入部分来说,这是可以的,但是舍入对我来说听起来像是“地板”。如果有什么对应的话,它可能是舍入或“上限”,这类似于t=Time.now;t-(60-t.to_i%60)%60
或Time.at((Time.now.to_f/60).ceil*60)
。to_i解决方案在这里需要的双模看起来有点难看,因此,尽管它要快得多,在这里我还是更喜欢ceil方法
为那些需要速度的人
在测试中,两个使用稍有不同的运算组合的to_i变量,然后将整数转换回时间对象。如果您很匆忙,则应使用以下两个变量:
Time.at((Time.now.to_i / 60) * 60)
t = Time.now.to_i; Time.at(t - (t % 60))
四舍五入/ceil基准的设置 四舍五入/ceil基准的结果
Ryan McGeary的解决方案不适用于不在半小时内的时区。例如,加德满都是+5:45,因此四舍五入到30.5分钟会得到错误的结果。这应该是可行的:
class ActiveSupport::TimeWithZone
def floor(seconds = 60)
return self if seconds.zero?
Time.at(((self - self.utc_offset).to_f / seconds).floor * seconds).in_time_zone + self.utc_offset
end
def ceil(seconds = 60)
return self if seconds.zero?
Time.at(((self - self.utc_offset).to_f / seconds).ceil * seconds).in_time_zone + self.utc_offset
end
# returns whichever (out of #floor and #ceil) is closer to the current time
def closest(seconds = 60)
down, up = floor(seconds), ceil(seconds)
((self - down).abs > (self - up).abs) ? up : down
end
end
和测试:
class TimeHelperTest < ActionDispatch::IntegrationTest
test "floor" do
t = Time.now.change(min: 14)
assert_equal Time.now.change(min: 10), t.floor(5.minutes)
assert_equal Time.now.change(min: 0), t.floor(30.minutes)
end
test "ceil" do
t = Time.now.change(min: 16)
assert_equal Time.now.change(min: 20), t.ceil(5.minutes)
assert_equal Time.now.change(min: 30), t.ceil(30.minutes)
end
test "closest" do
t = Time.now.change(min: 18)
assert_equal Time.now.change(min: 20), t.closest(5.minutes)
assert_equal Time.now.change(min: 30), t.closest(30.minutes)
assert_equal Time.now.change(min: 0), t.closest(60.minutes)
end
test "works in time zones that are off the half hour" do
Time.zone = "Kathmandu"
#2.1.0p0 :028 > Time.zone.now
# => Tue, 30 Sep 2014 06:46:12 NPT +05:45 # doing .round(30.minutes) here would give 06:45 under the old method
t = Time.zone.now.change(min: 30)
assert_equal Time.zone.now.change(min: 30), t.closest(30.minutes)
t = Time.zone.now.change(min: 0)
assert_equal Time.zone.now.change(min: 0), t.closest(30.minutes)
end
end
class TimeHelperTestTime.at((Time.now.to_i / 60) * 60)
t = Time.now.to_i; Time.at(t - (t % 60))
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
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)
class ActiveSupport::TimeWithZone
def floor(seconds = 60)
return self if seconds.zero?
Time.at(((self - self.utc_offset).to_f / seconds).floor * seconds).in_time_zone + self.utc_offset
end
def ceil(seconds = 60)
return self if seconds.zero?
Time.at(((self - self.utc_offset).to_f / seconds).ceil * seconds).in_time_zone + self.utc_offset
end
# returns whichever (out of #floor and #ceil) is closer to the current time
def closest(seconds = 60)
down, up = floor(seconds), ceil(seconds)
((self - down).abs > (self - up).abs) ? up : down
end
end
class TimeHelperTest < ActionDispatch::IntegrationTest
test "floor" do
t = Time.now.change(min: 14)
assert_equal Time.now.change(min: 10), t.floor(5.minutes)
assert_equal Time.now.change(min: 0), t.floor(30.minutes)
end
test "ceil" do
t = Time.now.change(min: 16)
assert_equal Time.now.change(min: 20), t.ceil(5.minutes)
assert_equal Time.now.change(min: 30), t.ceil(30.minutes)
end
test "closest" do
t = Time.now.change(min: 18)
assert_equal Time.now.change(min: 20), t.closest(5.minutes)
assert_equal Time.now.change(min: 30), t.closest(30.minutes)
assert_equal Time.now.change(min: 0), t.closest(60.minutes)
end
test "works in time zones that are off the half hour" do
Time.zone = "Kathmandu"
#2.1.0p0 :028 > Time.zone.now
# => Tue, 30 Sep 2014 06:46:12 NPT +05:45 # doing .round(30.minutes) here would give 06:45 under the old method
t = Time.zone.now.change(min: 30)
assert_equal Time.zone.now.change(min: 30), t.closest(30.minutes)
t = Time.zone.now.change(min: 0)
assert_equal Time.zone.now.change(min: 0), t.closest(30.minutes)
end
end
require "rounding"
Time.current.floor_to(15.minutes) # => Thu, 07 May 2015 16:45:00 UTC +00:00
Time.current.ceil_to(15.minutes) # => Thu, 07 May 2015 17:00:00 UTC +00:00
Time.current.round_to(15.minutes) # => Thu, 07 May 2015 16:45:00 UTC +00:00