python中有效的日期范围重叠计算?

74 python date date-range

我有两个日期范围,其中每个范围由开始和结束日期(显然,datetime.date()实例)确定.这两个范围可以重叠或不重叠.我需要重叠的天数.当然,我可以预先填充两个集合,所有日期都在两个范围内,并且执行一个集合交集,但这可能是低效的...除了使用覆盖所有情况的长if-elif部分的另一个解决方案之外,还有更好的方法吗?

Ray*_*ger 158

  • 确定两个开始日期中的最新日期和两个结束日期中最早的日期.
  • 通过减去它们来计算timedelta.
  • 如果delta是正数,那就是重叠的天数.

这是一个示例计算:

>>> from datetime import datetime
>>> from collections import namedtuple
>>> Range = namedtuple('Range', ['start', 'end'])

>>> r1 = Range(start=datetime(2012, 1, 15), end=datetime(2012, 5, 10))
>>> r2 = Range(start=datetime(2012, 3, 20), end=datetime(2012, 9, 15))
>>> latest_start = max(r1.start, r2.start)
>>> earliest_end = min(r1.end, r2.end)
>>> delta = (earliest_end - latest_start).days + 1
>>> overlap = max(0, delta)
>>> overlap
52
Run Code Online (Sandbox Code Playgroud)

  • @darkless实际上,它返回2****正确**.尝试这些输入``r1 = Range(start = datetime(2012,1,1),end = datetime(2012,1,4)); r2 =范围(start = datetime(2012,1,2),end = datetime(2012,1,3))``.我认为你在重叠计算中错过了``+ 1``(必要因为两端的间隔是关闭的). (2认同)
  • 如果您将日期时间对象与时间一起使用,则可以编写 .total_seconds() 而不是 .days。 (2认同)

Joh*_*hin 8

函数调用比算术运算更昂贵.

这样做的最快方法是2次减法和1分钟():

min(r1.end - r2.start, r2.end - r1.start).days + 1
Run Code Online (Sandbox Code Playgroud)

与下一个需要1减法,1分钟()和最大()的最佳相比:

(min(r1.end, r2.end) - max(r1.start, r2.start)).days + 1
Run Code Online (Sandbox Code Playgroud)

当然,对于这两个表达式,您仍需要检查正面重叠.


小智 6

我实现了一个TimeRange类,如下所示。

get_overlapped_range首先通过简单的条件取反所有非重叠选项,然后通过考虑所有可能的选项来计算重叠范围。

要获得天数,您需要获取从get_overlapped_range返回的TimeRange值,然后将持续时间除以60 * 60 * 24。

class TimeRange(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.duration = self.end - self.start

    def is_overlapped(self, time_range):
        if max(self.start, time_range.start) < min(self.end, time_range.end):
            return True
        else:
            return False

    def get_overlapped_range(self, time_range):
        if not self.is_overlapped(time_range):
            return

        if time_range.start >= self.start:
            if self.end >= time_range.end:
                return TimeRange(time_range.start, time_range.end)
            else:
                return TimeRange(time_range.start, self.end)
        elif time_range.start < self.start:
            if time_range.end >= self.end:
                return TimeRange(self.start, self.end)
            else:
                return TimeRange(self.start, time_range.end)

    def __repr__(self):
        return '{0} ------> {1}'.format(*[time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(d))
                                          for d in [self.start, self.end]])
Run Code Online (Sandbox Code Playgroud)


小智 5

您可以使用 datetimerange 包:https : //pypi.org/project/DateTimeRange/

from datetimerange import DateTimeRange
time_range1 = DateTimeRange("2015-01-01T00:00:00+0900", "2015-01-04T00:20:00+0900") 
time_range2 = DateTimeRange("2015-01-01T00:00:10+0900", "2015-01-04T00:20:00+0900")
tem3 = time_range1.intersection(time_range2)
if tem3.NOT_A_TIME_STR == 'NaT':  # No overlap
    S_Time = 0
else: # Output the overlap seconds
    S_Time = tem3.timedelta.total_seconds()
Run Code Online (Sandbox Code Playgroud)

DateTimeRange() 中的 "2015-01-01T00:00:00+0900" 也可以是日期时间格式,例如 Timestamp('2017-08-30 20:36:25')。

  • 谢谢,刚刚查看了“DateTimeRange”包的文档,似乎它们支持“is_intersection”,它本机返回一个布尔值(True 或 False),具体取决于两个日期范围之间是否存在交集。因此,对于您的示例:如果它们相交,“time_range1.is_intersection(time_range2)”将返回“True”,否则返回“False” (2认同)