Ste*_*fan 24 ruby ruby-on-rails
我正在寻找一种优雅的方式来制作一系列日期时间,例如:
def DateRange(start_time, end_time, period)
...
end
>> results = DateRange(DateTime.new(2013,10,10,12), DateTime.new(2013,10,10,14), :hourly)
>> puts results
2013-10-10:12:00:00
2013-10-10:13:00:00
2013-10-10:14:00:00
Run Code Online (Sandbox Code Playgroud)
该步骤应该是可配置的,例如每小时,每天,每月.
我想要times包容,即包括end_time.
其他要求是:
:advance,来处理诸如几个月的可变天数之类的事情.有优雅的解决方案吗?
Dan*_*yen 25
使用@ CaptainPete的基础,我修改它以使用该ActiveSupport::DateTime#advance调用.当时间间隔不均匀时,例如":月"和":年",差异就会生效
require 'active_support/all'
class RailsDateRange < Range
# step is similar to DateTime#advance argument
def every(step, &block)
c_time = self.begin.to_datetime
finish_time = self.end.to_datetime
foo_compare = self.exclude_end? ? :< : :<=
arr = []
while c_time.send( foo_compare, finish_time) do
arr << c_time
c_time = c_time.advance(step)
end
return arr
end
end
# Convenience method
def RailsDateRange(range)
RailsDateRange.new(range.begin, range.end, range.exclude_end?)
end
Run Code Online (Sandbox Code Playgroud)
我的方法也返回一个Array.为了比较,我改变了@ CaptainPete的答案也返回了一个数组:
RailsDateRange((4.years.ago)..Time.now).every(years: 1)
=> [Tue, 13 Oct 2009 11:30:07 -0400,
Wed, 13 Oct 2010 11:30:07 -0400,
Thu, 13 Oct 2011 11:30:07 -0400,
Sat, 13 Oct 2012 11:30:07 -0400,
Sun, 13 Oct 2013 11:30:07 -0400]
DateRange((4.years.ago)..Time.now).every(1.year)
=> [2009-10-13 11:30:07 -0400,
2010-10-13 17:30:07 -0400,
2011-10-13 23:30:07 -0400,
2012-10-13 05:30:07 -0400,
2013-10-13 11:30:07 -0400]
Run Code Online (Sandbox Code Playgroud)
RailsDateRange((5.months.ago)..Time.now).every(months: 1)
=> [Mon, 13 May 2013 11:31:55 -0400,
Thu, 13 Jun 2013 11:31:55 -0400,
Sat, 13 Jul 2013 11:31:55 -0400,
Tue, 13 Aug 2013 11:31:55 -0400,
Fri, 13 Sep 2013 11:31:55 -0400,
Sun, 13 Oct 2013 11:31:55 -0400]
DateRange((5.months.ago)..Time.now).every(1.month)
=> [2013-05-13 11:31:55 -0400,
2013-06-12 11:31:55 -0400,
2013-07-12 11:31:55 -0400,
2013-08-11 11:31:55 -0400,
2013-09-10 11:31:55 -0400,
2013-10-10 11:31:55 -0400]
Run Code Online (Sandbox Code Playgroud)
RailsDateRange((4.years.ago)..Time.now).every(years: 1)
=> [Tue, 13 Oct 2009 11:30:07 -0400,
Wed, 13 Oct 2010 11:30:07 -0400,
Thu, 13 Oct 2011 11:30:07 -0400,
Sat, 13 Oct 2012 11:30:07 -0400,
Sun, 13 Oct 2013 11:30:07 -0400]
DateRange((4.years.ago)..Time.now).every(1.year)
=> [2009-10-13 11:30:07 -0400,
2010-10-13 17:30:07 -0400,
2011-10-13 23:30:07 -0400,
2012-10-13 05:30:07 -0400,
2013-10-13 11:30:07 -0400]
Run Code Online (Sandbox Code Playgroud)
kwa*_*ick 15
没有舍入错误,Range调用.succ枚举序列的方法,这不是你想要的.
不是一个单行,但是,一个简短的帮助函数就足够了:
def datetime_sequence(start, stop, step)
dates = [start]
while dates.last < (stop - step)
dates << (dates.last + step)
end
return dates
end
datetime_sequence(DateTime.now, DateTime.now + 1.day, 1.hour)
# [Mon, 30 Sep 2013 08:28:38 -0400, Mon, 30 Sep 2013 09:28:38 -0400, ...]
Run Code Online (Sandbox Code Playgroud)
但请注意,对于大范围而言,这可能在内存方面非常低效.
或者,您可以使用自纪元以来的秒数:
start = DateTime.now
stop = DateTime.now + 1.day
(start.to_i..stop.to_i).step(1.hour)
# => #<Enumerator: 1380545483..1380631883:step(3600 seconds)>
Run Code Online (Sandbox Code Playgroud)
你将拥有一系列整数,但你可以DateTime轻松转换回来:
Time.at(i).to_datetime
Run Code Online (Sandbox Code Playgroud)
cap*_*ete 14
Range#stepmodule RangeWithStepTime
def step(step_size = 1, &block)
return to_enum(:step, step_size) unless block_given?
# Defer to Range for steps other than durations on times
return super unless step_size.kind_of? ActiveSupport::Duration
# Advance through time using steps
time = self.begin
op = exclude_end? ? :< : :<=
while time.send(op, self.end)
yield time
time = step_size.parts.inject(time) { |t, (type, number)| t.advance(type => number) }
end
self
end
end
Range.prepend(RangeWithStepTime)
Run Code Online (Sandbox Code Playgroud)
Range#step方法添加持续时间支持(不需要子类,或方便的方法Object,虽然这仍然很有趣)1.hour + 3.seconds在step_size这增加了使用现有API对Range的持续时间的支持.它允许您使用我们期望简单"正常工作"的风格的常规范围.
# Now the question's invocation becomes even
# simpler and more flexible
step = 2.months + 4.days + 22.3.seconds
( Time.now .. 7.months.from_now ).step(step) do |time|
puts "It's #{time} (#{time.to_f})"
end
# It's 2013-10-17 13:25:07 +1100 (1381976707.275407)
# It's 2013-12-21 13:25:29 +1100 (1387592729.575407)
# It's 2014-02-25 13:25:51 +1100 (1393295151.8754072)
# It's 2014-04-29 13:26:14 +1000 (1398741974.1754072)
Run Code Online (Sandbox Code Playgroud)
...是添加#every使用DateRange < Range类+ DateRange"构造函数" Object,然后在内部将时间转换为整数,在step几秒钟内逐步执行.这最初不适用于时区.增加了对时区的支持,但随后发现另一个问题,即一些步骤持续时间是动态的(例如1.month).
阅读Rubinius的Range实现,很明显有人可能会增加支持ActiveSupport::Duration; 所以这个方法被改写了.非常感谢Dan Nguyen提供有关这方面的#advance提示和调试,以及Rubinius的精彩Range#step编写实现:D
这个补丁是不能合并到Rails /的ActiveSupport.你应该坚持for使用循环#advance.如果你正在获得can't iterate from Time或类似的东西,那么使用这个补丁,或者只是避免使用Range.
更新了补丁以反映Rails 4+ prepend风格alias_method_chain.
另一种解决方法是使用uniq方法。考虑示例:
date_range = (Date.parse('2019-01-05')..Date.parse('2019-03-01'))
date_range.uniq { |d| d.month }
# => [Sat, 05 Jan 2019, Fri, 01 Feb 2019]
date_range.uniq { |d| d.cweek }
# => [Sat, 05 Jan 2019, Mon, 07 Jan 2019, Mon, 14 Jan 2019, Mon, 21 Jan 2019, Mon, 28 Jan 2019, Mon, 04 Feb 2019, Mon, 11 Feb 2019, Mon, 18 Feb 2019, Mon, 25 Feb 2019]
Run Code Online (Sandbox Code Playgroud)
请注意,此方法尊重范围最小值和最大值
| 归档时间: |
|
| 查看次数: |
24714 次 |
| 最近记录: |