import reHOURS_PER_MERIDIEM = 12HOURS_PER_DAY = 24MINUTES_PER_HOUR = 60MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOURclass TimeError (Exception): def __init__(self, msg): self.msg = msg def __str__(self): return self.msgclass TimeFormatError (TimeError): passclass Time (object): def __init__(self, time = "12:00 AM"): self.time = time def __add__(self, rhs): """Add minutes to the internal time reprezentation. @param rhs: The right hand side of the operation.""" mins = int(rhs) self.__time += rhs return self def __repr__(self): return self.time def addminutes(self, t, mins): """Add minutes to an arbitrary time without changing the state of the object. Ideally this should be a class method. @params t: The time string to add minutes to. @params mins: The number of minutes to add. @returns: A tuple of the form (hours, minutes, {AM|PM})""" match = re.match(r"(?P<hrs>\d{1,2}):(?P<mins>\d{2})\s*(?P<meridiem>AM|PM)", t) t = self.__to_minutes(match.groups()) + mins return self.__to_time(t) def __to_minutes(self, (hours, minutes, meridiem)): """Convert a tuple of hours, minutes and either `AM' or `PM' to minutes since midnight of the current day. @param meridiem: The side of midday the time falls on, either `AM' or `PM' @returns: The time converted to minutes since midnight as an integer.""" hours = int(hours); minutes = int(minutes) # Sanity check the time to make sure it even makes sense assert(hours <= HOURS_PER_MERIDIEM); assert(minutes < MINUTES_PER_HOUR) # The twelf hour is actually the zero-th hour for each side of the meridiem hours %= 12 if "PM" == meridiem: hours += 12 # Convert the whole time to minutes since midnight. return hours * MINUTES_PER_HOUR + minutes def __to_time(self, m): """Convert seconds since midnight of the current day to a tuple of (hours, minutes, {AM|PM}). If the time rolls over to another day, this will convert it to the corresponding time in the next day. @param m: The minutes since midday to convert.""" # Correct for time rolling over to a new day. time = m % MINUTES_PER_DAY # Convert the minutes to time format. hours = (time / MINUTES_PER_HOUR) % 12 if 0 == hours: hours = 12 minutes = time % MINUTES_PER_HOUR meridiem = "AM" if time >= (MINUTES_PER_DAY / 2): meridiem = "PM" return (hours, minutes, meridiem) def get_time(self): return "%d:%02d %s" % self.__to_time(self.__time) def set_time(self, t): """Property setter which converts a string time `t' to an internal representation. @param t: A time string in the format [H]H:MM {AM|PM}.""" match = re.match(r"(?P<hrs>\d{1,2}):(?P<mins>\d{2})\s*(?P<meridiem>AM|PM)", t) try: self.__time = self.__to_minutes(match.groups()) except AttributeError: raise TimeFormatError("%s is not properly formatted ([H]H:MM {AM|PM})." % t) except AssertionError, e: raise TimeError("invalid time (%s)" % str(e)) time = property(get_time, set_time)