如何在Ruby中将时间缩短到最接近的15分钟?
是否有简单的方法将时间缩短到最接近的15分钟?
这是我目前正在做的。 有没有更简单的方法来做到这一点?
t = Time.new rounded_t = Time.local(t.year, t.month, t.day, t.hour, t.min/15*15)
你说“下来”,所以我不知道你是否真的在寻找轮或地板,但是这是两个代码。 我觉得像这样的东西读取真的很好,如果你添加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
注意:只有漂亮的15.minutes
参数才需要15.minutes
。 如果你不想要依赖关系,请改用15 * 60
。
我想我会张贴另一个解决scheme,提供上下舍入到最接近的秒数。 哦,这不会像其他一些解决scheme一样改变时区。
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
在x.minutes特性的例子中使用了ActiveSupport。 你可以用15 * 60代替。
基于这个解决scheme,可以很容易地实现方法floor和ceil。
我对ruby的语法不是很熟悉,但是可以使用模数向下舍入到最接近的15分钟。 (即x – (x模15))。 我会猜的语法会是这样的
t.min - ( t.min % 15)
这将使您的可能值为0,15,30和45的一组。假设0 <= t.min <= 59。
由于Ruby允许在Times上进行算术(以秒为单位),所以可以这样做:
t = Time.new rounded_t = tt.sec-t.min%15*60
我发现一个非常可读的解决scheme
这将会把你的时间缩短到最后的十五分钟。 您可以更改每个时间刻度的15.分钟。
Time.at(Time.now.to_i - (Time.now.to_i % 15.minutes))
我写了圆润的gem来处理这种情况。
缩短时间就像在时间上调用floor_to
一样简单。 四舍五入到最近的也被支持( ceil_to
和round_to
)。
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
原始时间的时区保留(本例中为UTC)。 你不需要加载ActiveSupport – 你可以写floor_to(15*60)
,它会正常工作。 gem使用有理数来避免舍入误差。 您可以通过提供偏移round_to(1.week, Time.parse("2015-5-4 00:00:00 UTC"))
来四舍五入到最近的星期一。 我们在生产中使用它。
我写了一篇博文 ,解释更多。 希望你觉得有帮助。
你可以这样做:
Time.at(t.to_i/(15*60)*(15*60))
# this is an extension of Ryan McGeary's solution, specifically for Rails. # Note the use of utc, which is necessary to keep Rails time zone stuff happy. # put this in config/initializers/time_extensions require 'rubygems' require 'active_support' module TimeExtensions %w[ round floor ceil ].each do |_method| define_method _method do |*args| seconds = args.first || 60 Time.at((self.to_f / seconds).send(_method) * seconds).utc end end end Time.send :include, TimeExtensions
前言
这里有很多解决scheme,我开始怀疑它们的效率(你的效率可能不是这个问题中最重要的方面)。 我从这里带走了一些,并投入了我自己的一些。 (注意,尽pipeOP问到了最近的15分钟,为了更简单的样本,我只做了1分钟/ 60秒的比较和样品)。
build立
Benchmark.bmbm do |x| x.report("to_f, /, floor, * and Time.at") { 1_000_000.times { Time.at((Time.now.to_f / 60).floor * 60) } } x.report("to_i, /, * and Time.at") { 1_000_000.times { Time.at((Time.now.to_i / 60) * 60) } } x.report("to_i, %, - and Time.at") { 1_000_000.times { t = Time.now.to_i; Time.at(t - (t % 60)) } } x.report("to_i, %, seconds and -") { 1_000_000.times { t = Time.now; t - (t.to_i % 60).seconds } } x.report("to_i, % and -") { 1_000_000.times { t = Time.now; t - (t.to_i % 60) } } end
结果
Rehearsal ----------------------------------------------------------------- to_f, /, floor, * and Time.at 4.380000 0.010000 4.390000 ( 4.393235) to_i, /, * and Time.at 3.270000 0.010000 3.280000 ( 3.277615) to_i, %, - and Time.at 3.220000 0.020000 3.240000 ( 3.233176) to_i, %, seconds and - 10.860000 0.020000 10.880000 ( 10.893103) to_i, % and - 4.450000 0.010000 4.460000 ( 4.460001) ------------------------------------------------------- total: 26.250000sec user system total real to_f, /, floor, * and Time.at 4.400000 0.020000 4.420000 ( 4.419075) to_i, /, * and Time.at 3.220000 0.000000 3.220000 ( 3.226546) to_i, %, - and Time.at 3.270000 0.020000 3.290000 ( 3.275769) to_i, %, seconds and - 10.910000 0.010000 10.920000 ( 10.924287) to_i, % and - 4.500000 0.010000 4.510000 ( 4.513809)
分析结果
做什么呢? 那么在你的硬件上,事情可能会更快或者更慢,所以不要把我的计算机当作词汇来使用。 正如你所看到的,另一件事情是,除非我们在数百万个操作的规模上进行这些操作,否则就处理能力而言,使用哪种方法并没有多大区别(尽pipe如此,请注意,例如大多数云计算解决scheme提供非常小的处理能力,因此在这样的环境中,数百万人可能会有数百或数万人)。
最慢,但可能是最可读的解决scheme
从这个意义上讲,清楚地使用它们中最慢的t = Time.now; t - (t.to_i % 60).seconds
t = Time.now; t - (t.to_i % 60).seconds
可以被certificate是因为.seconds在那里很酷。
不是那么慢,几乎可读的解决scheme
但是,因为实际上根本不需要这个操作,而且操作成本比没有这个操作高一倍,所以我不得不说我的select是t = Time.now; t - (t.to_i % 60)
t = Time.now; t - (t.to_i % 60)
。 在我看来,这个速度足够快,比其他任何解决scheme的可读性都高出百万倍。 这就是为什么我认为这是您休闲地板需求的最佳解决scheme,尽pipe这比其他三个要慢得多。
尴尬,不是特别慢或快
Time.at((Time.now.to_f / 60).floor * 60)
是本页面上所有解决scheme中最慢的一个(此答案之前),比前两个解决scheme慢得多。 使用浮动只是为了能够将小数点排除在外也似乎是非常不合逻辑的。 对于圆整的部分,没关系,但四舍五入的声音像“地板”给我。 如果有什么东西可能是四舍五入或“天花板”,这将是一些像t = Time.now; t - (60 - t.to_i % 60) % 60
t = Time.now; t - (60 - t.to_i % 60) % 60
或者Time.at((Time.now.to_f / 60).ceil * 60)
。 to_i解决scheme需要的双模有点讨厌,所以即使明显更快,在这里我更喜欢ceil方法。 (在这篇文章最后附加的基准)
对于那些需要速度的人
在testing中,两个to_ivariables使用稍微不同的操作组合,然后将整数转换回Time对象,这两个variables之间的联系(差异非常微不足道,以至于无法真正宣布获胜者)。 如果你匆忙,这些是你应该使用的:
Time.at((Time.now.to_i / 60) * 60) t = Time.now.to_i; Time.at(t - (t % 60))
设置四舍五入/ ceil基准
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)
查克的回答,虽然优雅,但如果你试图比较以这种方式得出的值,将会使你陷入麻烦; usecs不会被清零。
Shalmanese的答案照顾,或查克的可以修改为:
t = Time.new truncated_t = Time.at(t.to_i - t.sec - t.min % 15 * 60)
瑞安麦克格里的解决scheme不适用于半小时以外的时区。 例如,加德满都是+5:45,所以四舍五入到30.分钟得到了错误的结果。 这应该工作:
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
并testing:
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
您目前的评估使用
min / 15 * 15
只是截断min,所以
15 => 15 16 => 15 .. 29 => 15 30 => 30
这不是“四舍五入”。
你可以近似于四舍五入
(( min + 7.5 ) / 15).to_i * 15
或者,使用内部:
( min.to_f / 15 ).round * 15