# -*- coding: utf-8 -*-
"""Extension of datetime.timedelta.
"""
import datetime
import re
[docs]class Duration(datetime.timedelta):
"""A duration of time.
"""
@classmethod
[docs] def sum(cls, sequence, start=None):
"""Return the sum of sequence.
(built-in sum only works with numbers).
"""
if start is None:
start = cls(0)
res = start
for item in sequence:
res += item
return res
@classmethod
[docs] def parse(cls, txt):
"""Parse a textual representation into a Duration object.
Format HHH:MM:SS.
"""
if not txt:
return None
time_matcher = re.compile(r"""
(?:
(?P<negation>-)
)?
(?:
(?P<weeks>\d+) \W* (?:weeks?|w),?
)?
\W*
(?:
(?P<days>\d+) \W* (?:days?|d),?
)?
\W*
(?:
(?P<hours>\d+):
(?P<minutes>\d+)
(?::(?P<seconds>\d+)
(?:\.(?P<microseconds>\d+))?)?
)?
""", re.VERBOSE)
time_matches = time_matcher.match(txt)
time_groups = time_matches.groupdict()
keys = time_groups.keys()
if time_groups.get('negation') == '-':
scale = -1
keys.remove('negation')
else:
scale = 1
for key in keys:
time_groups[key] = int(time_groups[key]) if time_groups[key] else 0
time_groups["days"] = time_groups["days"] + (time_groups["weeks"] * 7)
res = cls(
days=time_groups["days"],
hours=time_groups["hours"],
minutes=time_groups["minutes"],
seconds=time_groups["seconds"])
return res * scale
def __new__(cls, *args, **kw):
if len(args) == 1 and isinstance(args[0], datetime.timedelta):
years = 0
days = args[0].days
hours = 0
minutes = 0
seconds = args[0].seconds
else:
years = kw.get('years', 0)
days = kw.get('days', 0)
hours = kw.get('hours', 0)
minutes = kw.get('minutes', 0)
seconds = kw.get('seconds', 0)
# an average year is 365.2425 days..
leap_days = int(365.2425 * years - 365 * years)
obj = super(Duration, cls).__new__(cls,
days=days + years * 365,
hours=hours + leap_days,
minutes=minutes,
seconds=seconds)
return obj
def __repr__(self):
dt = self.duration_tuple()
return '%sDuration(hours=%d, minutes=%d, seconds=%d)' % dt
[docs] def duration_tuple(self):
"""Return self as hours, minutes, seconds.
"""
seconds = self.toint()
sign = -1 if seconds < 0 else 1
seconds *= sign
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
return '-' if sign < 0 else '', hours, minutes, seconds
@property
def hrs(self):
"""The number of hours in self.
"""
sgn, hr, _mn, _sc = self.duration_tuple()
return int(sgn == "") * hr
@property
def mins(self):
"""The number of minutes in self.
"""
sgn, _hr, mn, _sc = self.duration_tuple()
return int(sgn == "") * mn
@property
def secs(self):
"""The number of seconds in self.
"""
sgn, _hr, _mn, sc = self.duration_tuple()
return int(sgn == "") * sc
def __str__(self):
return '%s%d:%02d:%02d' % self.duration_tuple()
def __unicode__(self):
return unicode(str(self))
[docs] def toint(self):
"""Convert self to integer.
"""
return self.seconds + 3600 * 24 * self.days
__hash__ = datetime.timedelta.__hash__
def __eq__(self, other):
if hasattr(other, '__req__'):
return other.__req__(self)
if isinstance(other, datetime.timedelta):
return super(Duration, self).__eq__(other)
# if isinstance(other, Duration):
# return self.duration_tuple() == other.duration_tuple()
if isinstance(other, int) and other == 0:
return self.toint() == 0
return False
def __ne__(self, other):
if hasattr(other, '__rne__'):
return other.__rne__(self)
if isinstance(other, datetime.timedelta):
return super(Duration, self).__ne__(other)
# if isinstance(other, Duration):
# return self.duration_tuple() != other.duration_tuple()
if isinstance(other, int) and other == 0:
return self.toint() != 0
return False
def __lt__(self, other):
if hasattr(other, '__rlt__'):
return other.__rlt__(self)
if isinstance(other, datetime.timedelta):
return super(Duration, self).__lt__(other)
if hasattr(other, 'toint'):
return self.toint() < other.toint()
return False
def __le__(self, other):
if hasattr(other, '__rle__'):
return other.__rle__(self)
if isinstance(other, datetime.timedelta):
return super(Duration, self).__le__(other)
if hasattr(other, 'toint'):
return self.toint() <= other.toint()
return False
def __gt__(self, other):
if hasattr(other, '__rgt__'):
return other.__rgt__(self)
if isinstance(other, datetime.timedelta):
return super(Duration, self).__gt__(other)
if isinstance(other, int):
return self.toint() > other
if hasattr(other, 'toint'):
return self.toint() > other.toint()
return False
def __ge__(self, other):
if hasattr(other, '__rge__'):
return other.__rge__(self)
if isinstance(other, datetime.timedelta):
return super(Duration, self).__ge__(other)
if hasattr(other, 'toint'):
return self.toint() >= other.toint()
return False
def __mul__(self, other):
return Duration(super(Duration, self).__mul__(other))
def __add__(self, other):
return Duration(super(Duration, self).__add__(other))
def __sub__(self, other):
return Duration(super(Duration, self).__sub__(other))
def __div__(self, other):
if isinstance(other, Duration):
try:
return float(self.toint()) / float(other.toint())
except ZeroDivisionError:
return 0.0
return Duration(super(Duration, self).__div__(other))
# def __truediv__(self, other):
# # if isinstance(other, Duration):
# # try:
# # return int(float(self.toint()) / float(other.toint()))
# # except ZeroDivisionError:
# # return 0
# return Duration(super(Duration, self).__truediv__(other))
# def __rsub__(self, other):
# return other.__sub__(self)
#
# def __rmul__(self, other):
# return other.__mul__(self)
#
# def __rfloordiv__(self, other):
# return other.__floordiv__(self)
#
# def __rdiv__(self, other):
# return other.__div__(self)
#
# def __radd__(self, other):
# return other.__add__(self)