高效截断 Ruby Time 对象

发布于 2024-07-21 07:42:41 字数 528 浏览 2 评论 0原文

我试图想出一种根据给定分辨率截断 Ruby Time 对象的有效方法。

class Time
  def truncate resolution
    t = to_a
    case resolution
    when :min   
      t[0] = 0
    when :hour
      t[0] = t[1] = 0
    when :day
      t[0] = t[1] = 0
      t[2] = 1
    when :month 
      t[0] = t[1] = 0
      t[2] = t[3] = 1
    when :week  
      t[0] = t[1] = 0
      t[2] = 1
      t[3] -= t[6] - 1
    when :year
      t[0] = t[1] = 0
      t[2] = t[3] = t[4] = 1
    end

    Time.local *t
  end
end

有谁知道可以完成相同任务的更快版本?

I am trying to come up with an efficient method for truncating Ruby Time objects according to a given resolution.

class Time
  def truncate resolution
    t = to_a
    case resolution
    when :min   
      t[0] = 0
    when :hour
      t[0] = t[1] = 0
    when :day
      t[0] = t[1] = 0
      t[2] = 1
    when :month 
      t[0] = t[1] = 0
      t[2] = t[3] = 1
    when :week  
      t[0] = t[1] = 0
      t[2] = 1
      t[3] -= t[6] - 1
    when :year
      t[0] = t[1] = 0
      t[2] = t[3] = t[4] = 1
    end

    Time.local *t
  end
end

Does anyone know a faster version that achieves the same task?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

三人与歌 2024-07-28 07:42:42

感谢你们俩。 由于我不使用 Rails,因此我复制了代码来执行性能基准测试。

require 'benchmark'

class Time
  def truncate resolution
    t = to_a
    case resolution
    when :min   
      t[0] = 0
    when :hour
      t[0] = t[1] = 0
    when :day
      t[0] = t[1] = t[2] = 0
    when :week  
      t[0] = t[1] = t[2] = 0
      t[3] -= t[6] - 1
    when :month 
      t[0] = t[1] = t[2] = 0
      t[3] = 1
    when :year
      t[0] = t[1] = t[2] = 0
      t[3] = t[4] = 1
    end

    Time.local *t 
  end

  def truncate2 resolution
    opts = {}

    case resolution
    when :min   
      opts[:sec] = 0
    when :hour
      opts[:sec] = opts[:min] = 0
    when :day
      opts[:sec] = opts[:min] = opts[:hour] = 0
    when :week  
      opts[:sec] = opts[:min] = opts[:hour] = 0
      opts[:day] = wday - 1 if wday != 1
    when :month 
      opts[:sec] = opts[:min] = opts[:hour] = 0
      opts[:day] = 1
    when :year
      opts[:sec] = opts[:min] = opts[:hour] = 0
      opts[:day] = opts[:month] = 1
    end

    change opts 
  end

  def truncate3 resolution
    t = to_a
    if resolution == :week
      t[0] = t[1] = 0
      t[2] = 1
      t[3] -= t[6] - 1
    else
      len = [:sec, :min, :hour, :day, :month, :year].index(resolution)
      t.fill(0, 0, len)
      t.fill(1, 3, len-3)
    end

    Time.local *t
  end    

  def change opts 
    Time.local(
      opts[:year]  || year,
      opts[:month] || month,
      opts[:day]   || day,
      opts[:hour]  || hour,
      opts[:min]   || (opts[:hour] ? 0 : min),
      opts[:sec]   || ((opts[:hour] || opts[:min]) ? 0 : sec),
    )
  end
end

Resolutions = [:sec, :min, :hour, :day, :week, :month, :year]

# Correctness check.
puts Resolutions.map { |r| "#{r}: #{Time.now.truncate r}" } << "\n"
puts Resolutions.map { |r| "#{r}: #{Time.now.truncate2 r}" } << "\n"
puts Resolutions.map { |r| "#{r}: #{Time.now.truncate3 r}" } << "\n"

