From 27eb4b1b7a0b21cb5bf36094bd56c34ee2a73112 Mon Sep 17 00:00:00 2001 From: Gerhard Weis Date: Thu, 24 Jan 2013 15:55:42 +1000 Subject: - raise Exception for unsupported operations with duration and date/datetime objects - store year and month as Decimal in Duration object. - added test cases to support changes closes #2 --- src/isodate/duration.py | 6 ++++++ src/isodate/isoduration.py | 7 ++++++- src/isodate/tests/test_duration.py | 24 +++++++++++++++++------- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/isodate/duration.py b/src/isodate/duration.py index fa501cd..0d5e0fa 100644 --- a/src/isodate/duration.py +++ b/src/isodate/duration.py @@ -140,6 +140,8 @@ class Duration(object): newduration.tdelta = self.tdelta + other.tdelta return newduration if isinstance(other, (date, datetime)): + if (not( float(self.years).is_integer() and float(self.months).is_integer())): + raise ValueError('fractional years or months not supported for date calculations') newmonth = other.month + self.months carry, newmonth = fquotmod(newmonth, 1, 13) newyear = other.year + self.years + carry @@ -162,6 +164,8 @@ class Duration(object): newduration.tdelta = self.tdelta + other return newduration if isinstance(other, (date, datetime)): + if (not( float(self.years).is_integer() and float(self.months).is_integer())): + raise ValueError('fractional years or months not supported for date calculations') newmonth = other.month + self.months carry, newmonth = fquotmod(newmonth, 1, 13) newyear = other.year + self.years + carry @@ -199,6 +203,8 @@ class Duration(object): ''' #print '__rsub__:', self, other if isinstance(other, (date, datetime)): + if (not( float(self.years).is_integer() and float(self.months).is_integer())): + raise ValueError('fractional years or months not supported for date calculations') newmonth = other.month - self.months carry, newmonth = fquotmod(newmonth, 1, 13) newyear = other.year - self.years + carry diff --git a/src/isodate/isoduration.py b/src/isodate/isoduration.py index 3554b1f..97affdc 100644 --- a/src/isodate/isoduration.py +++ b/src/isodate/isoduration.py @@ -31,6 +31,7 @@ It also provides a wrapper to strftime. This wrapper makes it easier to format timedelta or Duration instances as ISO conforming strings. ''' from datetime import timedelta +from decimal import Decimal import re from isodate.duration import Duration @@ -103,7 +104,11 @@ def parse_duration(datestring): if val is None: groups[key] = "0n" #print groups[key] - groups[key] = float(groups[key][:-1].replace(',', '.')) + if key in ('years', 'months'): + groups[key] = Decimal(groups[key][:-1].replace(',', '.')) + else: + # these values are passed into a timedelta object, which works with floats. + groups[key] = float(groups[key][:-1].replace(',', '.')) if groups["years"] == 0 and groups["months"] == 0: ret = timedelta(days=groups["days"], hours=groups["hours"], minutes=groups["minutes"], seconds=groups["seconds"], diff --git a/src/isodate/tests/test_duration.py b/src/isodate/tests/test_duration.py index d8b4cf2..a66652e 100644 --- a/src/isodate/tests/test_duration.py +++ b/src/isodate/tests/test_duration.py @@ -192,12 +192,18 @@ DATE_CALC_TEST_CASES = ( (Duration(years=1, months=1, weeks=5), date(2000, 1, 30), date(2001, 4, 4)), - (Duration(years=1, months=1, weeks=5), - 'raise exception', - None), - ('raise exception', - Duration(years=1, months=1, weeks=5), + (parse_duration("P1Y1M5W"), + date(2000, 1, 30), + date(2001, 4, 4)), + (parse_duration("P0.5Y"), + date(2000, 1, 30), None), + (Duration(years=1, months=1, hours=3), + datetime(2000, 1, 30, 12, 15, 00), + datetime(2001, 2, 28, 15, 15, 00)), + (parse_duration("P1Y1MT3H"), + datetime(2000, 1, 30, 12, 15, 00), + datetime(2001, 2, 28, 15, 15, 00)), (Duration(years=1, months=2), timedelta(days=1), Duration(years=1, months=2, days=1)), @@ -239,7 +245,11 @@ class DurationTest(unittest.TestCase): date(2000, 1, 1)) self.assertRaises(TypeError, operator.sub, 'raise exc', Duration(years=1)) - + self.assertRaises(TypeError, operator.add, Duration(years=1, months=1, weeks=5), + 'raise exception') + self.assertRaises(TypeError, operator.add, 'raise exception', + Duration(years=1, months=1, weeks=5)) + def test_parseerror(self): ''' Test for unparseable duration string. @@ -451,7 +461,7 @@ def create_datecalctestcase(start, duration, expectation): Test operator +. ''' if expectation is None: - self.assertRaises(TypeError, operator.add, start, duration) + self.assertRaises(ValueError, operator.add, start, duration) else: self.assertEqual(start + duration, expectation) -- cgit v1.2.1