Source code for test_package.timerange

"""
This module provides a object that can handle a time range.
"""

from datetime import timedelta

import astropy.units as u
from astropy.time import Time, TimeDelta
from sunpy import config
from sunpy.time import is_time_equal, parse_time
from sunpy.time.time import _variables_for_parse_time_docstring
from sunpy.util.decorators import add_common_docstring

TIME_FORMAT = config.get("general", "time_format")

__all__ = ["TimeRange"]


[docs] @add_common_docstring(**_variables_for_parse_time_docstring()) class TimeRange: """ A class to create and handle time ranges. .. note:: Regardless of how a `sunpy.time.TimeRange` is constructed it will always provide a positive time range where the start time is before the end time. Parameters ---------- a : {parse_time_types} A time (the start time) specified as a parse_time-compatible time string, number, or a datetime object. b : {parse_time_types} Another time (the end time) specified as a parse_time-compatible time string, number, or a datetime object. May also be the size of the time range specified as a timedelta object, or a `~astropy.units.Quantity`. Examples -------- >>> from sunpy.time import TimeRange >>> time_range = TimeRange("2010/03/04 00:10", "2010/03/04 00:20") >>> time_range = TimeRange(("2010/03/04 00:10", "2010/03/04 00:20")) >>> import astropy.units as u >>> time_range = TimeRange("2010/03/04 00:10", 400 * u.s) >>> TimeRange("2010/03/04 00:10", 400 * u.day) <sunpy.time.timerange.TimeRange object at ...> Start: 2010-03-04 00:10:00 End: 2011-04-08 00:10:00 Center:2010-09-20 00:10:00 Duration:400.0 days or 9600.0 hours or 576000.0 minutes or 34560000.0 seconds <BLANKLINE> """ def __init__(self, a, b=None, format=None) -> None: # NOQA: A002 # If a is a TimeRange object, copy attributes to new instance. self._t1 = None self._t2 = None if isinstance(a, TimeRange): self.__dict__ = a.__dict__.copy() return # Normalize different input types if b is None: x = parse_time(a[0], format=format) if len(a) != 2: msg = "If b is None a must have two elements" raise ValueError(msg) y = a[1] else: x = parse_time(a, format=format) y = b if isinstance(y, u.Quantity): y = TimeDelta(y) if isinstance(y, timedelta): y = TimeDelta(y, format="datetime") # Timedelta if isinstance(y, TimeDelta): if y.jd >= 0: self._t1 = x self._t2 = x + y else: self._t1 = x + y self._t2 = x return # Otherwise, assume that the second argument is parse_time-compatible y = parse_time(y, format=format) if isinstance(y, Time): if x < y: self._t1 = x self._t2 = y else: self._t1 = y self._t2 = x def __hash__(self): """ Returns a fake hash. """ return 42 @property def start(self): """ Get the start time. Returns ------- `astropy.time.Time` The start time. """ return self._t1 @property def end(self): """ Get the end time. Returns ------- `astropy.time.Time` The end time. """ return self._t2 @property def dt(self): """ Get the length of the time range. Always a positive value. Returns ------- `astropy.time.TimeDelta` The difference between the start and the end time. """ return self._t2 - self._t1 @property def center(self): """ Gets the center of the time range. Returns ------- `astropy.time.Time` The center time. """ return self.start + self.dt / 2 @property def hours(self): """ Get the number of hours elapsed. Returns ------- `astropy.units.Quantity` The amount of hours between the start and end time. """ return self.dt.to("hour") @property def days(self): """ Gets the number of days elapsed. Returns ------- `astropy.units.Quantity` The amount of days between the start and end time. """ return self.dt.to("d") @property def seconds(self): """ Gets the number of seconds elapsed. Returns ------- `astropy.units.Quantity` The amount of seconds between the start and end time. """ return self.dt.to("s") @property def minutes(self): """ Gets the number of minutes elapsed. Returns ------- `astropy.units.Quantity` The amount of minutes between the start and end time. """ return self.dt.to("min") def __eq__(self, other): """ Check that two `sunpy.time.TimeRange` have the same start and end datetime. Parameters ---------- other : `~sunpy.time.timerange.TimeRange` The second `sunpy.time.TimeRange` to compare to. Returns ------- `bool` `True` if equal, `False` otherwise. """ if isinstance(other, TimeRange): return is_time_equal(self.start, other.start) and is_time_equal(self.end, other.end) return NotImplemented def __ne__(self, other): """ Check two `sunpy.time.TimeRange` have different start or end datetimes. Parameters ---------- other : `~sunpy.time.timerange.TimeRange` The second `sunpy.time.TimeRange` to compare to. Returns ------- `bool` `True` if non-equal, `False` otherwise. """ if isinstance(other, TimeRange): return not (is_time_equal(self.start, other.start) and is_time_equal(self.end, other.end)) return NotImplemented def __repr__(self) -> str: """ Returns a human-readable representation of `sunpy.time.TimeRange`. """ t1 = self.start.strftime(TIME_FORMAT) t2 = self.end.strftime(TIME_FORMAT) center = self.center.strftime(TIME_FORMAT) fully_qualified_name = f"{self.__class__.__module__}.{self.__class__.__name__}" return ( f" <{fully_qualified_name} object at {hex(id(self))}>" + "\n Start:".ljust(12) + t1 + "\n End:".ljust(12) + t2 + "\n Center:".ljust(12) + center + "\n Duration:".ljust(12) + str(self.days.value) + " days or" + "\n ".ljust(12) + str(self.hours.value) + " hours or" + "\n ".ljust(12) + str(self.minutes.value) + " minutes or" + "\n ".ljust(12) + str(self.seconds.value) + " seconds" + "\n" )
[docs] def split(self, n=2): """ Splits the time range into multiple equally sized parts. Parameters ---------- n : `int`, optional The number of times to split the time range (must >= 1). Defaults to 2. Returns ------- `list` A list of equally sized `sunpy.time.TimeRange` between the start and end times. """ if n <= 0: msg = "n must be greater than or equal to 1" raise ValueError(msg) subsections = [] previous_time = self.start next_time = None for _ in range(n): next_time = previous_time + self.dt / n next_range = TimeRange(previous_time, next_time) subsections.append(next_range) previous_time = next_time return subsections
[docs] def window(self, cadence, window): """ Split the time range up into a series of `~sunpy.time.TimeRange` that are ``window`` long, with a cadence of ``cadence``. Parameters ---------- cadence : `astropy.units.Quantity`, `astropy.time.TimeDelta` Cadence. window : `astropy.units.quantity`, `astropy.time.TimeDelta` The length of window. Returns ------- `list` A list of `~sunpy.time.TimeRange`, that are ``window`` long and separated by ``cadence``. Examples -------- >>> import astropy.units as u >>> from sunpy.time import TimeRange >>> time_range = TimeRange("2010/03/04 00:10", "2010/03/04 01:20") >>> time_range.window(60 * 60 * u.s, window=12 * u.s) # doctest: +SKIP [ <sunpy.time.timerange.TimeRange object at 0x7f0214bfc208> Start: 2010-03-04 00:10:00 End: 2010-03-04 00:10:12 Center:2010-03-04 00:10:06 Duration:0.0001388888888888889 days or 0.003333333333333333 hours or 0.2 minutes or 12.0 seconds, <sunpy.time.timerange.TimeRange object at 0x7f01fe43ac50> Start: 2010-03-04 01:10:00 End: 2010-03-04 01:10:12 Center:2010-03-04 01:10:06 Duration:0.0001388888888888889 days or 0.003333333333333333 hours or 0.2 minutes or 12.0 seconds, <sunpy.time.timerange.TimeRange object at 0x7f01fb90b898> Start: 2010-03-04 02:10:00 End: 2010-03-04 02:10:12 Center:2010-03-04 02:10:06 Duration:0.0001388888888888889 days or 0.003333333333333333 hours or 0.2 minutes or 12.0 seconds] """ # TODO: After astropy 3.1 remove this check if isinstance(window, timedelta): window = TimeDelta(window, format="datetime") if isinstance(cadence, timedelta): cadence = TimeDelta(cadence, format="datetime") if not isinstance(window, TimeDelta): window = TimeDelta(window) if not isinstance(cadence, TimeDelta): cadence = TimeDelta(cadence) n = 1 times = [TimeRange(self.start, self.start + window)] while times[-1].end < self.end: times.append(TimeRange(self.start + cadence * n, self.start + cadence * n + window)) n += 1 return times
[docs] def next(self): """ Shift the time range forward by the amount of time elapsed. """ dt = self.dt self._t1 = self._t1 + dt self._t2 = self._t2 + dt return self
[docs] def previous(self): """ Shift the time range backward by the amount of time elapsed. """ dt = self.dt self._t1 = self._t1 - dt self._t2 = self._t2 - dt return self
[docs] def extend(self, dt_start, dt_end) -> None: """ Extend the time range forwards and backwards. Parameters ---------- dt_start : `astropy.time.TimeDelta` The amount to shift the start time. dt_end : `astropy.time.TimeDelta` The amount to shift the end time. """ # TODO: Support datetime.timedelta self._t1 = self._t1 + dt_start self._t2 = self._t2 + dt_end
[docs] def get_dates(self): """ Return all partial days contained within the time range. """ return [ parse_time(self.start.strftime("%Y-%m-%d")) + TimeDelta(i * u.day) for i in range(int(self.days.value) + 1) ]
@add_common_docstring(**_variables_for_parse_time_docstring()) def __contains__(self, time) -> bool: """ Checks whether the given time lies within this range. Both limits are inclusive (i.e., ``__contains__(t1)`` and ``__contains__(t2)`` always return `True`, where ``t1, t2`` are the start and end times of the range. Parameters ---------- time : {parse_time_types} {parse_time_desc} Returns ------- bool `True` if time lies between start and end, `False` otherwise. Examples -------- >>> from sunpy.time import TimeRange >>> time1 = "2014/5/5 12:11" >>> time2 = "2012/5/5 12:11" >>> time_range = TimeRange("2014/05/04 13:54", "2018/02/03 12:12") >>> time1 in time_range True >>> time2 in time_range False """ this_time = parse_time(time) return this_time >= self.start and this_time <= self.end # Create TimeRange.contains, so it shows up in the docs. contains = __contains__