检查两个“时间范围”是否相互重叠

时间:2019-03-06 13:25:35

标签: python django

我有一个对象保存在db中,时间范围为(t1): 11:45-00:15。现在,我有另一个请求时间范围:(t2) 00:05-00:10。查找此新时间范围t2是否与时间范围t1的已保存对象重叠的最佳方法是什么。午夜就是这种情况,那是一天在变的地方。对于同一天,我可以成功找到重叠的时间范围。我没有datetime字段,而是只有time字段,因此我必须处理已有的内容。

在模型t1中,其存储为:

start_time = TimeField(null=True, blank=True, db_index=True)
end_time = TimeField(null=True, blank=True, db_index=True)

因此11:45将存储在start_time中,而00:15将存储在end_time

2 个答案:

答案 0 :(得分:1)

这个逻辑应该可以:

if (d1.start_time < d2.start_time < d1.end_time) or (d1.start_time < d2.end_time < d1.end_time) or (d2.start_time <= d1.start_time and d2.end_time >= d1.end_time): return True

说明:

首先,检查 d2 的开始和结束时间是否与 w d1 的时间范围重叠。如果是,则返回 True。

接下来,查看 d2 是否大于或与 d1 相同。如果是,则返回 True。

否则,这些日期不会重叠。

答案 1 :(得分:0)

我假设在start_time大于end_time的情况下,时间范围应该只是重叠,因为时间是周期性的,我们只是不在乎日期(换句话说,这些范围代表每天发生的事件,我们想检测是否有任何事件与其他任何事件重叠

基于此假设,这里是快速答案:

if t2.start_time > t2.end_time:
    condition = (
        Q(start_time__gt=F('end_time')) |
        Q(start_time__lt=t2.end_time) |
        Q(end_time__gt=t2.start_time)
    )
else:
    condition = (
        Q(start_time__gt=F('end_time'), start_time__lt=t2.end_time) |
        Q(start_time__gt=F('end_time'), end_time__gt=t2.start_time) |
        Q(start_time__lt=t2.end_time, end_time__gt=t2.start_time)
    )

说明

有24种安排每个事件的开始时间和结束时间的情况(不包括两个日期相等的情况)。在这24个案例中,其中20个表示存在重叠,而其中4个表示没有重叠。由于没有重叠包含的情况要少得多,所以让我们仔细看一下,下面是这4种情况:

  1. t1.end_time < t2.start_time < t2.end_time < t1.start_time
  2. t1.start_time < t1.end_time < t2.start_time < t2.end_time
  3. t2.start_time < t2.end_time < t1.start_time < t1.end_time
  4. t2.end_time < t1.start_time < t1.end_time < t2.start_time

您可以按以下方式在python代码中表示这些情况(添加了多余的空格以显示如何将它们分组):

(                                t1.start_time > t2.end_time and t2.start_time > t1.end_time and t2.start_time < t2.end_time) or \ # 1.
(t1.start_time < t1.end_time                                 and t2.start_time > t1.end_time and t2.start_time < t2.end_time) or \ # 2.
(t1.start_time < t1.end_time and t1.start_time > t2.end_time                                 and t2.start_time < t2.end_time) or \ # 3.
(t1.start_time < t1.end_time and t1.start_time > t2.end_time and t2.start_time > t1.end_time                                )      # 4. 

我们可以看到,如果这4个条件中的任何3个条件适用,则可以简化判断项是否重叠:

(
    t1.start_time < t1.end_time,
    t1.start_time > t2.end_time,
    t2.start_time > t1.end_time,
    t2.start_time < t2.end_time,
)

如果要确定2个项目是否重叠,则必须逆转此条件。因此,如果这4个条件中的2个条件中的任何一个满足条件,则项目是重叠的:

(
    t1.start_time > t1.end_time,
    t1.start_time < t2.end_time,
    t2.start_time < t1.end_time,
    t2.start_time > t2.end_time,
)

因此完整状态如下:

t1.start_time > t1.end_time and t1.start_time < t2.end_time or \
t1.start_time > t1.end_time and t2.start_time < t1.end_time or \
t1.start_time > t1.end_time and t2.start_time > t2.end_time or \
t1.start_time < t2.end_time and t2.start_time < t1.end_time or \
t1.start_time < t2.end_time and t2.start_time > t2.end_time or \
t2.start_time < t1.end_time and t2.start_time > t2.end_time

在Django queryset中对其进行修饰,因为在执行它之前我们可以检查1个条件(我们可以检查start_time的{​​{1}}是否低于{{1}的t2 }),我们可以将其分为两种情况,并简化两种情况。

end_time

但是平等呢?

是的...我们已经跳过了这一点...我们需要做出3个决定才能将它们包括在内。

  1. 任何范围都可以具有0个长度(开始和结束相等)并且不与任何东西冲突吗?
  2. 任何范围都可以具有24小时长度(开始和结束相等)并与所有内容发生碰撞吗?
  3. 如果一个范围的开始等于另一个范围的结束,它们是否重叠?

第一个条件和第二个条件不能都为是。如果您对所有这些问题的回答均为“否”,则说明您已完成,上面显示的代码将适用于您的情况。如果您对任何问题的回答是“是”,请从下面的列表中进行相应的修改:

  1. 在最终查询集中添加t2,并假设if t2.start_time > t2.end_time: condition = ( Q(start_time__gt=F('end_time')) | Q(start_time__lt=t2.end_time) | Q(end_time__gt=t2.start_time) ) else: condition = ( Q(start_time__gt=F('end_time'), start_time__lt=t2.end_time) | Q(start_time__gt=F('end_time'), end_time__gt=t2.start_time) | Q(start_time__lt=t2.end_time, end_time__gt=t2.start_time) ) .exclude(start_time=F('end_time'))没有任何其他重叠,只需跳过检查即可
  2. 在两个条件中都添加t2,并假设t2.start_time == t2.end_time| Q(start_time=F('end_time')与其他所有元素重叠,并跳过检查
  3. 当内部条件的右侧有t2而不是{时,在两种情况下都用t2.start_time == t2.end_time替换每个__gt,用__gte替换每个__lt {1}}功能。