222 lines
7.9 KiB
Python
222 lines
7.9 KiB
Python
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
|
|
"""Des fonction pour gérer les heures.
|
|
"""
|
|
|
|
__all__ = ('pytime',
|
|
'NUM_TIMEF', 'FR_TIMEF', 'HM_TIMEF',
|
|
'Time', 'istime', 'isanytime',
|
|
'parse_time', 'ensure_time',
|
|
)
|
|
|
|
import re
|
|
from time import time as timedbl
|
|
from time import localtime
|
|
from datetime import time as pytime, datetime as pydatetime, timedelta
|
|
|
|
from .base import isstr, isnum
|
|
from .uio import _s, _u
|
|
|
|
NUM_TIMEF = '%H%M%S'
|
|
FR_TIMEF = '%H:%M:%S'
|
|
HM_TIMEF = '%H:%M'
|
|
|
|
def _fix_second(minute, second):
|
|
while second > 59:
|
|
second -= 60
|
|
minute += 1
|
|
while second < 0:
|
|
second += 60
|
|
minute -= 1
|
|
return minute, second
|
|
def _fix_minute(hour, minute):
|
|
while minute > 59:
|
|
minute -= 60
|
|
hour += 1
|
|
while minute < 0:
|
|
minute += 60
|
|
hour -= 1
|
|
return hour, minute
|
|
def _fix_hour(hour):
|
|
return hour % 24
|
|
def _fix_time(hour, minute, second):
|
|
minute, second = _fix_second(minute, second)
|
|
hour, minute = _fix_minute(hour, minute)
|
|
hour = _fix_hour(hour)
|
|
return hour, minute, second
|
|
|
|
class Time(object):
|
|
"""Un wrapper pour 'datetime.time'.
|
|
|
|
Attention! Cet objet est mutable, il ne faut donc pas l'utiliser comme clé
|
|
dans un dictionnaire.
|
|
"""
|
|
_t = None
|
|
|
|
time = property(lambda self: self._t)
|
|
hour = property(lambda self: self._t.hour)
|
|
minute = property(lambda self: self._t.minute)
|
|
second = property(lambda self: self._t.second)
|
|
|
|
def __init__(self, hour=None, minute=None, second=None, t=None, s=None):
|
|
"""Initialiser l'objet
|
|
|
|
Dans l'ordre, les champs considérés sont:
|
|
- s le nombre de secondes depuis minuit.
|
|
- hour, minute, second la spécification de l'heure.
|
|
- t le nombre de secondes depuis l'epoch, comme retourné par
|
|
time.time().
|
|
- sinon prendre l'heure courante.
|
|
"""
|
|
if s is not None:
|
|
hour, s = s / 3600, s % 3600
|
|
minute, second = s / 60, s % 60
|
|
elif hour is None and minute is None and second is None:
|
|
if t is None: t = timedbl()
|
|
hour, minute, second = localtime(t)[3:6]
|
|
hour, minute, second = _fix_time(hour or 0, minute or 0, second or 0)
|
|
self._t = pytime(hour, minute, second)
|
|
|
|
FORMAT_MAP = {'%Y': '%(y)04i', '%m': '%(m)02i', '%d': '%(d)02i',
|
|
'%H': '%(H)02i', '%M': '%(M)02i', '%S': '%(S)02i',
|
|
}
|
|
def format(self, format=None):
|
|
"""Formater l'heure pour affichage.
|
|
|
|
Les champs valides sont %Y, %m, %d qui valent toujours 0, et %H, %M, %S
|
|
qui correspondent à l'heure de cet objet.
|
|
"""
|
|
if format is None: format = FR_TIMEF
|
|
y, m, d, H, M, S = 0, 0, 0, self.hour, self.minute, self.second
|
|
for fr, to in self.FORMAT_MAP.items():
|
|
format = format.replace(fr, to)
|
|
return format % locals()
|
|
|
|
def nbsecs(self):
|
|
"""Retourner le nombre de secondes depuis minuit.
|
|
"""
|
|
return self.hour * 3600 + self.minute * 60 + self.second
|
|
nbsecs = property(nbsecs)
|
|
|
|
def __repr__(self):
|
|
if self.second == 0: seconds = ''
|
|
else: seconds = ', %i' % self.second
|
|
return '%s(%i, %i%s)' % (self.__class__.__name__, self.hour, self.minute, seconds)
|
|
def __str__(self):
|
|
if self.second == 0: seconds = ''
|
|
else: seconds = ':%02i' % self.second
|
|
return '%02i:%02i%s' % (self.hour, self.minute, seconds)
|
|
def __unicode__(self):
|
|
if self.second == 0: seconds = ''
|
|
else: seconds = u':%02i' % self.second
|
|
return u'%02i:%02i%s' % (self.hour, self.minute, seconds)
|
|
|
|
def __eq__(self, other): return self._t == self._time(other, False)
|
|
def __ne__(self, other): return self._t != self._time(other, False)
|
|
def __lt__(self, other):
|
|
if other is None: return False
|
|
else: return self._t < self._time(other)
|
|
def __le__(self, other):
|
|
if other is None: return False
|
|
else: return self._t <= self._time(other)
|
|
def __gt__(self, other):
|
|
if other is None: return True
|
|
else: return self._t > self._time(other)
|
|
def __ge__(self, other):
|
|
if other is None: return True
|
|
else: return self._t >= self._time(other)
|
|
def __cmp__(self, other):
|
|
if other is None: return 1
|
|
else: return cmp(self._t, self._time(other))
|
|
def __hash__(self): return hash(self._t)
|
|
|
|
def _time(self, t, required=True):
|
|
"""Retourner l'instance de datetime.time correspondant à l'objet t.
|
|
"""
|
|
if isinstance(t, pytime): return t
|
|
elif isinstance(t, pydatetime): return t.timetz()
|
|
elif isinstance(t, Time): return t._t
|
|
elif required: raise ValueError("Expected datetime.*time or Time instance, got %s" % repr(t))
|
|
else: return None
|
|
|
|
def _s(self, t=None):
|
|
"""Obtenir le nombre de secondes depuis minuit de t.
|
|
"""
|
|
if t is None: t = self
|
|
if isinstance(t, Time): t = t._t
|
|
if isinstance(t, pytime):
|
|
return t.hour * 3600 + t.minute * 60 + t.second
|
|
elif isinstance(t, pydatetime):
|
|
return t.hour * 3600 + t.minute * 60 + t.second
|
|
elif isinstance(t, timedelta): return t.seconds
|
|
elif isnum(t): return t
|
|
else: raise ValueError("Expected number, datetime.*time, datetime.timedelta or Time instance, got %s" % repr(t))
|
|
|
|
def _new(cls, t=None, s=None):
|
|
"""Constructeur.
|
|
* t est une instance de Time, datetime.time ou datetime.datetime.
|
|
* s est un nombre de secondes depuis minuit.
|
|
"""
|
|
if t is not None:
|
|
if isinstance(t, pytime): return cls(t.hour, t.minute, t.second)
|
|
elif isinstance(t, pydatetime): return cls(t.hour, t.minute, t.second)
|
|
elif isinstance(t, Time): return cls(t.hour, t.minute, t.second)
|
|
else: raise ValueError("Expected datetime.*time or Time instance, got %s" % repr(t))
|
|
elif s is not None: return cls(s=s)
|
|
else: return cls()
|
|
_new = classmethod(_new)
|
|
|
|
def replace(self, hour=None, minute=None, second=None):
|
|
"""Retourner une nouvelle instance avec les champs spécifiés modifiés.
|
|
"""
|
|
kw = {}
|
|
for name, value in [('hour', hour), ('minute', minute), ('second', second)]:
|
|
if value is not None: kw[name] = value
|
|
return self._new(self._t.replace(**kw))
|
|
|
|
def __add__(self, other): return self._new(s=self._s() + self._s(other))
|
|
__radd__ = __add__
|
|
def add(self, seconds=1): return self + seconds
|
|
|
|
def __sub__(self, other): return self._new(s=self._s() - self._s(other))
|
|
__rsub__ = __sub__
|
|
def sub(self, seconds=1): return self - seconds
|
|
|
|
def diff(self, other): return self._s() - self._s(other)
|
|
|
|
def istime(t):
|
|
"""Tester si t est une instance de Time
|
|
"""
|
|
return isinstance(t, Time)
|
|
def isanytime(t):
|
|
"""Tester si t est une instance de Time, datetime.time ou datetime.datetime
|
|
"""
|
|
return isinstance(t, Time) or isinstance(t, pytime) or isinstance(t, pydatetime)
|
|
|
|
RE_TIME = re.compile(r'(\d+)(?:[.:](\d+)(?:[.:](\d+))?)?$')
|
|
def parse_time(s):
|
|
"""Parser une chaine et retourner une instance de Time.
|
|
"""
|
|
mo = RE_TIME.match(s)
|
|
if mo is None: raise ValueError("Invalid time format: %s" % _s(s))
|
|
hour = mo.group(1)
|
|
minute = mo.group(2)
|
|
second = mo.group(3)
|
|
if hour is not None: hour = int(hour)
|
|
if minute is not None: minute = int(minute)
|
|
if second is not None: second = int(second)
|
|
return Time(hour, minute, second)
|
|
|
|
def ensure_time(t):
|
|
"""Retourner une instance de Time, ou None si t==None.
|
|
|
|
t peut être une instance de Time, datetime.time, datetime.datetime ou une
|
|
chaine.
|
|
"""
|
|
if t is None: return None
|
|
elif isinstance(t, Time): return t
|
|
elif isinstance(t, pytime): return Time._new(t)
|
|
elif isinstance(t, pydatetime): return Time._new(t)
|
|
if not isstr(t): t = _s(t)
|
|
return parse_time(t)
|