在Ruby中如何将时间舍入到最接近的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

有没有一种简单的方法把时间缩短到最接近的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