如何在Ruby中将时间缩短到最接近的15分钟?

是否有简单的方法将时间缩短到最接近的15分钟?

这是我目前正在做的。 有没有更简单的方法来做到这一点?

t = Time.new rounded_t = Time.local(t.year, t.month, t.day, t.hour, t.min/15*15) 

你说“下来”,所以我不知道你是否真的在寻找轮或地板,但是这是两个代码。 我觉得像这样的东西读取真的很好,如果你添加round_offfloor方法的时间类。 附加的好处是,你可以更容易地在任何时间分区。

 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_toround_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