n = 100000
now = Time.now

Benchmark.bm(10) do |x|
  x.report("truncate") { n.times { Resolutions.each { |r| now.truncate  r } } }
  x.report("truncate2") { n.times { Resolutions.each { |r| now.truncate2 r } } }
  x.report("truncate3") { n.times { Resolutions.each { |r| now.truncate3 r } } }
end

Benchmark.bm(10) do |x|
  Resolutions.each do |unit| 
    x.report("#{unit}") { n.times { now.truncate unit } }
    x.report("#{unit}2") { n.times { now.truncate2 unit } }
    x.report("#{unit}3") { n.times { now.truncate3 unit } }
  end
end

结果如下:

sec: 2009-05-26 13:44:20 -0700
min: 2009-05-26 13:44:00 -0700
hour: 2009-05-26 13:00:00 -0700
day: 2009-05-26 00:00:00 -0700
week: 2009-05-25 00:00:00 -0700
month: 2009-05-01 00:00:00 -0700
year: 2008-12-31 23:00:00 -0800

sec: 2009-05-26 13:44:20 -0700
min: 2009-05-26 13:44:00 -0700
hour: 2009-05-26 13:00:00 -0700
day: 2009-05-26 00:00:00 -0700
week: 2009-05-01 00:00:00 -0700
month: 2009-05-01 00:00:00 -0700
year: 2009-01-01 00:00:00 -0800

sec: 2009-05-26 13:44:20 -0700
min: 2009-05-26 13:44:00 -0700
hour: 2009-05-26 13:00:00 -0700
day: 2009-05-26 00:00:00 -0700
week: 2009-05-25 01:00:00 -0700
month: 2009-05-01 00:00:00 -0700
year: 2008-12-31 23:00:00 -0800

                user     system      total        real
truncate    5.910000   0.020000   5.930000 (  5.947453)
truncate2   6.180000   0.020000   6.200000 (  6.232918)
truncate3   6.150000   0.020000   6.170000 (  6.253931)
                user     system      total        real
sec         0.720000   0.000000   0.720000 (  0.749040)
sec2        0.830000   0.010000   0.840000 (  0.863029)
sec3        0.800000   0.000000   0.800000 (  0.820477)
min         0.700000   0.000000   0.700000 (  0.709238)
min2        0.860000   0.010000   0.870000 (  0.860168)
min3        0.770000   0.000000   0.770000 (  0.795734)
hour        0.680000   0.000000   0.680000 (  0.705306)
hour2       0.850000   0.010000   0.860000 (  0.867235)
hour3       0.740000   0.000000   0.740000 (  0.746338)
day         0.720000   0.000000   0.720000 (  0.724324)
day2        0.890000   0.010000   0.900000 (  0.894312)
day3        0.780000   0.000000   0.780000 (  0.788007)
week        0.730000   0.000000   0.730000 (  0.736604)
week2       0.910000   0.000000   0.910000 (  0.910925)
week3       0.600000   0.000000   0.600000 (  0.611683)
month       0.720000   0.000000   0.720000 (  0.719515)
month2      0.880000   0.010000   0.890000 (  0.888045)
month3      0.780000   0.000000   0.780000 (  0.789726)
year        1.540000   0.010000   1.550000 (  1.565335)
year2       0.830000   0.000000   0.830000 (  0.849737)
year3       1.600000   0.010000   1.610000 (  1.644958)

看起来我的第一个截断版本仍然是最有效的,除了年份情况。 不过,今年 truncatetruncate3 有一个小怪癖。 它显示为

2008-12-31 23:00:00 -0800

而不是

2009-01-01 00:00:00 -0700

任何想法为什么?

Thanks to both of you. Since I don't use Rails I copied the code to perform a performance benchmark.

require 'benchmark'

