def expired(self, *, date: datetime) -> bool: """ Checks if the term's expiration date is before the specified time. @params time : the time to check against @returns true if the term expires before the specified time """ if date is None: raise TimeException(Constants.INVALID_ARGUMENT) if self.end_time is None: raise TimeException(Constants.INVALID_STATE) return date > self.end_time
def enforce_extends_term(self, *, old_term): """ Checks if this term extends the old one. In case this term does not extend old term, logs the error and throws an exception. @params old_term : old term @raises Exception if this term does not extend the old term """ if not isinstance(old_term, Term): raise TimeException("Invalid type: {}".format(type(old_term))) flag = self.extends_term(old_term=old_term) if flag is False: raise TimeException("New term does not extend previous term")
def ends_before(self, *, date: datetime) -> bool: """ Checks if the term ends after the given date. @params date : date to check against @returns true if the term ends before the given date """ if date is None: raise TimeException(Constants.INVALID_ARGUMENT) if self.end_time is None: raise TimeException(Constants.INVALID_STATE) return date > self.end_time
def extends_term(self, *, old_term) -> bool: """ Checks if this term extends the old term. @params old_term: old term to check against @returns true if this term extends the old one, false if not """ if old_term is None or old_term.start_time is None or old_term.end_time is None: raise TimeException(Constants.INVALID_STATE) if self.start_time is None or self.end_time is None or self.new_start_time is None: raise TimeException(Constants.INVALID_STATE) start_time_hh_mm = self.start_time.strftime(self.HH_MM_TIME_FORMAT) old_start_time_hh_mm = old_term.start_time.strftime(self.HH_MM_TIME_FORMAT) return start_time_hh_mm == old_start_time_hh_mm and self.end_time > self.new_start_time
def cycle_start_date(self, *, cycle: int) -> datetime: """ Calculates the first millisecond of the given cycle. @params cycle : cycle @returns first millisecond of the cycle """ if cycle < 0: raise TimeException(Constants.INVALID_ARGUMENT) return self.date(cycle=cycle)
def convert_millis(self, *, millis: int) -> int: """ Returns the number of cycles those milliseconds represent. @params millis : milliseconds @return cycles @raise TimeException for invalid arguments """ if millis < 0: raise TimeException(Constants.INVALID_ARGUMENT) return int(millis / self.cycle_millis)
def shift(self, *, date: datetime): """ Creates a new term from the term. The new term is shifted in time to start at the specified start time. @params date : start time @returns term starting at the specified time, with the length of the current term """ if date is None: raise TimeException("Invalid argument") return Term(start=date, length=self.get_length())
def get_millis(self, *, cycle: int) -> int: """ Returns the number of milliseconds for a specified number of cycles. Does not look at beginning of time. This is useful for knowing the length of a cycle span in milliseconds. @params cycle : cycle @returns millis for cycle cycles @raises Exception in case of invalid arguments """ if cycle < 0: raise TimeException(Constants.INVALID_ARGUMENT) return cycle * self.cycle_millis
def date(self, *, cycle) -> datetime: """ Converts a cycle to a date. @params cycle: cycle @returns date @raises Exception in case of invalid arguments """ if cycle < 0: raise TimeException(Constants.INVALID_ARGUMENT) millis = self.beginning_of_time + (cycle * self.cycle_millis) return self.from_milliseconds(milli_seconds=millis)
def contains(self, *, date: datetime = None, term=None) -> bool: """ Checks if the term contains the given date or term. @params date: the date to check @params term: Term to check @returns true if the term contains the given date or term; false otherwise """ if date is None and term is None: raise TimeException(Constants.INVALID_ARGUMENT) if self.start_time is None or self.end_time is None: raise TimeException(Constants.INVALID_STATE) if date is not None: return (not self.start_time > date) and (not self.end_time < date) if term is not None: if not isinstance(term, Term): raise TimeException(Constants.INVALID_ARGUMENT) return (not self.start_time > term.start_time) and (not self.end_time < term.end_time) return False
def get_length(self) -> int: """ Returns the length of a term in milliseconds. The length of a term is the number of milliseconds in the closed interval [new_start, end] @returns term length """ if self.new_start_time is None or self.end_time is None: raise TimeException(Constants.INVALID_STATE) new_start_ms = ActorClock.to_milliseconds(when=self.new_start_time) end_ms = ActorClock.to_milliseconds(when=self.end_time) return end_ms - new_start_ms + 1
def cycle_end_date(self, *, cycle: int) -> datetime: """ Calculates the last millisecond of the given cycle. @params cycle: cycle @return last millisecond of the cycle """ if cycle < 0: raise TimeException(Constants.INVALID_ARGUMENT) millis = (self.beginning_of_time + ((cycle + 1) * self.cycle_millis)) - 1 return self.from_milliseconds(milli_seconds=millis)
def __init__(self, *, beginning_of_time: int, cycle_millis: int): """ Constructor @params beginning_of_time : time offset (milliseconds) @params cycle_millis : cycle length (milliseconds) @raise TimeException for invalid arguments """ if beginning_of_time < 0 or cycle_millis < 1: raise TimeException("Invalid arguments {} {}".format( beginning_of_time, cycle_millis)) self.beginning_of_time = beginning_of_time self.cycle_millis = cycle_millis
def cycle(self, *, when: datetime) -> int: """ Converts date/milliseconds to cycles. @params date : date @return cycles """ if when is None: raise TimeException(Constants.INVALID_ARGUMENT) millis = self.to_milliseconds(when=when) if millis < self.beginning_of_time: return 0 difference = millis - self.beginning_of_time return int(difference / self.cycle_millis)
def extend(self, *, length: int = 0): """ Creates a new term as an extension of the specified term. The term is extended with the current term length. @params length new term length in milliseconds @returns term extended with the current term length """ if self.start_time is None or self.end_time is None: raise TimeException(Constants.INVALID_STATE) length_to_use = self.get_length() if length != 0: length_to_use = length new_start_ms = ActorClock.to_milliseconds(when=self.end_time) + 1 new_start = ActorClock.from_milliseconds(milli_seconds=new_start_ms) end = ActorClock.from_milliseconds(milli_seconds=new_start_ms + length_to_use - 1) return Term(start=self.start_time, end=end, new_start=new_start)
def __init__(self, *, start: datetime = None, end: datetime = None, new_start: datetime = None, length: int = None): """ Creates a new term. @params start: start time @params end: end time @params new_start: new start time @params length: length in ms """ # Start time: first valid millisecond. if start is not None: self.start_time = start else: self.start_time = datetime.utcnow() # End time: last valid millisecond. if end is not None: self.end_time = end else: if start is not None and length is not None and length > 1: start_ms = ActorClock.to_milliseconds(when=start) + length - 1 self.end_time = ActorClock.from_milliseconds(milli_seconds=start_ms) else: raise TimeException("Invalid arguments, length and end both not specified") # Start time for this section of the lease if new_start is not None: self.new_start_time = new_start else: if start is not None: self.new_start_time = start else: self.new_start_time = datetime.utcnow() # Start cycle. Used only for debugging. self.cycle_start = 0 # End cycle. Used only for debugging. self.cycle_end = 0 # New start cycle. Used only for debugging. self.cycle_new_start = 0 self._set_cycles()
def validate(self): """ Validates the term @raises Exception thrown if invalid start time for term thrown if invalid end time for term thrown if negative duration for term """ if self.start_time is None: raise TimeException("Invalid start time") if self.end_time is None: raise TimeException("Invalid end time") if self.end_time < self.start_time: raise TimeException("negative duration for term") if self.start_time == self.end_time: raise TimeException("zero duration for term") if self.new_start_time < self.start_time: raise TimeException("new start before start") if self.new_start_time > self.end_time: raise TimeException("new start after end")