From 6ad3a48d64534340a643bd40ad65c7d7b4d13458 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sun, 17 Feb 2008 17:53:43 +0000 Subject: [PATCH] 2008-02-17 Douglas S. Blank * src/gen/lib/date.py (Date.__sub__): fixed some date math (#1649) * src/gen/lib/test/date_test.py: added some unit tests for date math svn: r10043 --- ChangeLog | 4 +++ src/gen/lib/date.py | 54 +++++++++++++++++++++++++------ src/gen/lib/test/date_test.py | 60 +++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index d38a1a52c..fc29b4192 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2008-02-17 Douglas S. Blank + * src/gen/lib/date.py (Date.__sub__): fixed some date math (#1649) + * src/gen/lib/test/date_test.py: added some unit tests for date math + 2008-02-15 Gary Burton * src/DataViews/RelationView.py: * src/Editors/_EditFamily.py: prevent right mouse button causing crash. diff --git a/src/gen/lib/date.py b/src/gen/lib/date.py index 5562f27e5..f07512501 100644 --- a/src/gen/lib/date.py +++ b/src/gen/lib/date.py @@ -246,13 +246,20 @@ class Date: """ Date artithmetic: Date() - years, Date - (y,m,d), or Date() - Date() """ - if type(other) == int: + if type(other) == int: # Date - value -> Date return self.copy_offset_ymd(-other) - elif type(other) == type(self): # date + elif type(other) in [tuple, list]: # Date - (y, m, d) -> Date + return self.copy_offset_ymd(*map(lambda x: -x, other)) + elif type(other) == type(self): # Date1 - Date2 -> tuple + # We should make sure that Date2 + tuple -> Date1 and + # Date1 - tuple -> Date2 d1 = map(lambda i: i or 1, self.get_ymd()) d2 = map(lambda i: i or 1, other.get_ymd()) + date1 = self + date2 = other if d1 < d2: d1, d2 = d2, d1 + date1, date2 = date2, date1 # d1 - d2 (1998, 12, 32) - (1982, 12, 15) = # days: if d2[2] > d1[2]: @@ -269,18 +276,39 @@ class Date: days = d1[2] - d2[2] months = d1[1] - d2[1] years = d1[0] - d2[0] - if days > 31: + if days > 31: months += days / 31 days = days % 31 if months > 12: years += months / 12 months = months % 12 - return (years, months, days) - elif type(other) in [tuple, list]: - return self.copy_offset_ymd(*map(lambda x: -x, other)) + # estimate: (years, months, days) + # Check transitivity: + eDate = date1 - (years, months, days) + if eDate < date2: # too small + diff = 0 + while eDate < date2 and diff < 60: + diff += 1 + eDate = eDate + (0, 0, diff) + if diff == 60: + return (0, 0, 0) + return (years, months, days - diff) + elif eDate > date2: + diff = 0 + while eDate > date2 and diff > -60: + diff -= 1 + eDate = eDate - (0, 0, abs(diff)) + if diff == -60: + return (0, 0, 0) + return (years, months, days + diff) + else: + return (years, months, days) else: raise AttributeError, "unknown date sub type: %s " % type(other) + def __eq__(self, other): + return self.sortval == other.sortval + def __lt__(self, other): return self.sortval < other.sortval @@ -629,12 +657,18 @@ class Date: if dv[Date._POS_MON]: dv[Date._POS_MON] += month elif month: - dv[Date._POS_MON] = month + if month < 0: + dv[Date._POS_MON] = 1 + month + else: + dv[Date._POS_MON] = month # Fix if month out of bounds: if month != 0: # only check if changed - if dv[Date._POS_MON] <= 0: # subtraction - dv[Date._POS_YR] -= int(-dv[Date._POS_MON] / 12) - dv[Date._POS_MON] = 13 - dv[Date._POS_MON] % 12 + if dv[Date._POS_MON] == 0: # subtraction + dv[Date._POS_MON] = 12 + dv[Date._POS_YR] -= 1 + elif dv[Date._POS_MON] < 0: # subtraction + dv[Date._POS_YR] -= int((-dv[Date._POS_MON]) / 12) + 1 + dv[Date._POS_MON] = (dv[Date._POS_MON] % 12) elif dv[Date._POS_MON] > 12 or dv[Date._POS_MON] < 1: dv[Date._POS_YR] += int(dv[Date._POS_MON] / 12) dv[Date._POS_MON] = dv[Date._POS_MON] % 12 diff --git a/src/gen/lib/test/date_test.py b/src/gen/lib/test/date_test.py index e7bc9de8c..61a694864 100644 --- a/src/gen/lib/test/date_test.py +++ b/src/gen/lib/test/date_test.py @@ -135,5 +135,65 @@ def suite(): count += 1 return suite +def assert_func(exp1, exp2): + Date = date.Date + e1 = eval(exp1) + e2 = eval(exp2) + assert e1 == e2, "%s should be %s but was %s" % (exp1, e2, e1) + +class Assert(unittest.TestCase): + def __init__(self, method_name, part, exp1, exp2): + self.__dict__[method_name + ("-%d" % part)] = \ + lambda: assert_func(exp1, exp2) + unittest.TestCase.__init__(self, method_name + ("-%d" % part)) + +def suite2(): + """ interface to automated test runner test/regrtest.py """ + Config.set(Config.DATE_BEFORE_RANGE, 9999) + Config.set(Config.DATE_AFTER_RANGE, 9999) + Config.set(Config.DATE_ABOUT_RANGE, 10) + tests = [ + # Date +/- int/tuple -> Date + ("Date(2008, 1, 1) - 1", "Date(2007, 1, 1)"), + ("Date(2008, 1, 1) + 1", "Date(2009, 1, 1)"), + ("Date(2008, 1, 1) - (0,0,1)", "Date(2007, 12, 31)"), + ("Date(2008, 1, 1) - (0,0,2)", "Date(2007, 12, 30)"), + ("Date(2008) - (0,0,1)", "Date(2007, 12, 31)"), + ("Date(2008) - 1", "Date(2007, 1, 1)"), + ("Date(2008, 12, 31) + (0, 0, 1)", "Date(2009, 1, 1)"), + ("Date(2000,1,1) - (0,11,0)", "Date(1999,02,01)"), + ("Date(2000,1,1) - (0,1,0)", "Date(1999, 12, 1)"), + ("Date(2008, 1, 1) + (0, 0, 32)", "Date(2008, 02, 02)"), + ("Date(2008, 2, 1) + (0, 0, 32)", "Date(2008, 03, 04)"), + ("Date(2000) - (0, 1, 0)", "Date(1999, 12, 1)"), + ("Date(2000) + (0, 1, 0)", "Date(2000, 1, 0)"), # Ok? + ("Date(2000, 1, 1) - (0, 1, 0)", "Date(1999, 12, 1)"), + ("Date(2000, 1, 1) - 1", "Date(1999, 1, 1)"), + ("Date(2000) - 1", "Date(1999)"), + ("Date(2000) + 1", "Date(2001)"), + +#My great great grandfather died on 1876-05-07. +#He died at an age of 65 years, 5 month 17 days according to the books: +#> Date(1876,5,7)-(65,5,17) +#1811-12-21 +#> Date(1876,5,7)-Date(1811,12,21) +#(64, 4, 17) +#But his correct birth date is 1810-11-20: +#("Date(1876,5,7)-Date(1810,11,20)", "(65, 5, 18)") + ("Date(1876,5,7) - Date(1876,5,1)", "(0, 0, 6)"), + ("Date(1876,5,7) - Date(1876,4,30)", "(0, 0, 7)"), + ("Date(2000,1,1) - Date(1999,2,1)", "(0, 11, 0)"), + ("Date(2000,1,1) - Date(1999,12,1)", "(0, 1, 0)"), + # Date +/- Date -> tuple + ("Date(2007, 12, 23) - Date(1963, 12, 4)", "(44, 0, 19)"), + ] + suite = unittest.TestSuite() + count = 1 + for (exp1, exp2) in tests: + suite.addTest(Assert('test_assert%04d' % count, 1, exp1, exp2)) + count += 1 + return suite + if __name__ == "__main__": unittest.TextTestRunner().run(suite()) + unittest.TextTestRunner().run(suite2())