Search code examples
pythonpython-dateutil

How to check if relativedelta object is negative


I have some problem with using relativedelta objects - cannot determine if "delta is negative". What I'm trying is:

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

that gives me True which is counter intuitive.

print relativedelta(seconds=5) > 0

also return True. Is there a way to check if "delta" represented by relativedata object is negative?

I'm currently using a workaround in the form of separate function to check if delta is negative but I expected that there is more elegant solution. Here is code I'm using:

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

Solution

  • relativedelta() objects do not implement the necessary comparison methods. In Python 2 that means that they are thus compared by their type name, and numbers are always sorted before any other objects; this makes these objects larger than integer values whatever their values. In Python 3 you'd get a TypeError instead.

    Your work-around doesn't account for an absolute positive value, relativedelta(years=1, seconds=-5) would move your datetime by almost a whole year forward, so it could hardly be named a negative delta.

    You'd have to compare individual attributes instead (so years, months, days, hours, minutes, seconds and microseconds). Depending on your use-case, you may have to convert those to a total number of seconds:

    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)
    

    then use this to do your comparisons:

    if total_seconds(relativedelta(seconds=-5)) > 0:
    

    The total_seconds() function produces an approximation; relative deltas handle leap years and the right number of days per month, so their actual effect on a datetime object will vary depending on that datetime value. However, the above should be good enough for the majority of cases. It does completely ignore the absolute components of the relative delta (hour, year, the singular names, that state a fixed value rather than a delta).