Source code for pycaps.util.temporal


from datetime import datetime, timedelta
from itertools import chain

_epoch = datetime(1970, 1, 1, 0, 0, 0)


def _to_list(iter, tolist):
    if tolist:
        return list(iter)
    else:
        return iter


[docs]class Temporal(object): """ Keep track of time in an experiment and convert times between seconds since experiment start, datetime objects and seconds since the epoch. """ def __init__(self, base_time, t_ens_start, t_ens_end, t_ens_step): """ Constructor for the Temporal object Args: base_time (datetime): A datetime object specifying the reference time of the experiment. t_ens_start (int): An integer specifying the start time for the experiment. t_ens_end (int): An integer specifying the end time for the experiment. t_ens_step (int): An integer specifying the time between history dumps for the experiment. """ self._base_time = base_time self._t_ens_start = t_ens_start self._t_ens_end = t_ens_end self._t_ens_step = t_ens_step return
[docs] def __iter__(self): """ Iterate over the experiment times. Yields: int: The next time in seconds since the experiment reference time. Examples: >>> dt = datetime(2011, 5, 24, 18) >>> temp = Temporal(dt, 0, 1800, 300) >>> [ t for t in temp ] [ 0, 300, 600, 900, 1200, 1500, 1800 ] """ for t_ens in xrange(self._t_ens_start, self._t_ens_end + self._t_ens_step, self._t_ens_step): yield t_ens
[docs] def __len__(self): """ Get the number of times in the experiment. Returns: int: The number of times in the experiment. Examples: >>> dt = datetime(2011, 5, 24, 18) >>> temp = Temporal(dt, 0, 1800, 300) >>> len(temp) 7 """ return len(xrange(self._t_ens_start, self._t_ens_end + self._t_ens_step, self._t_ens_step))
[docs] def __getitem__(self, index): """ Get the time at a particular index. Args: index: The index at which to return the time. Returns: If `index` is an integer, return the number of seconds at `index` since the experiment reference time. If `index` is a slice, return a new Temporal object with subsetted according to the slice. Examples: >>> dt = datetime(2011, 5, 24, 18) >>> temp = Temporal(dt, 0, 1800, 300) >>> temp[3] 900 """ times = list(self) if type(index) == slice: subset_times = times[index] if len(subset_times) == 1: ret_val = Temporal(self._base_time, subset_times[0], subset_times[-1], times[1] - times[0]) else: ret_val = Temporal(self._base_time, subset_times[0], subset_times[-1], subset_times[1] - subset_times[0]) else: ret_val = times[index] return ret_val
[docs] def get_times(self): """ Get the list of times represented by the Temporal object. Returns: A list of times in seconds since the experiment reference time. """ return list(self)
[docs] def get_datetimes(self, aslist=False): """ Get a list of datetimes represented by the Temporal object. Args: aslist (bool): Whether to return a list (True) or a Python generator (False). They can be used mostly interchangeably, but a generator is more efficient. Default is False (return a generator). Returns: A Python generator or list of datetime objects. """ dts = (self._base_time + timedelta(seconds=float(t_ens)) for t_ens in self) return _to_list(dts, aslist)
[docs] def get_epochs(self, aslist=False): """ Get a list of times represented by the Temporal object. Args: aslist (bool): Whether to return a list (True) or a Python generator (False). They can be used mostly interchangeably, but a generator is more efficient. Default is False (return a generator). Returns: A Python generator or list of times in seconds since the epoch. """ epochs = (self.sec_to_epoch(t_ens) for t_ens in self) return _to_list(epochs, aslist)
[docs] def get_strings(self, str_format, aslist=False): """ Get a list of times as formatted strings. Args: str_format (str): The string format to use. aslist (bool): Whether to return a list (True) or a Python generator (False). They can be used mostly interchangeably, but a generator is more efficient. Default is False (return a generator). Returns: A Python generator or list of strings containing the formatted times. """ strings = (dt.strftime(str_format) for dt in self.get_datetimes()) return _to_list(strings, aslist)
[docs] def sec_to_epoch(self, exp_seconds): """ Convert a time in seconds since experiment reference time to a time in seconds since the epoch. Args: exp_seconds (int): The number of seconds since the experiment reference time. Returns: int: A number of seconds since the epoch. """ base_epoch = dt_to_epoch(self._base_time) return base_epoch + exp_seconds
[docs] def sec_to_datetime(self, exp_seconds): """ Convert a number of seconds since the experiment reference time to a datetime object. Args: exp_seconds (int): The number of seconds since the experiment reference time. Returns: datetime: A datetime object representing that time. """ return self._base_time + timedelta(seconds=float(exp_seconds))
[docs]class PatchedTemporal(object): """ Keep track of time in an experiment and do time conversions, but for timelines in which the time step is not constant. """ def __init__(self, *args): """ Constructor for the PatchedTemporal object. Args: *args (Temporal): A variable length argument list. Each argument must be a Temporal object. """ if not all(isinstance(a, Temporal) for a in args): raise TypeError("All arguments to PatchedTemporal must be of type Temporal.") self._temporals = [ args[0] ] + [ a[1:] for a in args[1:] ] # Chop out the first element of each Temporal self._base_time = self._temporals[0]._base_time return
[docs] def __iter__(self): """ Iterate over the experiment times. Yields: int: The next time in seconds since the experiment reference time. Examples: >>> temp1 = Temporal(0, 1200, 300) >>> temp2 = Temporal(1200, 1800, 150) >>> temp = PatchedTemporal(temp1, temp2) >>> [ t for t in temp ] [ 0, 300, 600, 900, 1200, 1350, 1500, 1650, 1800 ] """ return chain(*self._temporals)
[docs] def __len__(self): """ Get the number of times in the experiment. Returns: int: The number of times in the experiment. Examples: >>> temp1 = Temporal(0, 1200, 300) >>> temp2 = Temporal(1200, 1800, 150) >>> temp = PatchedTemporal(temp1, temp2) >>> len(temp) 9 """ return sum( len(t) for t in self._temporals )
[docs] def __getitem__(self, index): """ Get the time at a particular index. Args: index (int): The index at which to return the time. Returns: int: The number of seconds at that index since the experiment reference time. Examples: >>> temp1 = Temporal(0, 1200, 300) >>> temp2 = Temporal(1200, 1800, 150) >>> temp = PatchedTemporal(temp1, temp2) >>> temp[6] 1500 """ return list(self)[index]
[docs] def get_times(self): """ Get the list of times represented by the Temporal object. Returns: A list of times in seconds since the experiment reference time. """ return list(self)
[docs] def get_datetimes(self, aslist=False): """ Get a list of datetimes represented by the Temporal object. Args: aslist (bool): Whether to return a list (True) or a Python generator (False). They can be used mostly interchangeably, but a generator is more efficient. Default is False (return a generator). Returns: A Python generator or list of datetime objects. """ dts = chain(*[temp.get_datetimes() for temp in self._temporals]) return _to_list(dts, aslist)
[docs] def get_epochs(self, aslist=False): """ Get a list of times represented by the Temporal object. Args: aslist (bool): Whether to return a list (True) or a Python generator (False). They can be used mostly interchangeably, but a generator is more efficient. Default is False (return a generator). Returns: A Python generator or list of times in seconds since the epoch. """ epochs = chain(*[temp.get_epochs() for temp in self._temporals]) return _to_list(epochs, aslist)
[docs] def get_strings(self, format, aslist=False): """ Get a list of times as formatted strings. Args: format (str): The string format to use. aslist (bool): Whether to return a list (True) or a Python generator (False). They can be used mostly interchangeably, but a generator is more efficient. Default is False (return a generator). Returns: A Python generator or list of strings containing the formatted times. """ strings = chain(*[temp.get_strings(format) for temp in self._temporals]) return _to_list(strings, aslist)
[docs] def sec_to_epoch(self, exp_seconds): """ Convert a time in seconds since experiment reference time to a time in seconds since the epoch. Args: exp_seconds (int): The number of seconds since the experiment reference time. Returns: int: A number of seconds since the epoch. """ base_epoch = dt_to_epoch(self._base_time) return base_epoch + exp_seconds
[docs] def sec_to_dt(self, exp_seconds): """ Convert a number of seconds since the experiment reference time to a datetime object. Args: exp_seconds (int): The number of seconds since the experiment reference time. Returns: datetime: A datetime object representing that time. """ return self._base_time + timedelta(seconds=float(exp_seconds))
[docs]def temporal(base_time, *slices): """ Create an object to keep track of time in an experiment and do time-related conversions. Args: base_time (datetime): The base time in the experiment (i.e. inittime in the ARPS input file) as a datetime object. *slices (slice): One or more slice objects, each specifying start and end times (in seconds since the base time) and the time step length (in seconds) for a segment. Multiple segments are chained together under the hood to form a continuous timeline. Returns: A Temporal or PatchedTemporal object (both have the same interface). """ temp_objs = [Temporal(base_time, s.start, s.stop, s.step) for s in slices] if len(temp_objs) == 0: raise ValueError("temporal() needs one or more slices.") elif len(temp_objs) == 1: temp = temp_objs[0] else: temp = PatchedTemporal(*temp_objs) return temp
[docs]def dt_to_epoch(dt): """ Convert a Python datetime object to a number of seconds since the epoch (00 UTC 1 January 1970). Args: dt (datetime): A Python datetime object Returns: Time since the epoch in seconds. """ return (dt - _epoch).total_seconds()
[docs]def epoch_to_dt(epoch): """ Convert a number of seconds since the epoch (00 UTC 1 January 1970) to a Python datetime object. Args: epoch (float): Time since the epoch in seconds Returns: A Python datetime object. """ return _epoch + timedelta(seconds=float(epoch))
if __name__ == "__main__": # This returns a temporal object with two segments. temp_test = temporal(datetime(2011, 5, 24, 18, 0, 0), slice(0, 1800, 300), slice(1800, 3600, 150)) print "Temporal testing ..." print type(temp_test) print list(temp_test) # For when you need a list, not an iterator print temp_test.get_datetimes(aslist=True) # Get a list of datetimes print temp_test.get_epochs(aslist=True) # Get a list of epochs for t_ens in temp_test.get_strings("%H%M"): print t_ens print "Done!"