2

请考虑这个问题:

我有一个过去的日期,我从那里开始添加句点。当添加这些期间导致日期大于今天时,我想停下来检查最后一个日期是什么。

这是计算会员借记日期的功能。一个成员加入,比如说,2007-01-31。他每个月都会被扣款。假设今天是 2013-03-29(实际上是 atm)。所以我需要从 2007 年 1 月 31 日开始计算月份,当我超过今天的日期时,我需要停止。然后我可以看到下一个借记日期是 2013-03-31。

我正在使用 dateutil 库来实现这一点,在 while 循环中添加 relativedelta,直到我超过当前日期。(我知道这可能不是最好的方法,但我对 Python 还是很陌生,这是一个概念验证)。问题是当我在 2007-01-31 中添加一个月时,下一个日期是 2007-02-28,这是正确的。但是下一次迭代的日期是 2007-03-28,因为 dateutil 不将 28 日识别为该月的最后一天,以保持其完整并迭代到 3 月的最后一天。当然,这是一个完全有效的实现。然后我尝试了 dateutils rrule 对象,但它具有相同的原理。它输出日期列表,但它只是跳过没有足够天数的月份。

period = rrule(MONTHLY, interval=1, dtstart=datetime.date(2012, 5, 31), until=datetime.date(2013, 3, 29))
print(list(period))

然后我想到了另一种方法:

如果我可以计算 2007 年 1 月 31 日到 2013 年 3 月 29 日期间的时间段数,我可以将这些时间段数添加到开始日期,并且 dateutil 将返回正确的日期。
问题是这个时期并不总是一个月。例如,也可以是四个星期、一个季度或一年。

我找不到一种方法来获取一个 relativedelta 并将它与另一个 relativedelta 相除,以获得后者进入第一个的次数。

如果有人能指出我正确的方向,我将不胜感激。例如,是否有一个库可以做到这一点(相互划分时间跨度,并在给定的时间块内输出结果,如几个月或几周)?是否有一个 datediff 函数可以接受一个句点作为输入(例如,我知道在 vbscript 中,您可以在任何您想要的时间段内获得两个日期之间的差异,无论是几周、几个月、几天等等)。可能有完全不同的解决方案吗?

为了完整起见,我将包含代码,但我认为文本已经解释了这一切:

def calculate(self, testdate=datetime.date.today()):
    self._testdate = testdate


    while self.next < self._testdate:
        self.next += self._ddinterval
    self.previous = self.next - self._ddinterval
    return self.next

谢谢,

埃里克

编辑:我现在有一个解决方案可以做它应该做的,但它几乎不是 Pythonic、优雅或快速的。所以问题仍然是一样的,如果有人能提出更好的解决方案,请这样做。这是我想出的:

def calculate(self, testdate=datetime.date.today()):
    self._testdate = testdate

    start = self.next
    count = 0
    while self.next < self._testdate:
        count += 1
        self.next = start + (count * self._ddinterval)
    self.previous = self.next - self._ddinterval
    return self.next
4

2 回答 2

2

在添加周期作为下一个循环的起点后,不要使用新值,而是创建一个不断增加的增量

today = datetime.date.today()
start = datetime.date(2007, 1, 31)
period = relativedelta(months=1)

delta = period
while start + delta < today:
    delta += period

next = start + delta

这导致:

>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> today = datetime.date.today()
>>> start = datetime.date(2007, 1, 31)
>>> period = relativedelta(months=1)
>>> delta = period
>>> while start + delta < today:
...     delta += period
... 
>>> delta
relativedelta(years=+6, months=+3)
>>> start + delta
datetime.date(2013, 4, 30)

这适用于任何可变的期间长度,包括季度和年份。对于以精确的几周或几天为单位的时间段,您也可以使用一个timedelta()对象,但这是一个通用的解决方案。

当使用可变宽度的时间段(例如月份和季度)时,您不能“划分”时间段。对于以整天为单位测量的时间段,您可以简单地将两个日期之间的差异转换为天数(来自 timedelta),然后获取模数:

period = datetime.timedelta(days=28)  # 4 weeks
delta = today - start
remainder = delta.days % period.days
end = today + datetime.timedelta(days=remainder)

这使:

>>> period = datetime.timedelta(days=28)  # 4 weeks
>>> delta = today - start
>>> remainder = delta.days % period.days
>>> today + datetime.timedelta(days=remainder)
datetime.date(2013, 4, 17)
于 2013-04-03T16:55:08.180 回答
0

如果您的增量相对于基准时间是可变的(即,1 个月可能意味着 28-31 天中的任何一天),那么您就会陷入循环。

但是,如果 delta 是一个恒定的天数,您可以通过转换为整数并进行模运算来回避迭代。

于 2013-04-03T17:30:46.800 回答