Mercurial > p > roundup > code
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
