TL;DR There is not a clear and intuitive definition of comparison between relativedelta objects, so comparison is not implemented in dateutil. If you want to compare them, you'll need to make an arbitrary choice about the ordering.
The problem
The semantics of comparisons between relativedelta are undefined because relativedelta objects themselves don't represent a fixed period of time. You can see this issue on github as to why this is a problem.
There are two major problems with comparisons between relativedelta objeccts. The more straightforward one is that relativedelta has "absolute" components (the singular arguments) such as day, hour, etc. So consider:
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
Since each relativedelta does not represent a fixed amount of time, it's not necessarily meaningful to compare which one is "bigger" or "smaller" than the other.
The other problem is that even if you restrict yourself to the "relative" components of the relativedelta, all units larger than week depend on what they are being added to, so:
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
Possible comparison operations
That said, there are a few possible definitions that you can meaningfully use if you want a semi-arbitrary but consistent definition of "less than" for relativedelta.
One somewhat limited version of this is to say that "absolute" components will throw an error and to set a fixed value for the "relative" components:
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)
Closest to universal comparison
The above works for limited cases, but probably most universal comparison method you can define is to simply add both to a fixed date and compare the results:
from datetime import datetime
def lt_at_dt(rd1, rd2, dt=datetime(1970, 1, 1)):
return (dt + rd1) < (dt + rd2)
If you want this as a key for sorting (rather than for pairwise comparisons), this same definition of "less than" can be used to convert relativedelta to timedelta (which is a fixed period of time):
def rd_to_td_at_dt(rd, dt=datetime(1970, 1, 1)):
return (dt + rd1) - dt
Note The previous two definitions are about the more general operation of comparison between relativedelta objects. To know if one of these is negative, just compare the result to a relativedelta representing zero, or convert to timedelta by one of the above methods and compare to timedelta(0).
Finally, I will note that in the forthcoming 2.7.0 release of dateutil, relativedelta will define __abs__ (GH PR #472), so your original definition of positivity can be reduced to abs(rd) == rd. However, as Martijn points out, abs(relativedelta(days=20, hours=-1)) != relativedelta(days=20, hours=-1), but by most reasonable definitions, that relative delta is always a positive offset.