diff options
author | Gerhard Weis <gweis@gmx.at> | 2009-02-09 10:25:14 +1000 |
---|---|---|
committer | Gerhard Weis <gweis@gmx.at> | 2009-02-09 10:25:14 +1000 |
commit | 40a99905da4df23a4d68753258c0354abd8ab932 (patch) | |
tree | a826fd5e96346c962319cffb9fd8caa40a6cb353 /src/isodate/isoduration.py | |
parent | 44c432ba38078eff35b1ab7cde07fc205e1564d3 (diff) | |
download | isodate-40a99905da4df23a4d68753258c0354abd8ab932.tar.gz |
* added ISO 8601 formating methods
* still problems with negative durations
* refactored Duration class to separate module
* allow access to wrapped timedelta attributes directly on Duration.
* refactored tzinfo parsing and formating into separate module
* updated test cases for new functionality
Diffstat (limited to 'src/isodate/isoduration.py')
-rw-r--r-- | src/isodate/isoduration.py | 225 |
1 files changed, 20 insertions, 205 deletions
diff --git a/src/isodate/isoduration.py b/src/isodate/isoduration.py index ebbd7eb..5002e51 100644 --- a/src/isodate/isoduration.py +++ b/src/isodate/isoduration.py @@ -27,14 +27,16 @@ ''' This module provides an ISO 8601:2004 duration parser. -It also defines a class Duration, which allows to define -durations in years and months. +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 date, datetime, timedelta +from datetime import timedelta import re +from isodate.duration import Duration from isodate.isoerror import ISO8601Error from isodate.isodatetime import parse_datetime +from isodate.isostrf import strftime, D_DEFAULT ISO8601_PERIOD_REGEX = re.compile(r"^(?P<sign>[+-])?" r"P(?P<years>[0-9]+([,.][0-9]+)?Y)?" @@ -117,208 +119,21 @@ def parse_duration(datestring): ret = Duration(0) - ret return ret - -def fquotmod(val, low, high): - ''' - A divmod function with boundaries. - ''' - div, mod = divmod(val - low, high - low) - mod += low - return int(div), mod - -def max_days_in_month(year, month): - ''' - Determines the number of days of a specific month in a specific year. +def duration_isoformat(tduration, format=D_DEFAULT): ''' - if month in (1, 3, 5, 7, 8, 10, 12): - return 31 - if month in (4, 6, 9, 11): - return 30 - if ((year % 400) == 0) or ((year % 100) != 0) and ((year % 4) == 0): - return 29 - return 28 - -class Duration(object): - ''' - A class which represents a duration. - - The difference to datetime.timedelta is, that this class handles also - differences given in years and months. - A Duration treats differences given in year, months separately from all - other components. - - A Duration can be used almost like any timedelta object, however there - are some restrictions: - * It is not really possible to compare Durations, because it is unclear, - whether a duration of 1 year is bigger than 365 days or not. - * Equality is only tested between the two (year, month vs. timedelta) - basic components. - - A Duration can also be converted into a datetime object, but this requires - a start date or an end date. + Format duration strings. - The algorithm to add a duration to a date is defined at - http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes + This method is just a wrapper around isodate.isostrf.strftime and uses + P%P (D_DEFAULT) as default format. ''' - - def __init__(self, days=0, seconds=0, microseconds=0, milliseconds=0, - minutes=0, hours=0, weeks=0, months=0, years=0): - ''' - Initialise this Duration instance with the given parameters. - ''' - self.months = months - self.years = years - self.tdelta = timedelta(days, seconds, microseconds, milliseconds, - minutes, hours, weeks) - - def __str__(self): - ''' - Return a string representation of this duration similar to timedelta. - ''' - params = [] - if self.years: - params.append('%d years' % self.years) - if self.months: - params.append('%d months' % self.months) - params.append(str(self.tdelta)) - return ', '.join(params) - - def __repr__(self): - ''' - Return a string suitable for repr(x) calls. - ''' - return "%s.%s(%d, %d, %d, years=%d, months=%d)" % ( - self.__class__.__module__, self.__class__.__name__, - self.tdelta.days, self.tdelta.seconds, - self.tdelta.microseconds, self.years, self.months) - - def __add__(self, other): - ''' - Durations can be added with Duration, timedelta, date and datetime - objects. - ''' - if isinstance(other, timedelta): - newduration = Duration(years=self.years, months=self.months) - newduration.tdelta = self.tdelta + other - return newduration - if isinstance(other, Duration): - newduration = Duration(years=self.years+other.years, - months=self.months+other.months) - newduration.tdelta = self.tdelta + other.tdelta - return newduration - if isinstance(other, (date, datetime)): - newmonth = other.month + self.months - carry, newmonth = fquotmod(newmonth, 1, 13) - newyear = other.year + self.years + carry - maxdays = max_days_in_month(newyear, newmonth) - if other.day > maxdays: - newday = maxdays - else: - newday = other.day - newdt = other.replace(year=newyear, month=newmonth, day=newday) - return self.tdelta + newdt - raise TypeError('unsupported operand type(s) for +: %s and %s' % - (self.__class__, other.__class__)) - - - def __radd__(self, other): - ''' - Add durations to timedelta, date and datetime objects. - ''' - if isinstance(other, timedelta): - newduration = Duration(years=self.years, months=self.months) - newduration.tdelta = self.tdelta + other - return newduration - if isinstance(other, (date, datetime)): - newmonth = other.month + self.months - carry, newmonth = fquotmod(newmonth, 1, 13) - newyear = other.year + self.years + carry - maxdays = max_days_in_month(newyear, newmonth) - if other.day > maxdays: - newday = maxdays - else: - newday = other.day - newdt = other.replace(year=newyear, month=newmonth, day=newday) - return newdt + self.tdelta - raise TypeError('unsupported operand type(s) for +: %s and %s' % - (other.__class__, self.__class__)) - - def __sub__(self, other): - ''' - It is possible to subtract Duration and timedelta objects from Duration - objects. - ''' - if isinstance(other, Duration): - newduration = Duration(years=self.years-other.years, - months=self.months-other.months) - newduration.tdelta = self.tdelta - other.tdelta - return newduration - if isinstance(other, timedelta): - newduration = Duration(years=self.years, months=self.months) - newduration.tdelta = self.tdelta - other - return newduration - raise TypeError('unsupported operand type(s) for -: %s and %s' % - (self.__class__, other.__class__)) - - def __rsub__(self, other): - ''' - It is possible to subtract Duration objecs from date, datetime and - timedelta objects. - ''' - #print '__rsub__:', self, other - if isinstance(other, (date, datetime)): - newmonth = other.month - self.months - carry, newmonth = fquotmod(newmonth, 1, 13) - newyear = other.year - self.years + carry - maxdays = max_days_in_month(newyear, newmonth) - if other.day > maxdays: - newday = maxdays - else: - newday = other.day - newdt = other.replace(year=newyear, month=newmonth, day=newday) - return newdt - self.tdelta - if isinstance(other, timedelta): - tmpdur = Duration() - tmpdur.tdelta = other - return tmpdur - self - raise TypeError('unsupported operand type(s) for -: %s and %s' % - (other.__class__, self.__class__)) - - def __eq__(self, other): - ''' - If the years, month part and the timedelta part are both equal, then - the two Durations are considered equal. - ''' - if not isinstance(other, Duration): - return NotImplemented - if ((self.years * 12 + self.months) == - (other.years * 12 + other.months) and self.tdelta == other.tdelta): - return True - return False - - def __ne__(self, other): - ''' - If the years, month part or the timedelta part is not equal, then - the two Durations are considered not equal. - ''' - if not isinstance(other, Duration): - return NotImplemented - if ((self.years * 12 + self.months) != - (other.years * 12 + other.months) or self.tdelta != other.tdelta): - return True - return False - - def todatetime(self, start=None, end=None): - ''' - Convert this duration into a timedelta object. - - This method requires a start datetime or end datetimem, but raises - an exception if both are given. - ''' - if start is None and end is None: - raise ValueError("start or end required") - if start is not None and end is not None: - raise ValueError("only start or end allowed") - if start is not None: - return (start + self) - start - return end - (end - self) + # TODO: implement better decision for negative Durations. + # should be done in Duration class in consistent way with timedelta. + if ((isinstance(tduration, Duration) and (tduration.years < 0 or + tduration.months < 0 or + tduration.tdelta < timedelta(0))) + or (isinstance(tduration, timedelta) and (tduration < timedelta(0)))): + ret = '-' + else: + ret = '' + ret += strftime(tduration, format) + return ret |