在ruby中,给定两个日期范围,我想要表示两个日期范围交集的范围,如果没有交集,则需要nil。例如:
(Date.new(2011,1,1)..Date.new(2011,1,15)) & (Date.new(2011,1,10)..Date.new(2011,2,15))
=> Mon, 10 Jan 2011..Sat, 15 Jan 2011
编辑:应该说我希望它也适用于DateTime,所以间隔可以缩短到分钟和秒:
(DateTime.new(2011,1,1,22,45)..Date.new(2011,2,15)) & (Date.new(2011,1,1)..Date.new(2011,2,15))
=> Sat, 01 Jan 2011 22:45:00 +0000..Tue, 15 Feb 2011
答案 0 :(得分:23)
require 'date'
class Range
def intersection(other)
return nil if (self.max < other.begin or other.max < self.begin)
[self.begin, other.begin].max..[self.max, other.max].min
end
alias_method :&, :intersection
end
p (Date.new(2011,1,1)..Date.new(2011,1,15)) & (Date.new(2011,1,10)..Date.new(2011,2,15))
#<Date: 2011-01-10 ((2455572j,0s,0n),+0s,2299161j)>..#<Date: 2011-01-15 ((2455577j,0s,0n),+0s,2299161j)>
答案 1 :(得分:7)
您可以尝试这样做以获得表示交叉点的范围
range1 = Date.new(2011,12,1)..Date.new(2011,12,10)
range2 = Date.new(2011,12,4)..Date.new(2011,12,12)
inters = range1.to_a & range2.to_a
intersected_range = inters.min..inters.max
转换您的示例:
class Range
def intersection(other)
raise ArgumentError, 'value must be a Range' unless other.kind_of?(Range)
inters = self.to_a & other.to_a
inters.empty? ? nil : inters.min..inters.max
end
alias_method :&, :intersection
end
答案 2 :(得分:2)
我为解决范围提升了这个解决方案,同时也处理了排除结束的情况:
intersect_ranges = ->(r1, r2) do
new_end = [r1.end, r2.end].min
new_begin = [r1.begin, r2.begin].max
exclude_end = (r2.exclude_end? && new_end == r2.end) || (r1.exclude_end? && new_end == r1.end)
valid = (new_begin <= new_end && !exclude_end)
valid ||= (new_begin < new_end && exclude_end))
valid ? Range.new(new_begin, new_end, exclude_end) : nil
end
我也有点担心你们将它添加到Range类本身,因为交叉范围的行为没有统一定义。 (如果相交1 ... 4和4 ... 1?为什么没有交叉时为零;我们也可以说这是一个空范围:1 ... 1)
答案 3 :(得分:1)
我发现了这个:http://www.postal-code.com/binarycode/2009/06/06/better-range-intersection-in-ruby/这是一个非常好的开始,但不适用于约会。我已经对此进行了一些调整:
class Range
def intersection(other)
raise ArgumentError, 'value must be a Range' unless other.kind_of?(Range)
new_min = self.cover?(other.min) ? other.min : other.cover?(min) ? min : nil
new_max = self.cover?(other.max) ? other.max : other.cover?(max) ? max : nil
new_min && new_max ? new_min..new_max : nil
end
alias_method :&, :intersection
end
我省略了测试,但它们基本上是上面更改日期的帖子中的测试。这适用于ruby 1.9.2。
任何人都有更好的解决方案吗?
答案 4 :(得分:0)
尝试这样的事情
require 'date'
sample = Date.parse('2011-01-01')
sample1 = Date.parse('2011-01-15')
sample2 = Date.parse('2010-12-19')
sample3 = Date.parse('2011-01-11')
puts (sample..sample1).to_a & (sample2..sample3).to_a
这将给你一个交叉日期数组!!
答案 5 :(得分:0)
我的时间为[[start, end], ...]
,我希望从每个初始时间范围中删除一些时间范围,这就是我所做的:
def exclude_intersecting_time_ranges(initial_times, other_times)
initial_times.map { |initial_time|
other_times.each do |other_time|
next unless initial_time
# Other started after initial ended
next if other_time.first >= initial_time.last
# Other ended before initial started
next if other_time.last <= initial_time.first
# if other time started before and ended after after, no hour is counted
if other_time.first <= initial_time.first && other_time.last >= initial_time.last
initial_time = nil
# if other time range is inside initial time range, split in two time ranges
elsif initial_time.first < other_time.first && initial_time.last > other_time.last
initial_times.push([other_time.last, initial_time.last])
initial_time = [initial_time.first, other_time.first]
# if start time of other time range is before initial time range
elsif other_time.first <= initial_time.first
initial_time = [other_time.last, initial_time.last]
# if end time of other time range if after initial time range
elsif other_time.last >= initial_time.last
initial_time = [initial_time.first, other_time.first]
end
end
initial_time
}.compact
end
答案 6 :(得分:0)
由于这个问题与How to combine overlapping time ranges (time ranges union)有关,我还想在这里发布我对gem range_operators的发现,因为如果在同样的情况下帮助我。
答案 7 :(得分:0)
从 Rails v3 开始,您可以将 overlaps?
与 Range
一起使用
# For dates, make sure you have the correct format
first_range = first_start.to_date..first_end.to_date
second_range = second_start.to_date..second_end.to_date
intersection = first_range.overlaps?(second_range) # => Boolean
# Example with numbers
(1..7).overlaps?(3..5) # => true
docs 中的更多详细信息
答案 8 :(得分:-1)
我将它们转移到一个数组中,因为数组知道交叉操作:
(Date.new(2011,1,1)..Date.new(2011,1,15)).to_a & (Date.new(2011,1,10)..Date.new(2011,2,15)).to_a
当然这会返回一个数组。因此,如果你想要一个枚举器(范围似乎不可能,因为它们不再是连续值),只需在最后抛出to_enum
。