jpayne@69: # -*- coding: utf-8 -*- jpayne@69: import datetime jpayne@69: import calendar jpayne@69: jpayne@69: import operator jpayne@69: from math import copysign jpayne@69: jpayne@69: from six import integer_types jpayne@69: from warnings import warn jpayne@69: jpayne@69: from ._common import weekday jpayne@69: jpayne@69: MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) jpayne@69: jpayne@69: __all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] jpayne@69: jpayne@69: jpayne@69: class relativedelta(object): jpayne@69: """ jpayne@69: The relativedelta type is designed to be applied to an existing datetime and jpayne@69: can replace specific components of that datetime, or represents an interval jpayne@69: of time. jpayne@69: jpayne@69: It is based on the specification of the excellent work done by M.-A. Lemburg jpayne@69: in his jpayne@69: `mx.DateTime `_ extension. jpayne@69: However, notice that this type does *NOT* implement the same algorithm as jpayne@69: his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. jpayne@69: jpayne@69: There are two different ways to build a relativedelta instance. The jpayne@69: first one is passing it two date/datetime classes:: jpayne@69: jpayne@69: relativedelta(datetime1, datetime2) jpayne@69: jpayne@69: The second one is passing it any number of the following keyword arguments:: jpayne@69: jpayne@69: relativedelta(arg1=x,arg2=y,arg3=z...) jpayne@69: jpayne@69: year, month, day, hour, minute, second, microsecond: jpayne@69: Absolute information (argument is singular); adding or subtracting a jpayne@69: relativedelta with absolute information does not perform an arithmetic jpayne@69: operation, but rather REPLACES the corresponding value in the jpayne@69: original datetime with the value(s) in relativedelta. jpayne@69: jpayne@69: years, months, weeks, days, hours, minutes, seconds, microseconds: jpayne@69: Relative information, may be negative (argument is plural); adding jpayne@69: or subtracting a relativedelta with relative information performs jpayne@69: the corresponding arithmetic operation on the original datetime value jpayne@69: with the information in the relativedelta. jpayne@69: jpayne@69: weekday: jpayne@69: One of the weekday instances (MO, TU, etc) available in the jpayne@69: relativedelta module. These instances may receive a parameter N, jpayne@69: specifying the Nth weekday, which could be positive or negative jpayne@69: (like MO(+1) or MO(-2)). Not specifying it is the same as specifying jpayne@69: +1. You can also use an integer, where 0=MO. This argument is always jpayne@69: relative e.g. if the calculated date is already Monday, using MO(1) jpayne@69: or MO(-1) won't change the day. To effectively make it absolute, use jpayne@69: it in combination with the day argument (e.g. day=1, MO(1) for first jpayne@69: Monday of the month). jpayne@69: jpayne@69: leapdays: jpayne@69: Will add given days to the date found, if year is a leap jpayne@69: year, and the date found is post 28 of february. jpayne@69: jpayne@69: yearday, nlyearday: jpayne@69: Set the yearday or the non-leap year day (jump leap days). jpayne@69: These are converted to day/month/leapdays information. jpayne@69: jpayne@69: There are relative and absolute forms of the keyword jpayne@69: arguments. The plural is relative, and the singular is jpayne@69: absolute. For each argument in the order below, the absolute form jpayne@69: is applied first (by setting each attribute to that value) and jpayne@69: then the relative form (by adding the value to the attribute). jpayne@69: jpayne@69: The order of attributes considered when this relativedelta is jpayne@69: added to a datetime is: jpayne@69: jpayne@69: 1. Year jpayne@69: 2. Month jpayne@69: 3. Day jpayne@69: 4. Hours jpayne@69: 5. Minutes jpayne@69: 6. Seconds jpayne@69: 7. Microseconds jpayne@69: jpayne@69: Finally, weekday is applied, using the rule described above. jpayne@69: jpayne@69: For example jpayne@69: jpayne@69: >>> from datetime import datetime jpayne@69: >>> from dateutil.relativedelta import relativedelta, MO jpayne@69: >>> dt = datetime(2018, 4, 9, 13, 37, 0) jpayne@69: >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) jpayne@69: >>> dt + delta jpayne@69: datetime.datetime(2018, 4, 2, 14, 37) jpayne@69: jpayne@69: First, the day is set to 1 (the first of the month), then 25 hours jpayne@69: are added, to get to the 2nd day and 14th hour, finally the jpayne@69: weekday is applied, but since the 2nd is already a Monday there is jpayne@69: no effect. jpayne@69: jpayne@69: """ jpayne@69: jpayne@69: def __init__(self, dt1=None, dt2=None, jpayne@69: years=0, months=0, days=0, leapdays=0, weeks=0, jpayne@69: hours=0, minutes=0, seconds=0, microseconds=0, jpayne@69: year=None, month=None, day=None, weekday=None, jpayne@69: yearday=None, nlyearday=None, jpayne@69: hour=None, minute=None, second=None, microsecond=None): jpayne@69: jpayne@69: if dt1 and dt2: jpayne@69: # datetime is a subclass of date. So both must be date jpayne@69: if not (isinstance(dt1, datetime.date) and jpayne@69: isinstance(dt2, datetime.date)): jpayne@69: raise TypeError("relativedelta only diffs datetime/date") jpayne@69: jpayne@69: # We allow two dates, or two datetimes, so we coerce them to be jpayne@69: # of the same type jpayne@69: if (isinstance(dt1, datetime.datetime) != jpayne@69: isinstance(dt2, datetime.datetime)): jpayne@69: if not isinstance(dt1, datetime.datetime): jpayne@69: dt1 = datetime.datetime.fromordinal(dt1.toordinal()) jpayne@69: elif not isinstance(dt2, datetime.datetime): jpayne@69: dt2 = datetime.datetime.fromordinal(dt2.toordinal()) jpayne@69: jpayne@69: self.years = 0 jpayne@69: self.months = 0 jpayne@69: self.days = 0 jpayne@69: self.leapdays = 0 jpayne@69: self.hours = 0 jpayne@69: self.minutes = 0 jpayne@69: self.seconds = 0 jpayne@69: self.microseconds = 0 jpayne@69: self.year = None jpayne@69: self.month = None jpayne@69: self.day = None jpayne@69: self.weekday = None jpayne@69: self.hour = None jpayne@69: self.minute = None jpayne@69: self.second = None jpayne@69: self.microsecond = None jpayne@69: self._has_time = 0 jpayne@69: jpayne@69: # Get year / month delta between the two jpayne@69: months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) jpayne@69: self._set_months(months) jpayne@69: jpayne@69: # Remove the year/month delta so the timedelta is just well-defined jpayne@69: # time units (seconds, days and microseconds) jpayne@69: dtm = self.__radd__(dt2) jpayne@69: jpayne@69: # If we've overshot our target, make an adjustment jpayne@69: if dt1 < dt2: jpayne@69: compare = operator.gt jpayne@69: increment = 1 jpayne@69: else: jpayne@69: compare = operator.lt jpayne@69: increment = -1 jpayne@69: jpayne@69: while compare(dt1, dtm): jpayne@69: months += increment jpayne@69: self._set_months(months) jpayne@69: dtm = self.__radd__(dt2) jpayne@69: jpayne@69: # Get the timedelta between the "months-adjusted" date and dt1 jpayne@69: delta = dt1 - dtm jpayne@69: self.seconds = delta.seconds + delta.days * 86400 jpayne@69: self.microseconds = delta.microseconds jpayne@69: else: jpayne@69: # Check for non-integer values in integer-only quantities jpayne@69: if any(x is not None and x != int(x) for x in (years, months)): jpayne@69: raise ValueError("Non-integer years and months are " jpayne@69: "ambiguous and not currently supported.") jpayne@69: jpayne@69: # Relative information jpayne@69: self.years = int(years) jpayne@69: self.months = int(months) jpayne@69: self.days = days + weeks * 7 jpayne@69: self.leapdays = leapdays jpayne@69: self.hours = hours jpayne@69: self.minutes = minutes jpayne@69: self.seconds = seconds jpayne@69: self.microseconds = microseconds jpayne@69: jpayne@69: # Absolute information jpayne@69: self.year = year jpayne@69: self.month = month jpayne@69: self.day = day jpayne@69: self.hour = hour jpayne@69: self.minute = minute jpayne@69: self.second = second jpayne@69: self.microsecond = microsecond jpayne@69: jpayne@69: if any(x is not None and int(x) != x jpayne@69: for x in (year, month, day, hour, jpayne@69: minute, second, microsecond)): jpayne@69: # For now we'll deprecate floats - later it'll be an error. jpayne@69: warn("Non-integer value passed as absolute information. " + jpayne@69: "This is not a well-defined condition and will raise " + jpayne@69: "errors in future versions.", DeprecationWarning) jpayne@69: jpayne@69: if isinstance(weekday, integer_types): jpayne@69: self.weekday = weekdays[weekday] jpayne@69: else: jpayne@69: self.weekday = weekday jpayne@69: jpayne@69: yday = 0 jpayne@69: if nlyearday: jpayne@69: yday = nlyearday jpayne@69: elif yearday: jpayne@69: yday = yearday jpayne@69: if yearday > 59: jpayne@69: self.leapdays = -1 jpayne@69: if yday: jpayne@69: ydayidx = [31, 59, 90, 120, 151, 181, 212, jpayne@69: 243, 273, 304, 334, 366] jpayne@69: for idx, ydays in enumerate(ydayidx): jpayne@69: if yday <= ydays: jpayne@69: self.month = idx+1 jpayne@69: if idx == 0: jpayne@69: self.day = yday jpayne@69: else: jpayne@69: self.day = yday-ydayidx[idx-1] jpayne@69: break jpayne@69: else: jpayne@69: raise ValueError("invalid year day (%d)" % yday) jpayne@69: jpayne@69: self._fix() jpayne@69: jpayne@69: def _fix(self): jpayne@69: if abs(self.microseconds) > 999999: jpayne@69: s = _sign(self.microseconds) jpayne@69: div, mod = divmod(self.microseconds * s, 1000000) jpayne@69: self.microseconds = mod * s jpayne@69: self.seconds += div * s jpayne@69: if abs(self.seconds) > 59: jpayne@69: s = _sign(self.seconds) jpayne@69: div, mod = divmod(self.seconds * s, 60) jpayne@69: self.seconds = mod * s jpayne@69: self.minutes += div * s jpayne@69: if abs(self.minutes) > 59: jpayne@69: s = _sign(self.minutes) jpayne@69: div, mod = divmod(self.minutes * s, 60) jpayne@69: self.minutes = mod * s jpayne@69: self.hours += div * s jpayne@69: if abs(self.hours) > 23: jpayne@69: s = _sign(self.hours) jpayne@69: div, mod = divmod(self.hours * s, 24) jpayne@69: self.hours = mod * s jpayne@69: self.days += div * s jpayne@69: if abs(self.months) > 11: jpayne@69: s = _sign(self.months) jpayne@69: div, mod = divmod(self.months * s, 12) jpayne@69: self.months = mod * s jpayne@69: self.years += div * s jpayne@69: if (self.hours or self.minutes or self.seconds or self.microseconds jpayne@69: or self.hour is not None or self.minute is not None or jpayne@69: self.second is not None or self.microsecond is not None): jpayne@69: self._has_time = 1 jpayne@69: else: jpayne@69: self._has_time = 0 jpayne@69: jpayne@69: @property jpayne@69: def weeks(self): jpayne@69: return int(self.days / 7.0) jpayne@69: jpayne@69: @weeks.setter jpayne@69: def weeks(self, value): jpayne@69: self.days = self.days - (self.weeks * 7) + value * 7 jpayne@69: jpayne@69: def _set_months(self, months): jpayne@69: self.months = months jpayne@69: if abs(self.months) > 11: jpayne@69: s = _sign(self.months) jpayne@69: div, mod = divmod(self.months * s, 12) jpayne@69: self.months = mod * s jpayne@69: self.years = div * s jpayne@69: else: jpayne@69: self.years = 0 jpayne@69: jpayne@69: def normalized(self): jpayne@69: """ jpayne@69: Return a version of this object represented entirely using integer jpayne@69: values for the relative attributes. jpayne@69: jpayne@69: >>> relativedelta(days=1.5, hours=2).normalized() jpayne@69: relativedelta(days=+1, hours=+14) jpayne@69: jpayne@69: :return: jpayne@69: Returns a :class:`dateutil.relativedelta.relativedelta` object. jpayne@69: """ jpayne@69: # Cascade remainders down (rounding each to roughly nearest microsecond) jpayne@69: days = int(self.days) jpayne@69: jpayne@69: hours_f = round(self.hours + 24 * (self.days - days), 11) jpayne@69: hours = int(hours_f) jpayne@69: jpayne@69: minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) jpayne@69: minutes = int(minutes_f) jpayne@69: jpayne@69: seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) jpayne@69: seconds = int(seconds_f) jpayne@69: jpayne@69: microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) jpayne@69: jpayne@69: # Constructor carries overflow back up with call to _fix() jpayne@69: return self.__class__(years=self.years, months=self.months, jpayne@69: days=days, hours=hours, minutes=minutes, jpayne@69: seconds=seconds, microseconds=microseconds, jpayne@69: leapdays=self.leapdays, year=self.year, jpayne@69: month=self.month, day=self.day, jpayne@69: weekday=self.weekday, hour=self.hour, jpayne@69: minute=self.minute, second=self.second, jpayne@69: microsecond=self.microsecond) jpayne@69: jpayne@69: def __add__(self, other): jpayne@69: if isinstance(other, relativedelta): jpayne@69: return self.__class__(years=other.years + self.years, jpayne@69: months=other.months + self.months, jpayne@69: days=other.days + self.days, jpayne@69: hours=other.hours + self.hours, jpayne@69: minutes=other.minutes + self.minutes, jpayne@69: seconds=other.seconds + self.seconds, jpayne@69: microseconds=(other.microseconds + jpayne@69: self.microseconds), jpayne@69: leapdays=other.leapdays or self.leapdays, jpayne@69: year=(other.year if other.year is not None jpayne@69: else self.year), jpayne@69: month=(other.month if other.month is not None jpayne@69: else self.month), jpayne@69: day=(other.day if other.day is not None jpayne@69: else self.day), jpayne@69: weekday=(other.weekday if other.weekday is not None jpayne@69: else self.weekday), jpayne@69: hour=(other.hour if other.hour is not None jpayne@69: else self.hour), jpayne@69: minute=(other.minute if other.minute is not None jpayne@69: else self.minute), jpayne@69: second=(other.second if other.second is not None jpayne@69: else self.second), jpayne@69: microsecond=(other.microsecond if other.microsecond jpayne@69: is not None else jpayne@69: self.microsecond)) jpayne@69: if isinstance(other, datetime.timedelta): jpayne@69: return self.__class__(years=self.years, jpayne@69: months=self.months, jpayne@69: days=self.days + other.days, jpayne@69: hours=self.hours, jpayne@69: minutes=self.minutes, jpayne@69: seconds=self.seconds + other.seconds, jpayne@69: microseconds=self.microseconds + other.microseconds, jpayne@69: leapdays=self.leapdays, jpayne@69: year=self.year, jpayne@69: month=self.month, jpayne@69: day=self.day, jpayne@69: weekday=self.weekday, jpayne@69: hour=self.hour, jpayne@69: minute=self.minute, jpayne@69: second=self.second, jpayne@69: microsecond=self.microsecond) jpayne@69: if not isinstance(other, datetime.date): jpayne@69: return NotImplemented jpayne@69: elif self._has_time and not isinstance(other, datetime.datetime): jpayne@69: other = datetime.datetime.fromordinal(other.toordinal()) jpayne@69: year = (self.year or other.year)+self.years jpayne@69: month = self.month or other.month jpayne@69: if self.months: jpayne@69: assert 1 <= abs(self.months) <= 12 jpayne@69: month += self.months jpayne@69: if month > 12: jpayne@69: year += 1 jpayne@69: month -= 12 jpayne@69: elif month < 1: jpayne@69: year -= 1 jpayne@69: month += 12 jpayne@69: day = min(calendar.monthrange(year, month)[1], jpayne@69: self.day or other.day) jpayne@69: repl = {"year": year, "month": month, "day": day} jpayne@69: for attr in ["hour", "minute", "second", "microsecond"]: jpayne@69: value = getattr(self, attr) jpayne@69: if value is not None: jpayne@69: repl[attr] = value jpayne@69: days = self.days jpayne@69: if self.leapdays and month > 2 and calendar.isleap(year): jpayne@69: days += self.leapdays jpayne@69: ret = (other.replace(**repl) jpayne@69: + datetime.timedelta(days=days, jpayne@69: hours=self.hours, jpayne@69: minutes=self.minutes, jpayne@69: seconds=self.seconds, jpayne@69: microseconds=self.microseconds)) jpayne@69: if self.weekday: jpayne@69: weekday, nth = self.weekday.weekday, self.weekday.n or 1 jpayne@69: jumpdays = (abs(nth) - 1) * 7 jpayne@69: if nth > 0: jpayne@69: jumpdays += (7 - ret.weekday() + weekday) % 7 jpayne@69: else: jpayne@69: jumpdays += (ret.weekday() - weekday) % 7 jpayne@69: jumpdays *= -1 jpayne@69: ret += datetime.timedelta(days=jumpdays) jpayne@69: return ret jpayne@69: jpayne@69: def __radd__(self, other): jpayne@69: return self.__add__(other) jpayne@69: jpayne@69: def __rsub__(self, other): jpayne@69: return self.__neg__().__radd__(other) jpayne@69: jpayne@69: def __sub__(self, other): jpayne@69: if not isinstance(other, relativedelta): jpayne@69: return NotImplemented # In case the other object defines __rsub__ jpayne@69: return self.__class__(years=self.years - other.years, jpayne@69: months=self.months - other.months, jpayne@69: days=self.days - other.days, jpayne@69: hours=self.hours - other.hours, jpayne@69: minutes=self.minutes - other.minutes, jpayne@69: seconds=self.seconds - other.seconds, jpayne@69: microseconds=self.microseconds - other.microseconds, jpayne@69: leapdays=self.leapdays or other.leapdays, jpayne@69: year=(self.year if self.year is not None jpayne@69: else other.year), jpayne@69: month=(self.month if self.month is not None else jpayne@69: other.month), jpayne@69: day=(self.day if self.day is not None else jpayne@69: other.day), jpayne@69: weekday=(self.weekday if self.weekday is not None else jpayne@69: other.weekday), jpayne@69: hour=(self.hour if self.hour is not None else jpayne@69: other.hour), jpayne@69: minute=(self.minute if self.minute is not None else jpayne@69: other.minute), jpayne@69: second=(self.second if self.second is not None else jpayne@69: other.second), jpayne@69: microsecond=(self.microsecond if self.microsecond jpayne@69: is not None else jpayne@69: other.microsecond)) jpayne@69: jpayne@69: def __abs__(self): jpayne@69: return self.__class__(years=abs(self.years), jpayne@69: months=abs(self.months), jpayne@69: days=abs(self.days), jpayne@69: hours=abs(self.hours), jpayne@69: minutes=abs(self.minutes), jpayne@69: seconds=abs(self.seconds), jpayne@69: microseconds=abs(self.microseconds), jpayne@69: leapdays=self.leapdays, jpayne@69: year=self.year, jpayne@69: month=self.month, jpayne@69: day=self.day, jpayne@69: weekday=self.weekday, jpayne@69: hour=self.hour, jpayne@69: minute=self.minute, jpayne@69: second=self.second, jpayne@69: microsecond=self.microsecond) jpayne@69: jpayne@69: def __neg__(self): jpayne@69: return self.__class__(years=-self.years, jpayne@69: months=-self.months, jpayne@69: days=-self.days, jpayne@69: hours=-self.hours, jpayne@69: minutes=-self.minutes, jpayne@69: seconds=-self.seconds, jpayne@69: microseconds=-self.microseconds, jpayne@69: leapdays=self.leapdays, jpayne@69: year=self.year, jpayne@69: month=self.month, jpayne@69: day=self.day, jpayne@69: weekday=self.weekday, jpayne@69: hour=self.hour, jpayne@69: minute=self.minute, jpayne@69: second=self.second, jpayne@69: microsecond=self.microsecond) jpayne@69: jpayne@69: def __bool__(self): jpayne@69: return not (not self.years and jpayne@69: not self.months and jpayne@69: not self.days and jpayne@69: not self.hours and jpayne@69: not self.minutes and jpayne@69: not self.seconds and jpayne@69: not self.microseconds and jpayne@69: not self.leapdays and jpayne@69: self.year is None and jpayne@69: self.month is None and jpayne@69: self.day is None and jpayne@69: self.weekday is None and jpayne@69: self.hour is None and jpayne@69: self.minute is None and jpayne@69: self.second is None and jpayne@69: self.microsecond is None) jpayne@69: # Compatibility with Python 2.x jpayne@69: __nonzero__ = __bool__ jpayne@69: jpayne@69: def __mul__(self, other): jpayne@69: try: jpayne@69: f = float(other) jpayne@69: except TypeError: jpayne@69: return NotImplemented jpayne@69: jpayne@69: return self.__class__(years=int(self.years * f), jpayne@69: months=int(self.months * f), jpayne@69: days=int(self.days * f), jpayne@69: hours=int(self.hours * f), jpayne@69: minutes=int(self.minutes * f), jpayne@69: seconds=int(self.seconds * f), jpayne@69: microseconds=int(self.microseconds * f), jpayne@69: leapdays=self.leapdays, jpayne@69: year=self.year, jpayne@69: month=self.month, jpayne@69: day=self.day, jpayne@69: weekday=self.weekday, jpayne@69: hour=self.hour, jpayne@69: minute=self.minute, jpayne@69: second=self.second, jpayne@69: microsecond=self.microsecond) jpayne@69: jpayne@69: __rmul__ = __mul__ jpayne@69: jpayne@69: def __eq__(self, other): jpayne@69: if not isinstance(other, relativedelta): jpayne@69: return NotImplemented jpayne@69: if self.weekday or other.weekday: jpayne@69: if not self.weekday or not other.weekday: jpayne@69: return False jpayne@69: if self.weekday.weekday != other.weekday.weekday: jpayne@69: return False jpayne@69: n1, n2 = self.weekday.n, other.weekday.n jpayne@69: if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): jpayne@69: return False jpayne@69: return (self.years == other.years and jpayne@69: self.months == other.months and jpayne@69: self.days == other.days and jpayne@69: self.hours == other.hours and jpayne@69: self.minutes == other.minutes and jpayne@69: self.seconds == other.seconds and jpayne@69: self.microseconds == other.microseconds and jpayne@69: self.leapdays == other.leapdays and jpayne@69: self.year == other.year and jpayne@69: self.month == other.month and jpayne@69: self.day == other.day and jpayne@69: self.hour == other.hour and jpayne@69: self.minute == other.minute and jpayne@69: self.second == other.second and jpayne@69: self.microsecond == other.microsecond) jpayne@69: jpayne@69: def __hash__(self): jpayne@69: return hash(( jpayne@69: self.weekday, jpayne@69: self.years, jpayne@69: self.months, jpayne@69: self.days, jpayne@69: self.hours, jpayne@69: self.minutes, jpayne@69: self.seconds, jpayne@69: self.microseconds, jpayne@69: self.leapdays, jpayne@69: self.year, jpayne@69: self.month, jpayne@69: self.day, jpayne@69: self.hour, jpayne@69: self.minute, jpayne@69: self.second, jpayne@69: self.microsecond, jpayne@69: )) jpayne@69: jpayne@69: def __ne__(self, other): jpayne@69: return not self.__eq__(other) jpayne@69: jpayne@69: def __div__(self, other): jpayne@69: try: jpayne@69: reciprocal = 1 / float(other) jpayne@69: except TypeError: jpayne@69: return NotImplemented jpayne@69: jpayne@69: return self.__mul__(reciprocal) jpayne@69: jpayne@69: __truediv__ = __div__ jpayne@69: jpayne@69: def __repr__(self): jpayne@69: l = [] jpayne@69: for attr in ["years", "months", "days", "leapdays", jpayne@69: "hours", "minutes", "seconds", "microseconds"]: jpayne@69: value = getattr(self, attr) jpayne@69: if value: jpayne@69: l.append("{attr}={value:+g}".format(attr=attr, value=value)) jpayne@69: for attr in ["year", "month", "day", "weekday", jpayne@69: "hour", "minute", "second", "microsecond"]: jpayne@69: value = getattr(self, attr) jpayne@69: if value is not None: jpayne@69: l.append("{attr}={value}".format(attr=attr, value=repr(value))) jpayne@69: return "{classname}({attrs})".format(classname=self.__class__.__name__, jpayne@69: attrs=", ".join(l)) jpayne@69: jpayne@69: jpayne@69: def _sign(x): jpayne@69: return int(copysign(1, x)) jpayne@69: jpayne@69: # vim:ts=4:sw=4:et