changeset 1896:b3f63a0615db maint-0.6

backport of Date arithmetic fixes from trunk
author Anthony Baxter <anthonybaxter@users.sourceforge.net>
date Tue, 04 Nov 2003 12:39:54 +0000
parents 11010aae4ef0
children db187e5cabc6
files CHANGES.txt roundup/date.py test/test_dates.py
diffstat 3 files changed, 118 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Tue Nov 04 12:39:16 2003 +0000
+++ b/CHANGES.txt	Tue Nov 04 12:39:54 2003 +0000
@@ -8,6 +8,9 @@
 - added script to help migrating queries from pre-0.6 trackers
 - Fixed "documentation" of getnodeids in roundup.hyperdb
 - added flush() to DevNull (sf bug #835365)
+- Date arithmetic was utterly broken, and has been for a long time.
+  Date +/- Interval now works, and Date - Date also works (produces
+  an Interval.
 
 
 2003-09-29 0.6.2
--- a/roundup/date.py	Tue Nov 04 12:39:16 2003 +0000
+++ b/roundup/date.py	Tue Nov 04 12:39:54 2003 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: date.py,v 1.54 2003-04-23 11:48:05 richard Exp $
+# $Id: date.py,v 1.54.2.1 2003-11-04 12:39:54 anthonybaxter Exp $
 
 __doc__ = """
 Date, time and time interval handling.
@@ -204,24 +204,31 @@
             if month > 12: year += 1; month -= 12
 
         # now do the days, now that we know what month we're in
-        mdays = calendar.mdays
-        if month == 2 and calendar.isleap(year): month_days = 29
-        else: month_days = mdays[month]
-        while month < 1 or month > 12 or day < 0 or day > month_days:
+        def get_mdays(year,month):
+            if month == 2 and calendar.isleap(year): return 29
+            else: return calendar.mdays[month]
+            
+        while month < 1 or month > 12 or day < 0 or day > get_mdays(year,month):
             # now to day under/over
-            if day < 0: month -= 1; day += month_days
-            elif day > month_days: month += 1; day -= month_days
+            if day < 0: 
+                # When going backwards, decrement month, then increment days
+                month -= 1
+                day += get_mdays(year,month)
+            elif day > get_mdays(year,month): 
+                # When going forwards, decrement days, then increment month
+                day -= get_mdays(year,month)
+                month += 1
 
             # possibly fix up the month so we're within range
             while month < 1 or month > 12:
-                if month < 1: year -= 1; month += 12
+                if month < 1: year -= 1; month += 12 ; day += 31
                 if month > 12: year += 1; month -= 12
 
-            # re-figure the number of days for this month
-            if month == 2 and calendar.isleap(year): month_days = 29
-            else: month_days = mdays[month]
         return (year, month, day, hour, minute, second, 0, 0, 0)
 
+    def differenceDate(self, other):
+        "Return the difference between this date and another date"
+
     def applyInterval(self, interval):
         ''' Apply the interval to this date
         '''
@@ -246,29 +253,29 @@
 
         assert isinstance(other, Date), 'May only subtract Dates or Intervals'
 
-        # TODO this code will fall over laughing if the dates cross
-        # leap years, phases of the moon, ....
+        return self.dateDelta(other)
+
+    def dateDelta(self, other):
+        """ Produce an Interval of the difference between this date
+            and another date. Only returns days:hours:minutes:seconds.
+        """
+        # Returning intervals larger than a day is almost
+        # impossible - months, years, weeks, are all so imprecise.
         a = calendar.timegm((self.year, self.month, self.day, self.hour,
             self.minute, self.second, 0, 0, 0))
         b = calendar.timegm((other.year, other.month, other.day,
             other.hour, other.minute, other.second, 0, 0, 0))
         diff = a - b
-        if diff < 0:
+        if diff > 0:
             sign = 1
-            diff = -diff
         else:
             sign = -1
+            diff = -diff
         S = diff%60
         M = (diff/60)%60
-        H = (diff/(60*60))%60
-        if H>1: S = 0
-        d = (diff/(24*60*60))%30
-        if d>1: H = S = M = 0
-        m = (diff/(30*24*60*60))%12
-        if m>1: H = S = M = 0
-        y = (diff/(365*24*60*60))
-        if y>1: d = H = S = M = 0
-        return Interval((y, m, d, H, M, S), sign=sign)
+        H = (diff/(60*60))%24
+        d = diff/(24*60*60)
+        return Interval((0, 0, d, H, M, S), sign=sign)
 
     def __cmp__(self, other):
         """Compare this date to another date."""
--- a/test/test_dates.py	Tue Nov 04 12:39:16 2003 +0000
+++ b/test/test_dates.py	Tue Nov 04 12:39:54 2003 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: test_dates.py,v 1.24 2003-04-22 20:53:54 kedder Exp $ 
+# $Id: test_dates.py,v 1.24.2.1 2003-11-04 12:39:54 anthonybaxter Exp $ 
 
 import unittest, time
 
@@ -173,9 +173,89 @@
         now = Date('.')
         now.hour = now.minute = now.second = 0
         then = now + Interval('2d')
-        ae(str(Interval(str(then))), '+ 2d')
+        ae((Interval(str(then))), Interval('- 2d'))
+        then = now - Interval('2d')
+        ae(Interval(str(then)), Interval('+ 2d'))
+
+    def testIntervalAddMonthBoundary(self):
+        # force the transition over a month boundary
+        now = Date('2003-10-30.00:00:00')
+        then = now + Interval('2d')
+        self.assertEqual(str(then), '2003-11-01.00:00:00')
+        now = Date('2004-02-28.00:00:00')
+        then = now + Interval('1d')
+        self.assertEqual(str(then), '2004-02-29.00:00:00')
+        now = Date('2003-02-28.00:00:00')
+        then = now + Interval('1d')
+        self.assertEqual(str(then), '2003-03-01.00:00:00')
+        now = Date('2003-01-01.00:00:00')
+        then = now + Interval('59d')
+        self.assertEqual(str(then), '2003-03-01.00:00:00')
+        now = Date('2004-01-01.00:00:00')
+        then = now + Interval('59d')
+        self.assertEqual(str(then), '2004-02-29.00:00:00')
+
+    def testIntervalSubtractMonthBoundary(self):
+        # force the transition over a month boundary
+        now = Date('2003-11-01.00:00:00')
         then = now - Interval('2d')
-        ae(str(Interval(str(then))), '- 2d')
+        self.assertEqual(str(then), '2003-10-30.00:00:00')
+        now = Date('2004-02-29.00:00:00')
+        then = now - Interval('1d')
+        self.assertEqual(str(then), '2004-02-28.00:00:00')
+        now = Date('2003-03-01.00:00:00')
+        then = now - Interval('1d')
+        self.assertEqual(str(then), '2003-02-28.00:00:00')
+        now = Date('2003-03-01.00:00:00')
+        then = now - Interval('59d')
+        self.assertEqual(str(then), '2003-01-01.00:00:00')
+        now = Date('2004-02-29.00:00:00')
+        then = now - Interval('59d')
+        self.assertEqual(str(then), '2004-01-01.00:00:00')
+
+    def testIntervalAddYearBoundary(self):
+        # force the transition over a year boundary
+        now = Date('2003-12-30.00:00:00')
+        then = now + Interval('2d')
+        self.assertEqual(str(then), '2004-01-01.00:00:00')
+        now = Date('2003-01-01.00:00:00')
+        then = now + Interval('365d')
+        self.assertEqual(str(then), '2004-01-01.00:00:00')
+        now = Date('2004-01-01.00:00:00')
+        then = now + Interval('366d')
+        self.assertEqual(str(then), '2005-01-01.00:00:00')
+
+    def testIntervalSubtractYearBoundary(self):
+        # force the transition over a year boundary
+        now = Date('2003-01-01.00:00:00')
+        then = now - Interval('2d')
+        self.assertEqual(str(then), '2002-12-30.00:00:00')
+        now = Date('2004-02-01.00:00:00')
+        then = now - Interval('365d')
+        self.assertEqual(str(then), '2003-02-01.00:00:00')
+        now = Date('2005-02-01.00:00:00')
+        then = now - Interval('365d')
+        self.assertEqual(str(then), '2004-02-02.00:00:00')
+
+    def testDateSubtract(self):
+        # These are thoroughly broken right now.
+        i = Date('2003-03-15.00:00:00') - Date('2003-03-10.00:00:00')
+        self.assertEqual(i, Interval('5d'))
+        i = Date('2003-02-01.00:00:00') - Date('2003-03-01.00:00:00')
+        self.assertEqual(i, Interval('-28d'))
+        i = Date('2003-03-01.00:00:00') - Date('2003-02-01.00:00:00')
+        self.assertEqual(i, Interval('28d'))
+        i = Date('2003-03-03.00:00:00') - Date('2003-02-01.00:00:00')
+        self.assertEqual(i, Interval('30d'))
+        i = Date('2003-03-03.00:00:00') - Date('2002-02-01.00:00:00')
+        self.assertEqual(i, Interval('395d'))
+        i = Date('2003-03-03.00:00:00') - Date('2003-04-01.00:00:00')
+        self.assertEqual(i, Interval('-29d'))
+        i = Date('2003-03-01.00:00:00') - Date('2003-02-01.00:00:00')
+        self.assertEqual(i, Interval('28d'))
+        # force the transition over a year boundary
+        i = Date('2003-01-01.00:00:00') - Date('2002-01-01.00:00:00')
+        self.assertEqual(i, Interval('365d'))
 
     def testIntervalAdd(self):
         ae = self.assertEqual
@@ -257,7 +337,7 @@
         ae(str(Interval('-2m 3w', add_granularity=1)), '- 2m 14d')
 
 def suite():
-   return unittest.makeSuite(DateTestCase, 'test')
+    return unittest.makeSuite(DateTestCase, 'test')
 
 
 # vim: set filetype=python ts=4 sw=4 et si

Roundup Issue Tracker: http://roundup-tracker.org/