Repository: dateutil/dateutil (Python)

Issue:
wrong implementation of adding relativedelta's
Excerpt of `dateutil.relativedelta` for Python3.5, `dateutil==2.6.0`:

    def __add__(self, other):
        if isinstance(other, relativedelta):
            return self.__class__(years=other.years + self.years,
                                 ...
                                 hour=other.hour or self.hour,
                                 minute=other.minute or self.minute,
                                 second=other.second or self.second,
                                 ....

The problem is pretty simple: while for the plural version this might be correct, this is wrongly implemented for absolute minute/hour/second etc.

The problem is when `other.minute` is 0. That is, forcing the minute to be 0 cannot work. The default value is None for a reason :).

So instead of:

    minute=other.minute or self.minute,

it should be:
  
    minute=other.minute if other.minute is None else self.minute,




Hints:

Patch:
diff --git a/dateutil/relativedelta.py b/dateutil/relativedelta.py
--- a/dateutil/relativedelta.py
+++ b/dateutil/relativedelta.py
@@ -311,14 +311,22 @@ def __add__(self, other):
                                  microseconds=(other.microseconds +
                                                self.microseconds),
                                  leapdays=other.leapdays or self.leapdays,
-                                 year=other.year or self.year,
-                                 month=other.month or self.month,
-                                 day=other.day or self.day,
-                                 weekday=other.weekday or self.weekday,
-                                 hour=other.hour or self.hour,
-                                 minute=other.minute or self.minute,
-                                 second=other.second or self.second,
-                                 microsecond=(other.microsecond or
+                                 year=(other.year if other.year is not None
+                                       else self.year),
+                                 month=(other.month if other.month is not None
+                                        else self.month),
+                                 day=(other.day if other.day is not None
+                                      else self.day),
+                                 weekday=(other.weekday if other.weekday is not None
+                                          else self.weekday),
+                                 hour=(other.hour if other.hour is not None
+                                       else self.hour),
+                                 minute=(other.minute if other.minute is not None
+                                         else self.minute),
+                                 second=(other.second if other.second is not None
+                                         else self.second),
+                                 microsecond=(other.microsecond if other.microsecond
+                                              is not None else
                                               self.microsecond))
         if isinstance(other, datetime.timedelta):
             return self.__class__(years=self.years,
@@ -396,14 +404,23 @@ def __sub__(self, other):
                              seconds=self.seconds - other.seconds,
                              microseconds=self.microseconds - other.microseconds,
                              leapdays=self.leapdays or other.leapdays,
-                             year=self.year or other.year,
-                             month=self.month or other.month,
-                             day=self.day or other.day,
-                             weekday=self.weekday or other.weekday,
-                             hour=self.hour or other.hour,
-                             minute=self.minute or other.minute,
-                             second=self.second or other.second,
-                             microsecond=self.microsecond or other.microsecond)
+                             year=(self.year if self.year is not None
+                                   else other.year),
+                             month=(self.month if self.month is not None else
+                                    other.month),
+                             day=(self.day if self.day is not None else
+                                  other.day),
+                             weekday=(self.weekday if self.weekday is not None else
+                                      other.weekday),
+                             hour=(self.hour if self.hour is not None else
+                                   other.hour),
+                             minute=(self.minute if self.minute is not None else
+                                     other.minute),
+                             second=(self.second if self.second is not None else
+                                     other.second),
+                             microsecond=(self.microsecond if self.microsecond
+                                          is not None else
+                                          other.microsecond))
 
     def __neg__(self):
         return self.__class__(years=-self.years,
@@ -529,3 +546,5 @@ def _sign(x):
     return int(copysign(1, x))
 
 # vim:ts=4:sw=4:et
+
+


Test patch:
diff --git a/dateutil/test/test_relativedelta.py b/dateutil/test/test_relativedelta.py
--- a/dateutil/test/test_relativedelta.py
+++ b/dateutil/test/test_relativedelta.py
@@ -180,6 +180,12 @@ def testAddition(self):
                          relativedelta(years=1, months=2, days=13, hours=4,
                                        minutes=5, microseconds=6))
 
+    def testAbsoluteAddition(self):
+        self.assertEqual(relativedelta() + relativedelta(day=0, hour=0),
+                         relativedelta(day=0, hour=0))
+        self.assertEqual(relativedelta(day=0, hour=0) + relativedelta(),
+                         relativedelta(day=0, hour=0))
+ 
     def testAdditionToDatetime(self):
         self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=1),
                          datetime(2000, 1, 2))