如何检查一个日期持续时间是否存在于另一个日期时间持续时间中



我正在开发一个用于考试管理的Rails应用程序。

考试可以创建一段时间。

Exam模型具有字段start_date:datetimeend_date:datetime

考试创建是一个分两步进行的过程。在第一步中,给出了包括start_dateend_date的所有检查细节。

在第二步中,我想列出与当前检查时间段冲突的所有检查,以便用户可以选择是否继续。

示例:

当前考试:

Start date and time: "2015-02-23 10:30:00"
End date and time: "2015-02-23 13:30:00"

冲突的考试:

Start date and time: "2015-02-23 12:30:00"
End date and time: "2015-02-23 14:30:00"

这怎么可能?

检查是否没有冲突是最简单的,然后以!开头。显然,如果一次考试在另一次考试结束后开始,或者在另一项考试开始前结束,就没有冲突。

我认为,如果一项考试在另一项考试结束的同时开始,就不会有冲突,但如果这被认为是冲突,那只是一个简单的改变。我还假设没有时间旅行(即考试在开始前就结束了)。

require 'time'
def conflict?(exam1, exam2)
  !(Time.parse(exam1.last)  <= Time.parse(exam2.first) ||
    Time.parse(exam1.first) >= Time.parse(exam2.last))
end
conflict?(["2015-02-23 10:30:00", "2015-02-23 13:30:00"],
          ["2015-02-23 12:30:00", "2015-02-23 14:30:00"])
  #=> true
conflict?(["2015-02-23 10:30:00", "2015-02-23 13:30:00"],
          ["2015-02-23 12:30:00", "2015-02-23 13:30:00"])
  #=> true
conflict?(["2015-02-23 10:30:00", "2015-02-23 12:30:00"],
          ["2015-02-23 12:30:00", "2015-02-23 13:30:00"])
  #=> false
conflict?(["2015-02-23 10:30:00", "2015-02-23 12:30:00"],
          ["2015-02-24 12:30:00", "2015-02-24 13:30:00"])
  #=> false

编辑:想想看,由于采用了日期-时间字符串格式,因此没有必要解析字符串。例如:

"2015-02-23 10:30:00" <= "2015-02-23 12:30:00"
  #=> true

我们有:

def conflict?(exam1, exam2)
  !(exam1.last <= exam2.first || exam1.first >= exam2.last)
end
conflict?(["2015-02-23 10:30:00", "2015-02-23 13:30:00"],
          ["2015-02-23 12:30:00", "2015-02-23 14:30:00"])
  #=> true
conflict?(["2015-02-23 10:30:00", "2015-02-23 12:30:00"],
          ["2015-02-23 12:30:00", "2015-02-23 13:30:00"])
  #=> false

试试这个,通过其他考试conf对象to overlaps方法。它将返回冲突的检查对象数组

conf_array = []
def overlaps(conf)
    conf_array << conf if  (start_date - conf.end_date) * (conf.start_date - end_date) >= 0
end

conf_array = []
def overlaps(conf)
    conf_array << conf if ((start_date..end_date).cover?(conf.start_date) || (start_date..end_date).cover?(conf.end_date))
end

conf_array = []
def overlaps(conf)
    conf_array << conf if ((start_date..end_date).include_with_range?conf.start_date || (start_date..end_date).include_with_range?conf.end_date )
end

创建一个名为exam_time_range的属性怎么样

def exam_time_range
  start_date..end_date
end

另一种检查冲突的方法

def time_conflict?(time)
  exam_time_range.cover?(time) || time.cover?(exam_time_range)
end

最后但同样重要的是,保持逻辑的方法

def list_all_conflicts
  Exam.find_each do |exam|
    if time_conflict?(exam.start_date..exam.end_date)
      exam
    end
  end
end

为什么不在单个AR查询中进行?就像下面的一样,但它绝对可以写得更优雅。

# whatever u want
e = Exam.first 
# Where start between e.start , e.end
# Or end between e.start , e.end
# And != e
Exam.where('start_date between datetime(?) and datetime(?) 
OR end_date between datetime(?) and datetime(?)', 
e.start_date, e.end_date, e.start_date, e.end_date)
.where.not(id: e.id)

最新更新