class Time
  def truncate resolution
    t = to_a
    case resolution
    when :min   
      t[0] = 0
    when :hour
      t[0] = t[1] = 0
    when :day
      t[0] = t[1] = t[2] = 0
    when :week  
      t[0] = t[1] = t[2] = 0
      t[3] -= t[6] - 1
    when :month 
      t[0] = t[1] = t[2] = 0
      t[3] = 1
    when :year
      t[0] = t[1] = t[2] = 0
      t[3] = t[4] = 1
    end

    Time.local *t 
  end

  def truncate2 resolution
    opts = {}

    case resolution
    when :min   
      opts[:sec] = 0
    when :hour
      opts[:sec] = opts[:min] = 0
    when :day
      opts[:sec] = opts[:min] = opts[:hour] = 0
    when :week  
      opts[:sec] = opts[:min] = opts[:hour] = 0
      opts[:day] = wday - 1 if wday != 1
    when :month 
      opts[:sec] = opts[:min] = opts[:hour] = 0
      opts[:day] = 1
    when :year
      opts[:sec] = opts[:min] = opts[:hour] = 0
      opts[:day] = opts[:month] = 1
    end

    change opts 
  end

  def truncate3 resolution
    t = to_a
    if resolution == :week
      t[0] = t[1] = 0
      t[2] = 1
      t[3] -= t[6] - 1
    else
      len = [:sec, :min, :hour, :day, :month, :year].index(resolution)
      t.fill(0, 0, len)
      t.fill(1, 3, len-3)
    end

    Time.local *t
  end    

  def change opts 
    Time.local(
      opts[:year]  || year,
      opts[:month] || month,
      opts[:day]   || day,
      opts[:hour]  || hour,
      opts[:min]   || (opts[:hour] ? 0 : min),
      opts[:sec]   || ((opts[:hour] || opts[:min]) ? 0 : sec),
    )
  end
end

Resolutions = [:sec, :min, :hour, :day, :week, :month, :year]

# Correctness check.
puts Resolutions.map { |r| "#{r}: #{Time.now.truncate r}" } << "\n"
puts Resolutions.map { |r| "#{r}: #{Time.now.truncate2 r}" } << "\n"
puts Resolutions.map { |r| "#{r}: #{Time.now.truncate3 r}" } << "\n"

n = 100000
now = Time.now

Benchmark.bm(10) do |x|
  x.report("truncate") { n.times { Resolutions.each { |r| now.truncate  r } } }
  x.report("truncate2") { n.times { Resolutions.each { |r| now.truncate2 r } } }
  x.report("truncate3") { n.times { Resolutions.each { |r| now.truncate3 r } } }
end

Benchmark.bm(10) do |x|
  Resolutions.each do |unit| 
    x.report("#{unit}") { n.times { now.truncate unit } }
    x.report("#{unit}2") { n.times { now.truncate2 unit } }
    x.report("#{unit}3") { n.times { now.truncate3 unit } }
  end
end

Here are the results:

sec: 2009-05-26 13:44:20 -0700
min: 2009-05-26 13:44:00 -0700
hour: 2009-05-26 13:00:00 -0700
day: 2009-05-26 00:00:00 -0700
week: 2009-05-25 00:00:00 -0700
month: 2009-05-01 00:00:00 -0700
year: 2008-12-31 23:00:00 -0800

sec: 2009-05-26 13:44:20 -0700
min: 2009-05-26 13:44:00 -0700
hour: 2009-05-26 13:00:00 -0700
day: 2009-05-26 00:00:00 -0700
week: 2009-05-01 00:00:00 -0700
month: 2009-05-01 00:00:00 -0700
year: 2009-01-01 00:00:00 -0800

sec: 2009-05-26 13:44:20 -0700
min: 2009-05-26 13:44:00 -0700
hour: 2009-05-26 13:00:00 -0700
day: 2009-05-26 00:00:00 -0700
week: 2009-05-25 01:00:00 -0700
month: 2009-05-01 00:00:00 -0700
year: 2008-12-31 23:00:00 -0800

                user     system      total        real
