如何检查relativedelta对象是否为负

时间:2017-11-14 10:52:44

标签: python python-dateutil

我在使用relativedelta对象时遇到一些问题 - 无法确定" delta是否为负值"。我正在尝试的是:

from dateutil.relativedelta import relativedelta
print relativedelta(seconds=-5) > 0

这让我True反直觉。

print relativedelta(seconds=5) > 0

也会返回True。有没有办法检查是否" delta"由relativedata对象表示是否为负?

我目前正在使用单独功能形式的解决方法来检查delta是否为负数,但我预计会有更优雅的解决方案。以下是我使用的代码:

def is_relativedelta_positive(rel_delta):
    is_positive = True
    is_positive &= rel_delta.microseconds >= 0
    is_positive &= rel_delta.seconds >= 0
    is_positive &= rel_delta.minutes >= 0
    is_positive &= rel_delta.hours >= 0
    is_positive &= rel_delta.days >= 0 
    return is_positive

3 个答案:

答案 0 :(得分:2)

relativedelta()个对象没有实现必要的比较方法。在Python 2中,这意味着它们通过类型名称​​进行比较,并且数字总是在任何其他对象之前排序;这使得这些对象大于整数值,无论它们的值如何。在Python 3中,您将获得TypeError

您的解决方案没有考虑绝对正值,relativedelta(years=1, seconds=-5)会将您的日期时间移动几乎一整年前进,所以它很难被称为负三角洲。

您必须比较各个属性(因此yearsmonthsdayshoursminutes,{{1}和seconds)。根据您的使用情况,您可能需要将这些转换为总秒数:

microseconds

然后用它来进行比较:

def total_seconds(rd, _yeardays=365.2425, _monthdays=365.2425/12):
    """approximation of the number of seconds in a relative delta"""
    # year and month durations are averages, taking into account leap years
    total_days = rd.years * _yeardays + (rd.months * _monthdays) + rd.days
    total_hours = total_days * 24 + rd.hours
    total_minutes = total_hours * 60 + rd.minutes
    return total_minutes * 60 + rd.seconds + (rd.microseconds / 1000000)

if total_seconds(relativedelta(seconds=-5)) > 0: 函数产生近似值;相对增量处理闰年和每月正确的天数,因此它们对日期时间对象的实际影响将根据该日期时间值而变化。但是,对于大多数情况,上述情况应该足够好。它完全忽略相对delta(total_seconds()hour绝对组件,它们是单数名称,表示固定值而不是delta。“

答案 1 :(得分:1)

TL; DR relativedelta个对象之间的比较没有清晰直观的定义,因此dateutil中没有实现比较。如果您想比较它们,您需要对订购做出任意选择。

问题

relativedelta之间的比较语义未定义,因为relativedelta个对象本身并不表示固定的时间段。你可以看到this issue on github为什么这是一个问题。

relativedelta目标之间的比较存在两个主要问题。更直接的是relativedelta具有"绝对"组件(单数参数),例如dayhour等。请考虑:

from dateutil.relativedelta import relativedelta
from datetime import datetime

rd1 = relativedelta(day=5, hours=5)
rd2 = relativedelta(hours=8)

for i in range(4, 7):
    dt = datetime(2014, 1, i)
    print((dt + rd1) > (dt + rd2))

# Result:
# True
# False
# False

由于每个relativedelta并不代表固定的时间量,因此比较哪一个更大"并不一定有意义。或者"较小的"比另一个。

另一个问题是即使你把自己限制在"亲属" relativedelta的组件,所有大于week的单位都取决于它们的添加内容,因此:

rd3 = relativedelta(months=1)
rd4 = relativedelta(days=30)

for i in range(1, 4):
    dt = datetime(2015, i, 1)
    print((dt + rd3) > (dt + rd4))

# Result:
# True
# False
# True

可能的比较操作

也就是说,如果你想要一个半随意但一致的定义,你可以有意义地使用一些定义。#34;小于" relativedelta

一个有限的版本就是说"绝对"组件将抛出错误并为" relative"设置固定值。组件:

def rd_to_td(rd):
    for comp in ['year', 'month', 'day', 'hour', 'minute', 'second',
                 'microsecond', 'weekday', 'leapdays']:
        if getattr(rd, comp) is not None:
            raise ValueError('Conversion not supported with component ' + comp)

    YEAR_LEN = 365.25
    MON_LEN = 30

    days = (rd.years or 0) * YEAR_LEN
    days += (rd.months or 0) * MON_LEN
    return timedelta(days=days, hours=rd.hours, minutes=rd.minutes,
                     seconds=rd.seconds, microseconds=rd.microseconds)

最接近普遍比较

上述适用于有限的情况,但您可以定义的最常用的比较方法是简单地将两者都添加到固定日期并比较结果:

from datetime import datetime

def lt_at_dt(rd1, rd2, dt=datetime(1970, 1, 1)):
    return (dt + rd1) < (dt + rd2)

如果你想将它作为排序的关键(而不是成对比较),那么&#34;小于&#34;的相同定义。可用于将relativedelta转换为timedelta(这是固定的时间段):

def rd_to_td_at_dt(rd, dt=datetime(1970, 1, 1)):
    return (dt + rd1) - dt

注意前两个定义是关于relativedelta对象之间比较的更一般操作。要知道其中一个是否定,只需将结果与表示零的relativedelta进行比较,或者通过上述方法之一转换为timedelta并与timedelta(0)进行比较。< / p>

最后,我会注意到,在dateutil的即将发布的2.7.0版本中,relativedelta将定义__abs__GH PR #472),因此您对积极性的原始定义可以减少到abs(rd) == rd。但是,正如Martijn指出的那样,abs(relativedelta(days=20, hours=-1)) != relativedelta(days=20, hours=-1),但根据大多数合理的定义,相对增量总是一个正偏移量。

答案 2 :(得分:0)

一种优雅简洁的方法来检查relativedelta对象是否为负:

from datetime import datetime
from dateutil.relativedelta import relativedelta

def is_negative(rd: relativedelta) -> bool:
''' Check whether a relativedelta object is negative'''
    try:
        datetime.min + rd
        return False
    except OverflowError:
        return True

一些例子:

is_negative(relativedelta(hours=1))
>> False
is_negative(relativedelta(hours=0))
>> False
is_negative(relativedelta(hours=-1))
>> True
is_negative(relativedelta(days=1, hours=-1))
>> False
is_negative(relativedelta(days=-1, hours=1))
>> True