【问题标题】:Divide two timespans by eachother in Python/dateutil在 Python/dateutil 中将两个时间跨度除以彼此
【发布时间】:2013-03-20 07:33:39
【问题描述】:

请考虑这个问题:

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

这是一种计算会员借记日期的功能。一个成员加入,比如说,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 函数接受一个句点作为输入(我知道例如in vbscript you can get the difference between two dates in whatever period you want,无论是几周、几个月、几天,等等)。可能有完全不同的解决方案吗?

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

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

【问题讨论】:

  • 我在使用另一个日期处理模块时遇到了类似的问题。对于以月为单位的时间段,我的解决方案只是在将间隔添加到开始日期的间隔后比较结果的月份日期,并在必要时进行调整。
  • 感谢 Martineau,这会起作用,但只能持续几个月,例如,不能增加 4 周的时间。我发现了很多只添加几个月的例子。
  • IMO 添加 4 周(或固定跨度天数的任何时间)很容易。在 4 周内,您的时间总是会增加 28 天。所以即使像old_datetime + timedelta(days=28) 这样的东西也可以。对于月份和季度来说,这并不容易,因为不同的月份有不同的天数。
  • 感谢您的意见,RedBaron。我考虑过做出这种区分。但是, _ddinterval 属性是一个 relativedelta 对象,我找不到在该期间检查这样一个对象的方法。 (无法确定它是否包含周、天、年或组合)。如果我可以将 relativedelta 对象转换为,说“周”,这会使事情变得容易得多。
  • 我对@9​​87654326@ 没有太多经验,因此无法对此发表评论。你怎么设置_ddintervalBTW?

标签: python python-dateutil


【解决方案1】:

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

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)

【讨论】:

  • 在您提到其他时期的编辑之前,我已经添加了反对票。如问题所述,不仅可以使用几个月。
  • 感谢您的回答。我认为这是最优雅的方式。我将不得不设计一种方法将“固定”时期与“可变”时期分开,但我认为这是可行的。
  • 这是获得最近下个月日期的一个很好的解决方案,尤其是对于重复的每月任务。
【解决方案2】:

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

如果增量是一个常数日计数,则可以通过转换为整数并进行模数操作来播放迭代。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-27
    • 2021-05-22
    • 2020-11-03
    • 2017-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多