truncate    5.910000   0.020000   5.930000 (  5.947453)
truncate2   6.180000   0.020000   6.200000 (  6.232918)
truncate3   6.150000   0.020000   6.170000 (  6.253931)
                user     system      total        real
sec         0.720000   0.000000   0.720000 (  0.749040)
sec2        0.830000   0.010000   0.840000 (  0.863029)
sec3        0.800000   0.000000   0.800000 (  0.820477)
min         0.700000   0.000000   0.700000 (  0.709238)
min2        0.860000   0.010000   0.870000 (  0.860168)
min3        0.770000   0.000000   0.770000 (  0.795734)
hour        0.680000   0.000000   0.680000 (  0.705306)
hour2       0.850000   0.010000   0.860000 (  0.867235)
hour3       0.740000   0.000000   0.740000 (  0.746338)
day         0.720000   0.000000   0.720000 (  0.724324)
day2        0.890000   0.010000   0.900000 (  0.894312)
day3        0.780000   0.000000   0.780000 (  0.788007)
week        0.730000   0.000000   0.730000 (  0.736604)
week2       0.910000   0.000000   0.910000 (  0.910925)
week3       0.600000   0.000000   0.600000 (  0.611683)
month       0.720000   0.000000   0.720000 (  0.719515)
month2      0.880000   0.010000   0.890000 (  0.888045)
month3      0.780000   0.000000   0.780000 (  0.789726)
year        1.540000   0.010000   1.550000 (  1.565335)
year2       0.830000   0.000000   0.830000 (  0.849737)
year3       1.600000   0.010000   1.610000 (  1.644958)

Seems like my first truncation version is still most efficient, except for the year case. There is a small quirk in the year for truncate and truncate3 though. It is displayed as

2008-12-31 23:00:00 -0800

rather than

2009-01-01 00:00:00 -0700

Any ideas why?

给我一枪 2024-07-28 07:42:42

如果你不想像格雷格建议的那样使用 ActiveSupport,我想你可以做这样的事情,

t = to_a
if resolution==:week
  t[0] = t[1] = 0
  t[2] = 1
  t[3] -= t[6] - 1
else
  len = [:sec, :min, :hour, :day, :month, :year].index(resolution)
  t.fill(0, 0,len)
  t.fill(1, 3,len-3)
end
Time.local *t

但不知道它是否更快......

If you don't want ActiveSupport as Greg suggests, I think you could do something like this

t = to_a
if resolution==:week
  t[0] = t[1] = 0
  t[2] = 1
  t[3] -= t[6] - 1
else
  len = [:sec, :min, :hour, :day, :month, :year].index(resolution)
  t.fill(0, 0,len)
  t.fill(1, 3,len-3)
end
Time.local *t

Don't know if it's faster though...

や莫失莫忘 2024-07-28 07:42:42

您不必使用 Rails 即可使用 ActiveSupport 功能。 除了想要使用 1.day3.minesTime.now.beginning__of__day 之类的东西之外,我还遇到了这个确切的问题

irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'activesupport'
=> true
irb(main):003:0> 1.day
=> 1 day
irb(main):004:0> 3.minutes
=> 180 seconds
irb(main):005:0> Time.now.beginning_of_day
=> Sun Jun 28 00:00:00 -0400 2009
irb(main):006:0>

You don't have to use Rails to use the ActiveSupport goodies. I ran into this exact problem in addition to wanting to use stuff like 1.day or 3.minutes or Time.now.beginning__of__day

irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'activesupport'
=> true
irb(main):003:0> 1.day
=> 1 day
irb(main):004:0> 3.minutes
=> 180 seconds
irb(main):005:0> Time.now.beginning_of_day
=> Sun Jun 28 00:00:00 -0400 2009
irb(main):006:0>
拥有 2024-07-28 07:42:41

这已经在 Rails 的 ActiveSupport 库的 时间扩展。 请参阅 change 方法以及各种 at_beginning_of_* 方法。

This has already been implemented for you in Rails' ActiveSupport library's Time extensions. See the change method, as well as the various at_beginning_of_* methods.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文