Source code for cf.timeduration

import operator

from datetime import datetime

from numpy import array     as numpy_array
from numpy import size      as numpy_size

from cfdatetime import Datetime
from .functions import equals
from .functions import inspect as cf_inspect
from .units     import Units

from .data.data import Data


# Define some useful units
_calendar_years  = Units('calendar_years')
_calendar_months = Units('calendar_months')
_days            = Units('days')
_hours           = Units('hours')
_minutes         = Units('minutes')
_seconds         = Units('seconds')


# ====================================================================
#
# TimeDuration object
#
# ====================================================================

[docs]class TimeDuration(object): '''A duration of time. The duration of time is a number of either calendar years, calender months, days, hours, minutes or seconds. **Creating time intervals** A time interval of exactly the time duration, starting or ending at a particular date-time, may be produced with the `interval` method: >>> t = cf.TimeDuration(6, 'calendar_months') >>> t <CF TimeDuration: 6 calendar_months (from Y-01-01 00:00:00)> >>> t.interval(1999, 12) (<CF Datetime: 1999-12-01 00:00:00>, <CF Datetime: 2000-06-01 00:00:00>) >>> t = cf.TimeDuration(5, 'days', hour=6) >>> t <CF TimeDuration: 5 days (from Y-01-01 06:00:00)> >>> t.interval(2004, 3, 2, end=True) (datetime.datetime(2004, 2, 26, 6, 0), <CF Datetime: 2004-03-02 06:00:00>) >>> t.interval(2004, 3, 2, end=True, calendar='noleap') (<CF Datetime: 2004-02-25 06:00:00>, <CF Datetime: 2004-03-02 06:00:00>) >>> t.interval(2004, 3, 2, end=True, calendar='360_day') (<CF Datetime: 2004-02-27 06:00:00>, <CF Datetime: 2004-03-02 06:00:00>) >>> t.interval(2004, 3, 2, end=True, calendar='360_day', iso='start and duration') '2004-02-27 06:00:00/P5D' **Arithmetic and comparison operators** Arithmetic and comparison operations are defined for `cf.TimeDuration` objects, `cf.Data` objects, `numpy` arrays and numbers: >>> cf.TimeDuration(2, 'calendar_years') > cf.TimeDuration(1, 'calendar_years') True >>> cf.TimeDuration(2, 'calendar_years') < cf.TimeDuration(25, 'calendar_months') True >>> cf.TimeDuration(2, 'hours') <= cf.TimeDuration(1, 'days') True >>> cf.TimeDuration(2, 'hours') == cf.TimeDuration(1/12.0, 'days') True >>> cf.TimeDuration(2, 'days') == cf.TimeDuration(48, 'hours') True >>> cf.TimeDuration(2, 'days') == cf.Data(2) True >>> cf.TimeDuration(2, 'days') == cf.Data([2.], 'days') True >>> cf.TimeDuration(2, 'days') > cf.Data([[60]], 'seconds') True >>> cf.TimeDuration(2, 'hours') <= 2 True >>> cf.TimeDuration(2, 'days') != 30.5 True >>> cf.TimeDuration(2, 'calendar_years') > numpy.array(1.5) True >>> cf.TimeDuration(2, 'calendar_months') < numpy.array([[12]]) True >>> cf.TimeDuration(30, 'days') + 2 <CF TimeDuration: 32 days (from Y-01-01 00:00:00)> >>> cf.TimeDuration(64, 'calendar_years') - 2.5 <CF TimeDuration: 61.5 calendar_years (from Y-01-01 00:00:00)> >>> cf.TimeDuration(64, 'calendar_years') + cf.TimeDuration(23, 'calendar_months') <CF TimeDuration: 65.9166666667 calendar_years (from Y-01-01 00:00:00)> >>> cf.TimeDuration(36, 'hours') / numpy.array(8) <CF TimeDuration: 4 hours (from Y-01-01 00:00:00)> >>> cf.TimeDuration(36, 'hours') / numpy.array(8.0) <CF TimeDuration: 4.5 hours (from Y-01-01 00:00:00)> >>> cf.TimeDuration(36, 'hours') // numpy.array(8.0) <CF TimeDuration: 4.0 hours (from Y-01-01 00:00:00)> >>> cf.TimeDuration(36, 'calendar_months') * cf.Data([[2.25]]) <CF TimeDuration: 81.0 calendar_months (from Y-01-01 00:00:00)> >>> cf.TimeDuration(36, 'calendar_months') // cf.Data([0.825], '10') <CF TimeDuration: 4.3 calendar_months (from Y-01-01 00:00:00)> >>> cf.TimeDuration(36, 'calendar_months') % 10 <CF Data: 6 calendar_months> >>> cf.TimeDuration(36, 'calendar_months') % cf.Data(1, 'calendar_year') <CF Data: 0.0 calendar_months> >>> cf.TimeDuration(36, 'calendar_months') % cf.Data(2, 'calendar_year') <CF Data: 12.0 calendar_months> The in place operators (``+=``, ``//=``, etc.) are supported in a similar manner. **Attributes** =========== ========================================================= Attribute Description =========== ========================================================= `!duration` The length of the time duration in a `cf.Data` object with units. `!year` The default year for time interval creation. `!month` The default month for time interval creation. `!day` The default day for time interval creation. `!hour` The default hour for time interval creation. `!minute` The default minute for time interval creation. `!second` The default second for time interval creation. =========== ========================================================= **Constructors** For convenience, the following functions may also be used to create time duration objects: ======== ============================================================ Function Description ======== ============================================================ `cf.Y` Create a time duration of calendar years. `cf.M` Create a time duration of calendar months. `cf.D` Create a time duration of days. `cf.h` Create a time duration of hours. `cf.m` Create a time duration of minutes. `cf.s` Create a time duration of seconds. ======== ============================================================ .. seealso:: `cf.Data`, `cf.Datetime` .. versionadded:: 1.0 '''
[docs] def __init__(self, duration, units=None, year=None, month=1, day=1, hour=0, minute=0, second=0): '''**Initialization** :Parameters: duration : data-like The length of the time duration. units : str, optional The units of the time duration. Only required if *duration* is not a `cf.Data` object which contains the units. Must be one a units string equivalent to calendar years, calendar months, days, hours, minutes or seconds. year, month, day, hour, minute, second : ints or None, optional The default date-time elements for defining when a time interval based on this time duration (created with the `interval` method) begins or ends. See `cf.TimeDuration.interval` for details. :Examples: >>> t = cf.TimeDuration(cf.Data(3 , 'calendar_years')) >>> t = cf.TimeDuration(cf.Data(12 , 'hours')) >>> t = cf.TimeDuration(18 , 'calendar_months') >>> t = cf.TimeDuration(30 , 'days') >>> t = cf.TimeDuration(1 , 'day', hour=6) ''' if units is not None: units = Units(units) self.duration = Data(abs(duration), units) else: self.duration = abs(Data.asdata(duration)) units = self.duration.Units if not (units.iscalendartime or units.istime): raise ValueError( "Can't create %s of %s" % (self.__class__.__name__, self.duration)) self.year = year self.month = month self.day = day self.hour = hour self.minute = minute self.second = second
#--- End: def def __array__(self, *dtype): ''' ''' return self.duration.__array__(*dtype) #--- End: def def __data__(self): ''' Returns a new reference to self.duration. ''' return self.duration #--- End: def def __deepcopy__(self, memo): ''' Used if copy.deepcopy is called ''' return self.copy() #--- End: def def __nonzero__(self): ''' Truth value testing and the built-in operation `bool` x.__nonzero__() <==> x != 0 ''' return bool(self.duration) #--- End: if def __float__(self): ''' x.__float__() <==> float(x) ''' return float(self.duration) #--- End: def def __int__(self): ''' x.__int__() <==> int(x) ''' return int(self.duration) #--- End: def def __repr__(self): ''' x.__repr__() <==> repr(x) ''' return '<CF %s: %s>' % (self.__class__.__name__, str(self)) #--- End: def def __str__(self): ''' x.__str__() <==> str(x) ''' year = self.year month = self.month day = self.day hour = self.hour minute = self.minute second = self.second year = 'Y' if year is None else '%d' % year month = 'MM' if month is None else '%02d' % month day = 'DD' if day is None else '%02d' % day hour = 'hh' if hour is None else '%02d' % hour minute = 'mm' if minute is None else '%02d' % minute second = 'ss' if second is None else '%02d' % second return ('%s (from %s-%s-%s %s:%s:%s)' % (self.duration, year, month, day, hour, minute, second)) #--- End: def def __ge__(self, other): ''' The rich comparison operator ``>=`` x__ge__(y) <==> x>=y ''' return bool(self.duration >= other) #--- End: def def __gt__(self, other): ''' The rich comparison operator ``>`` x__gt__(y) <==> x>y ''' return bool(self.duration > other) #--- End: def def __le__(self, other): ''' The rich comparison operator ``<=`` x__le__(y) <==> x<=y ''' return bool(self.duration <= other) #--- End: def def __lt__(self, other): ''' The rich comparison operator ``<`` x__lt__(y) <==> x<y ''' return bool(self.duration < other) #--- End: def def __eq__(self, other): ''' The rich comparison operator ``==`` x__eq__(y) <==> x==y ''' return bool(self.duration == other) #--- End: def def __ne__(self, other): ''' The rich comparison operator ``!=`` x__ne__(y) <==> x!=y ''' return bool(self.duration != other) #--- End: def def __add__(self, other): ''' The binary arithmetic operation ``+`` x.__add__(y) <==> x + y ''' return self._binary_arithmetic(other, '__add__') #--- End: def def __sub__(self, other): ''' The binary arithmetic operation ``-`` x.__sub__(y) <==> x - y ''' return self._binary_arithmetic(other, '__sub__') #--- End: def def __mul__(self, other): ''' The binary arithmetic operation ``*`` x.__mul__(y) <==> x * y ''' return self._binary_arithmetic(other, '__mul__') #--- End: def def __div__(self, other): ''' The binary arithmetic operation ``/`` x.__div__(y) <==> x / y ''' return self._binary_arithmetic(other, '__div__') #--- End: def def __floordiv__(self, other): ''' The binary arithmetic operation ``//`` x.__floordiv__(y) <==> x // y ''' return self._binary_arithmetic(other, '__floordiv__') #--- End: def def __truediv__(self, other): ''' The binary arithmetic operation ``/`` (true division) x.__truediv__(y) <==> x / y ''' return self._binary_arithmetic(other, '__truediv__') #--- End: def def __pow__(self, other, modulo=None): ''' The binary arithmetic operations ``**`` and ``pow`` x.__pow__(y) <==> x**y ''' if modulo is not None: raise NotImplementedError("3-argument power not supported for '%s'" % self.__class__.__name__) return self._binary_arithmetic(other, '__pow__') #--- End: def def __rpow__(self, other, modulo=None): ''' The binary arithmetic operations ``**`` and ``pow`` with reflected operands x.__rpow__(y) <==> y**x ''' if modulo is not None: raise NotImplementedError("3-argument power not supported for '%s'" % self.__class__.__name__) return self._binary_arithmetic(other, '__rpow__') #--- End: def def __iadd__(self, other): ''' The augmented arithmetic assignment ``+=`` x.__iadd__(y) <==> x += y ''' return self._binary_arithmetic(other, '__iadd__', True) #--- End: def def __idiv__(self, other): ''' The augmented arithmetic assignment ``/=`` x.__idiv__(y) <==> x /= y ''' return self._binary_arithmetic(other, '__idiv__', True) #--- End: def def __itruediv__(self, other): ''' The augmented arithmetic assignment ``/=`` (true division) x.__truediv__(y) <==> x/y ''' return self._binary_arithmetic(other, '__itruediv__', True) #--- End: def def __ifloordiv__(self, other): ''' The augmented arithmetic assignment ``//=`` x.__ifloordiv__(y) <==> x//=y ''' return self._binary_arithmetic(other, '__ifloordiv__', True) #--- End: if def __imul__(self, other): ''' The augmented arithmetic assignment ``*=`` x.__imul__(y) <==> x *= y ''' return self._binary_arithmetic(other, '__imul__', True) #--- End: def def __isub__(self, other): ''' The augmented arithmetic assignment ``-=`` x.__isub__(y) <==> x -= y ''' return self._binary_arithmetic(other, '__isub__', True) #--- End: def def __imod__(self, other): ''' The augmented arithmetic assignment ``%=`` x.__imod__(y) <==> x %= y ''' return NotImplemented #--- End: def def __ipow__(self, other, modulo=None): ''' The augmented arithmetic assignment ``**=`` x.__ipow__(y) <==> x**=yyyy ''' return self._binary_arithmetic(other, '__ipow__', True) #--- End: def def __iadd__(self, other): ''' The augmented arithmetic assignment ``+=`` x.__iadd__(y) <==> x += y ''' return self._binary_arithmetic(other, '__iadd__', True) #--- End: def def __idiv__(self, other): ''' The augmented arithmetic assignment ``/=`` x.__idiv__(y) <==> x /= y ''' return self._binary_arithmetic(other, '__idiv__', True) #--- End: def def __itruediv__(self, other): ''' The augmented arithmetic assignment ``/=`` (true division) x.__truediv__(y) <==> x/y ''' return self._binary_arithmetic(other, '__itruediv__', True) #--- End: def def __ifloordiv__(self, other): ''' The augmented arithmetic assignment ``//=`` x.__ifloordiv__(y) <==> x//=y ''' return self._binary_arithmetic(other, '__ifloordiv__', True) #--- End: if def __imul__(self, other): ''' The augmented arithmetic assignment ``*=`` x.__imul__(y) <==> x *= y ''' return self._binary_arithmetic(other, '__imul__', True) #--- End: def def __radd__(self, other): ''' The binary arithmetic operation ``+`` with reflected operands x.__radd__(y) <==> y+x ''' return self._binary_arithmetic(other, '__add__') #--- End: def def __rmul__(self, other): ''' The binary arithmetic operation ``*`` with reflected operands x.__rmul__(y) <==> y*x ''' return self._binary_arithmetic(other, '__rmul__') #--- End: def def __rsub__(self, other): ''' The binary arithmetic operation ``-`` with reflected operands x.__rsub__(y) <==> y-x ''' return self._binary_arithmetic(other, '__rsub__') #--- End: def def __mod__(self, other): ''' The binary arithmetic operation ``%`` x.__mod__(y) <==> x % y ''' return self.duration % other #--- End: def def __rmod__(self, other): ''' The binary arithmetic operation ``%`` with reflected operands x.__rmod__(y) <==> y % x ''' return other % self.duration #--- End: def # ---------------------------------------------------------------- # Attribute # ---------------------------------------------------------------- def _binary_arithmetic(self, other, method, inplace=False): ''' ''' if inplace: new = self else: new = self.copy() duration = getattr(new.duration, method)(other) duration.squeeze(i=True) if duration.size != 1: raise ValueError("Can't create %s with more than one value: %s" % (self.__class__.__name__, duration)) if duration < 0: raise ValueError("Can't create %s with a negative duration" % self.__class__.__name__) units = duration.Units if not (units.iscalendartime or units.istime): raise ValueError("Can't create %s of %r" % (self.__class__.__name__, units)) duration.Units = self.Units new.duration = duration return new #--- End: def # ---------------------------------------------------------------- # Attribute (read only) # ---------------------------------------------------------------- @property def iso(self): '''Return the time duration as an ISO 8601 time duration string. .. versionadded:: 1.0 :Examples: >>> cf.TimeDuration(45, 'days').iso 'P45D' >>> cf.TimeDuration(5, 'seconds').iso 'PT5S' >>> cf.TimeDuration(10, 'calendar_years').iso 'P10Y' >>> cf.TimeDuration(18, 'calendar_months').iso 'P18M' ''' duration = self.duration units = duration.Units if units == _calendar_months: return 'P%dM' % duration.datum() if units == _calendar_years: return 'P%dY' % duration.datum() if units == _days: return 'P%sD' % duration.datum() if units == _hours: return 'PT%sH' % duration.datum() if units == _minutes: return 'PT%sM' % duration.datum() if units == _seconds: return 'PT%sS' % duration.datum() raise ValueError("jkbn p7hg un") #--- End: def # ---------------------------------------------------------------- # Attribute (read only) # ---------------------------------------------------------------- @property def isint(self): '''True if the time duration is a whole number. .. versionadded:: 1.0 :Examples: >>> cf.TimeDuration(2, 'hours').isint True >>> cf.TimeDuration(2.0, 'hours').isint True >>> cf.TimeDuration(2.5, 'hours').isint False ''' duration = self.duration if duration.dtype.kind == 'i': return True duration = duration.datum() return int(duration) == float(duration) #--- End: def # ---------------------------------------------------------------- # Attribute # ---------------------------------------------------------------- @property def Units(self): '''The units of the time duration. .. versionadded:: 1.0 :Examples: >>> cf.TimeDuration(3, 'days').Units <CF Units: days> >>> t = cf.TimeDuration(cf.Data(12, 'calendar_months')) >>> t.Units <CF Units: calendar_months> >>> t.Units = cf.Units('calendar_years') >>> t.Units <CF Units: calendar_years> >>> t <CF TimeDuration: 1.0 calendar_years (from Y-01-01 00:00:00)> ''' return self.duration.Units #--- End: def @Units.setter def Units(self, value): duration = getattr(self, 'duration', None) if duration is None: raise AttributeError("Can't set units when there is no duration attribute") self.duration.Units = value #--- End: def
[docs] def copy(self): ''' Return a deep copy. ``t.copy()`` is equivalent to ``copy.deepcopy(t)``. .. versionadded:: 1.0 :Returns: out : The deep copy. :Examples: >>> u = t.copy() ''' new = TimeDuration.__new__(TimeDuration) new.year = self.year new.month = self.month new.day = self.day new.hour = self.hour new.minute = self.minute new.second = self.second new.duration = self.duration.copy() return new
#--- End: def
[docs] def equals(self, other, rtol=None, atol=None, traceback=False): ''' True if two time durations are equal. .. seealso:: `equivalent` .. versionadded:: 1.0 :Parameters: other : The object to compare for equality. atol : float, optional The absolute tolerance for all numerical comparisons, By default the value returned by the `ATOL` function is used. rtol : float, optional The relative tolerance for all numerical comparisons, By default the value returned by the `RTOL` function is used. traceback : bool, optional If True then print a traceback highlighting where the two instances differ. :Returns: out : bool Whether or not the two instances are equal. :Examples: >>> t = cf.TimeDuration(36, 'calendar_months') >>> u = cf.TimeDuration(3, 'calendar_years') >>> t == u True >>> t.equals(u, traceback=True) TimeDuration: Different durations: <CF Data: 36 calendar_months>, <CF Data: 3 calendar_years> False ''' # Check each instance's id if self is other: return True # Check that each instance is the same type if self.__class__ != other.__class__: if traceback: print("%s: Different type: %s" % (self.__class__.__name__, other.__class__.__name__)) return False #--- End: if self__dict__ = self.__dict__.copy() other__dict__ = other.__dict__.copy() d0 = self__dict__.pop('duration', None) d1 = other__dict__.pop('duration', None) if not equals(d0, d1): if traceback: print("%s: Different durations: %r, %r" % (self.__class__.__name__, d0, d1)) return False #--- End: if if self__dict__ != other__dict__: if traceback: print("%s: Different The default date-time elements: %r != %r" % (self.__class__.__name__, self__dict__, other__dict__)) return False #--- End: if return True
#--- End: def
[docs] def equivalent(self, other, rtol=None, atol=None, traceback=False): ''' True if two time durations are logically equivalent. .. seealso:: `equals` .. versionadded:: 1.0 :Parameters: other : The object to compare for equivalence. atol : float, optional The absolute tolerance for all numerical comparisons, By default the value returned by the `ATOL` function is used. rtol : float, optional The relative tolerance for all numerical comparisons, By default the value returned by the `RTOL` function is used. traceback : bool, optional If True then print a traceback highlighting where the two instances differ. :Returns: out : bool Whether or not the two instances logically equivalent. :Examples: >>> t = cf.TimeDuration(36, 'calendar_months') >>> u = cf.TimeDuration(3, 'calendar_years') >>> t == u True >>> t.equivalent(u) True >>> t.equals(u, traceback=True) TimeDuration: Different durations: <CF Data: 12 calendar_months>, <CF Data: 1 calendar_years> False ''' # Check each instance's id if self is other: return True # Check that each instance is the same type if self.__class__ != other.__class__: if traceback: print("%s: Different type: %s" % (self.__class__.__name__, other.__class__.__name__)) return False #--- End: if self__dict__ = self.__dict__.copy() other__dict__ = other.__dict__.copy() d0 = self__dict__.pop('duration', None) d1 = other__dict__.pop('duration', None) if d0 != d0: if traceback: print("%s: Non-equivalent durations: %r, %r" % (self.__class__.__name__, d0, d1)) return False #--- End: if if self__dict__ != other__dict__: if traceback: print("%s: Non-equivalent default date-time elements: %r != %r" % (self.__class__.__name__, self__dict__, other__dict__)) return False #--- End: if return True
#--- End: def
[docs] def inspect(self): ''' Inspect the attributes. .. seealso:: `cf.inspect` .. versionadded:: 1.0 :Returns: None :Examples: >>> t=cf.TimeDuration(9, 'days') >>> t.inspect() <CF TimeDuration: 9 days (from Y-01-01 00:00:00)> ------------------------------------------------- day: 1 duration: <CF Data: 9 days> hour: 0 minute: 0 month: 1 second: 0 year: None ''' print cf_inspect(self)
#--- End: def
[docs] def interval(self, year=None, month=None, day=None, hour=None, minute=None, second=None, end=False, calendar=None, iso=None): '''Return a time interval of exactly the time duration. The start (or end, if the *end* parameter is True) date-time of the time interval is determined by the `!year`, `!month`, `!day`, `!hour`, `!minute` and `!second` attributes. .. versionadded:: 1.0 :Parameters: year, month, day, hour, minute, second : ints, optional The date-time of the start (or end) of the time interval. If any parameter is unset then its value defaults to the attribute of the same name. The time interval calculation requires that all of the parameters have numerical values, so if an unset parameter has a corresponding attribute default which is `None` then an exception will be raised. *Example:* ``t.interval(year=1999, day=16)`` is equivalent to ``t.interval(1999, t.month, 16, t.hour, t.minute, t.second)``. end : bool, optional If True then the date-time given by the *year*, *month*, *day*, *hour*, *minute* and *second* parameters defines the end of the time interval. By default it defines the start of the time interval. calendar : str, optional Define a CF calendar for the time interval. By default the Gregorian calendar is assumed. Ignored for time durations of calendar years or calendar months. iso : string, optional Return the time interval as an ISO 8601 time interval string rather than the default of a tuple of date-time objects. Valid values are (with example outputs for the time interval "3 years from 2007-03-01 13:00:00"): ======================== ============================================= iso Example output ======================== ============================================= ``'start and end'`` ``'2007-03-01 13:00:00/2010-03-01 13:00:00'`` ``'start and duration'`` ``'2007-03-01 13:00:00/P3Y'`` ``'duration and end'`` ``'P3Y/2010-03-01 13:00:00'`` ======================== ============================================= :Returns: out : 2-tuple of cf.Datetime or datetime.datetime; or str The date-times at each end of the time interval. The first date-time is always earlier than or equal to the second date-time. If *iso* has been set then an ISO 8601 time interval string is returned instead of a tuple. :Examples: >>> cf.TimeDuration(1, 'calendar_months').interval(1999, 12) (<CF Datetime: 1999-12-01 00:00:00>, <CF Datetime: 2000-01-01 00:00:00>) >>> cf.TimeDuration(2, 'calendar_years').interval(2000, 2, end=True) (<CF Datetime: 1998-02-01 00:00:00>, <CF Datetime: 2000-02-01 00:00:00>) >>> cf.TimeDuration(30, 'days').interval(1983, 12, 1, 6) (<CF Datetime: 1983-12-01 06:00:00>, <CF Datetime: 1983-12-31 06:00:00>) >>> cf.TimeDuration(30, 'days').interval(1983, 12, 1, 6, end=True) (<CF Datetime: 1983-11-01 06:00:00>, <CF Datetime: 1983-12-01 06:00:00>) >>> cf.TimeDuration(0, 'days').interval(1984, 2, 3) (<CF Datetime: 1984-02-03 00:00:00>, <CF Datetime: 1984-02-03 00:00:00>) >>> cf.TimeDuration(5, 'days', hour=6).interval(2004, 3, 2, end=True) (<CF Datetime: 2004-02-26 06:00:00>, <CF Datetime: 2004-03-02 06:00:00>) >>> cf.TimeDuration(5, 'days', hour=6).interval(2004, 3, 2, end=True, calendar='noleap') (<CF Datetime: 2004-02-25 06:00:00>, <CF Datetime: 2004-03-02 06:00:00>) >>> cf.TimeDuration(5, 'days', hour=6).interval(2004, 3, 2, end=True, calendar='360_day') (<CF Datetime: 2004-02-27 06:00:00>, <CF Datetime: 2004-03-02 06:00:00>) >>> cf.TimeDuration(19897.546, 'hours').interval(1984, 2, 3, 0) (<CF Datetime: 1984-02-03 00:00:00>, <CF Datetime: 1986-05-12 01:32:46>) >>> cf.TimeDuration(19897.546, 'hours').interval(1984, 2, 3, 0, end=True) (<CF Datetime: 1981-10-26 22:27:14>, <CF Datetime: 1984-02-03 00:00:00>) Create `cf.Query` objects for a time interval - one including both bounds and one which excludes the upper bound: >>> t = cf.TimeDuration(2, 'calendar_years') >>> interval = t.interval(1999, 12) >>> c = cf.wi(*interval) >>> c <CF Query: (wi [<CF Datetime: 1999-12-01 00:00:00>, <CF Datetime: 2001-01-01 00:00:00>])> >>> d = cf.ge(interval[0]) & cf.lt(interval[1]) >>> d <CF Query: [(ge <CF Datetime: 1999-12-01 00:00:00>) & (lt <CF Datetime: 2000-01-01 00:00:00>)]> >>> c == cf.dt('2001-1-1') True >>> d == cf.dt('2001-1-1') False Create a `cf.Query` object which may be used to test where a time coordinate object's bounds lie inside a time interval: >>> t = cf.TimeDuration(1, 'calendar_months') >>> c = cf.cellwi(*t.interval(2000, 1, end=True)) >>> c <CF Query: [lower_bounds(ge <CF Datetime: 1999-12-01 00:00:00>) & upper_bounds(le <CF Datetime: 2000-01-01 00:00:00>)]> Create ISO 8601 time interval strings: >>> t = cf.TimeDuration(6, 'calendar_years') >>> t.interval(1999, 12, end=True, iso='start and end') '1993-12-01 00:00:00/1999-12-01 00:00:00' >>> t.interval(1999, 12, end=True, iso='start and duration') '1993-12-01 00:00:00/P6Y' >>> t.interval(1999, 12, end=True, iso='duration and end') 'P6Y/1999-12-01 00:00:00' ''' def _dHMS(duration, year, month, day, hour, minute, second, calendar): dt = Datetime(year, month, day, hour, minute, second) units = Units('%s since %s' % (duration.Units, dt), calendar) dt1 = Data(0.0, units) if not end: dt1 += duration else: dt1 -= duration dt1 = dt1.asdatetime().datum() if not end: return dt, dt1 else: return dt1, dt #--- End: def if year is None: year = self.year if year is None: raise ValueError( "year must have a value when creating a time interval") if month is None: month = self.month if month is None: raise ValueError( "month must have a value when creating a time interval") if day is None: day = self.day if day is None: raise ValueError( "day must have a value when creating a time interval") if hour is None: hour = self.hour if hour is None: raise ValueError( "hour must have a value when creating a time interval") if minute is None: minute = self.minute if minute is None: raise ValueError( "minute must have a value when creating a time interval") if second is None: second = self.second if second is None: raise ValueError( "second must have a value when creating a time interval") duration = self.duration units = duration.Units if units == _calendar_years: months = duration.datum() * 12 int_months = int(months) if int_months != months: raise ValueError( "Can't create a time interval of a non-integer number of calendar months: %s" % months) elif units == _calendar_months: months = duration.datum() int_months = int(months) if int_months != months: raise ValueError( "Can't create a time interval of a non-integer number of calendar months: %s" % months) else: int_months = None if int_months is not None: if not end: y, month1 = divmod(month + int_months, 12) if not month1: y -= 1 month1 = 12 year1 = year + y dt0 = Datetime(year , month , day, hour, minute, second) dt1 = Datetime(year1, month1, day, hour, minute, second) else: y, month0 = divmod(month - int_months, 12) if not month0: y -= 1 month0 = 12 year0 = year + y dt0 = Datetime(year0, month0, day, hour, minute, second) dt1 = Datetime(year , month , day, hour, minute, second) elif units == _days: dt0, dt1 = _dHMS(duration, year, month, day, hour, minute, second, calendar) elif units == _hours: dt0, dt1 = _dHMS(duration, year, month, day, hour, minute, second, calendar) elif units == _minutes: dt0, dt1 = _dHMS(duration, year, month, day, hour, minute, second, calendar) elif units == _seconds: dt0, dt1 = _dHMS(duration, year, month, day, hour, minute, second, calendar) if not iso: return dt0, dt1 if iso == 'start and end': return '%s/%s' % (dt0, dt1) if iso == 'start and duration': return '%s/%s' % (dt0, self.iso) if iso == 'duration and end': return '%s/%s' % (self.iso, dt1)
#--- End: def
[docs] def is_day_factor(self): ''' :Returns: out : bool .. versionadded:: 1.0 :Examples: >>> cf.TimeDuration(1, 'days').is_day_factor() True >>> cf.TimeDuration(0.25, 'days').is_day_factor() True >>> cf.TimeDuration(0.3, 'days').is_day_factor() False >>> cf.TimeDuration(2, 'days').is_day_factor() False >>> cf.TimeDuration(24, 'hours').is_day_factor() True >>> cf.TimeDuration(6, 'hours').is_day_factor() True >>> cf.TimeDuration(7, 'hours').is_day_factor() False >>> cf.TimeDuration(27, 'hours').is_day_factor() >>> cf.TimeDuration(1440, 'minutes').is_day_factor() True >>> cf.TimeDuration(15, 'minutes').is_day_factor() True >>> cf.TimeDuration(17, 'minutes').is_day_factor() False >>> cf.TimeDuration(2007, 'minutes').is_day_factor() False >>> cf.TimeDuration(86400, 'seconds').is_day_factor() True >>> cf.TimeDuration(45, 'seconds').is_day_factor() True >>> cf.TimeDuration(47, 'seconds').is_day_factor() False >>> cf.TimeDuration(86401, 'seconds').is_day_factor() False >>> cf.TimeDuration(1, 'calendar_months').is_day_factor() False >>> cf.TimeDuration(1, 'calendar_years').is_day_factor() False ''' try: return not cf.Data(1, 'day') % self.duration except ValueError: return False
#--- End: class
[docs]def Y(duration=1, year=None, month=1, day=1, hour=0, minute=0, second=0): '''Return a time duration of calendar years in a `cf.TimeDuration` object. ``cf.Y()`` is equivalent to ``cf.TimeDuration(1, 'calendar_year')``. .. seealso:: `cf.M`, `cf.D`, `cf.h`, `cf.m`, `cf.s` .. versionadded:: 1.0 :Parameters: duration : int, optional The number of calendar years in the time duration. Must be a non-negative integer. year, month, day, hour, minute, second : ints, optional The default date-time elements for defining the start and end of a time interval based on this time duration. See `cf.TimeDuration` and `cf.TimeDuration.interval` for details. *Example:* ``cf.Y(month=12)`` is equivalent to ``cf.TimeDuration(1, 'calendar_years', month=12)``. :Returns: out : cf.TimeDuration The new `cf.TimeDuration` object. :Examples: >>> cf.Y() <CF TimeDuration: 1 calendar year (from Y-01-01 00:00:00)> >>> cf.Y(10, month=12) <CF TimeDuration: 10 calendar years (from Y-12-01 00:00:00)> >>> cf.Y(15, month=4, day=2, hour=12, minute=30, second=2) <CF TimeDuration: 15 calendar years (from Y-04-02 12:30:02)> >>> cf.Y(0) <CF TimeDuration: 0 calendar years (from Y-01-01 00:00:00)> ''' return TimeDuration(duration, 'calendar_years', year=year, month=month, day=day, hour=hour, minute=minute, second=second)
#--- End: def
[docs]def M(duration=1, year=None, month=1, day=1, hour=0, minute=0, second=0): '''Return a time duration of calendar months in a `cf.TimeDuration` object. ``cf.M()`` is equivalent to ``cf.TimeDuration(1, 'calendar_month')``. .. seealso:: `cf.Y`, `cf.D`, `cf.h`, `cf.m`, `cf.s` .. versionadded:: 1.0 :Parameters: duration : int, optional The number of calendar months in the time duration. Must be a non-negative integer. year, month, day, hour, minute, second : ints, optional The default date-time elements for defining the start and end of a time interval based on this time duration. See `cf.TimeDuration` and `cf.TimeDuration.interval` for details. *Example:* ``cf.M(day=16)`` is equivalent to ``cf.TimeDuration(1, 'calendar_months', day=16)``. :Returns: out : cf.TimeDuration The new `cf.TimeDuration` object. :Examples: >>> cf.M() <CF TimeDuration: 1 calendar month (from Y-MM-01 00:00:00)> >>> cf.M(3, day=16) <CF TimeDuration: 3 calendar months (from Y-MM-16 00:00:00)> >>> cf.M(24, day=2, hour=12, minute=30, second=2) <CF TimeDuration: 24 calendar months (from Y-MM-02 12:30:02)> >>> cf.M(0) <CF TimeDuration: 0 calendar months (from Y-MM-01 00:00:00)> ''' return TimeDuration(duration, 'calendar_months', year=year, month=month, day=day, hour=hour, minute=minute, second=second)
#--- End: def
[docs]def D(duration=1, year=None, month=1, day=1, hour=0, minute=0, second=0): '''Return a time duration of days in a `cf.TimeDuration` object. ``cf.D()`` is equivalent to ``cf.TimeDuration(1, 'day')``. .. seealso:: `cf.Y`, `cf.M`, `cf.h`, `cf.m`, `cf.s` .. versionadded:: 1.0 :Parameters: duration : number, optional The number of days in the time duration. Must be non-negative. year, month, day, hour, minute, second : ints, optional The default date-time elements for defining the start and end of a time interval based on this time duration. See `cf.TimeDuration` and `cf.TimeDuration.interval` for details. *Example:* ``cf.D(hour=12)`` is equivalent to ``cf.TimeDuration(1, 'day', hour=12)``. :Returns: out : cf.TimeDuration The new `cf.TimeDuration` object. :Examples: >>> cf.D() <CF TimeDuration: 1 day (from Y-MM-DD 00:00:00)> >>> cf.D(5, hour=12) <CF TimeDuration: 5 days (from Y-MM-DD 12:00:00)> >>> cf.D(48.5, minute=30) <CF TimeDuration: 48.5 days (from Y-MM-DD 00:30:00)> >>> cf.D(0.25, hour=6, minute=30, second=20) <CF TimeDuration: 0.25 days (from Y-MM-DD 06:30:20)> >>> cf.D(0) <CF TimeDuration: 0 days (from Y-MM-DD 00:00:00)> ''' return TimeDuration(duration, 'days', year=year, month=month, day=day, hour=hour, minute=minute, second=second)
#--- End: def
[docs]def h(duration=1, year=None, month=1, day=1, hour=0, minute=0, second=0): '''Return a time duration of hours in a `cf.TimeDuration` object. ``cf.h()`` is equivalent to ``cf.TimeDuration(1, 'hour')``. .. seealso:: `cf.Y`, `cf.M`, `cf.D`, `cf.m`, `cf.s` .. versionadded:: 1.0 :Parameters: duration : number, optional The number of hours in the time duration. Must be non-negative. year, month, day, hour, minute, second : ints, optional The default date-time elements for defining the start and end of a time interval based on this time duration. See `cf.TimeDuration` and `cf.TimeDuration.interval` for details. *Example:* ``cf.h(minute=30)`` is equivalent to ``cf.TimeDuration(1, 'hour', minute=30)``. :Returns: out : cf.TimeDuration The new `cf.TimeDuration` object. :Examples: >>> cf.h() <CF TimeDuration: 1 hour (from Y-MM-DD hh:00:00)> >>> cf.h(3, minute=15) <CF TimeDuration: 3 hours (from Y-MM-DD hh:15:00)> >>> cf.h(0.5) <CF TimeDuration: 0.5 hours (from Y-MM-DD hh:00:00)> >>> cf.h(6.5, minute=15, second=45) <CF TimeDuration: 6.5 hours (from Y-MM-DD hh:15:45)> >>> cf.h(0) <CF TimeDuration: 0 hours (from Y-MM-DD hh:00:00)> ''' return TimeDuration(duration, 'hours', year=year, month=month, day=day, hour=hour, minute=minute, second=second)
#--- End: def
[docs]def m(duration=1, year=None, month=1, day=1, hour=0, minute=0, second=0): '''Return a time duration of minutes in a `cf.TimeDuration` object. ``cf.m()`` is equivalent to ``cf.TimeDuration(1, 'minute')``. .. seealso:: `cf.Y`, `cf.M`, `cf.D`, `cf.h`, `cf.s` .. versionadded:: 1.0 :Parameters: duration : number, optional The number of hours in the time duration. Must be non-negative. year, month, day, hour, minute, second : ints, optional The default date-time elements for defining when a time interval based on this time duration begins or ends. See `cf.TimeDuration` and `cf.TimeDuration.interval` for details. *Example:* ``cf.m(second=30)`` is equivalent to ``cf.TimeDuration(1, 'minute', second=30)``. :Returns: out : cf.TimeDuration The new `cf.TimeDuration` object. :Examples: >>> cf.m() <CF TimeDuration: 1 minute (from Y-MM-DD hh:mm:00)> >>> cf.m(30, second=15) <CF TimeDuration: 30 minutes (from Y-MM-DD hh:mm:15)> >>> cf.m(0.5) <CF TimeDuration: 0.5 minutes (from Y-MM-DD hh:mm:00)> >>> cf.m(2.5, second=45) <CF TimeDuration: 2.5 minutes (from Y-MM-DD hh:mm:45)> >>> cf.m(0) <CF TimeDuration: 0 minutes (from Y-MM-DD hh:mm:00)> ''' return TimeDuration(duration, 'minutes', year=year, month=month, day=day, hour=hour, minute=minute, second=second)
#--- End: def
[docs]def s(duration=1, year=None, month=1, day=1, hour=0, minute=0, second=0): '''Return a time duration of seconds in a `cf.TimeDuration` object. ``cf.s()`` is equivalent to ``cf.TimeDuration(1, 'second')``. .. seealso:: `cf.Y`, `cf.M`, `cf.D`, `cf.h`, `cf.m` .. versionadded:: 1.0 :Parameters: duration : number, optional The number of hours in the time duration. Must be non-negative. year, month, day, hour, minute, second : ints, optional The default date-time elements for defining the start and end of a time interval based on this time duration. See `cf.TimeDuration` and `cf.TimeDuration.interval` for details. *Example:* ``cf.s(hour=6)`` is equivalent to ``cf.TimeDuration(1, 'seconds', hour=6)``. :Returns: out : cf.TimeDuration The new `cf.TimeDuration` object. :Examples: >>> cf.s() <CF TimeDuration: 1 second (from Y-01-01 00:00:00)> >>> cf.s().interval(1999, 12, 1) (<CF Datetime: 1999-12-01 00:00:00>, datetime.datetime(1999, 12, 1, 0, 0, 1)) >>> cf.s(30) <CF TimeDuration: 30 seconds (from Y-01-01 00:00:00)> >>> cf.s(0.5) <CF TimeDuration: 0.5 seconds (from Y-01-01 00:00:00)> >>> cf.s(12.25) <CF TimeDuration: 12.25 seconds (from Y-01-01 00:00:00)> >>> cf.s(2.5, year=1999, hour=12) <CF TimeDuration: 2.5 seconds (from 1999-01-01 12:00:00)> >>> cf.s(0) <CF TimeDuration: 0 seconds (from Y-01-01 00:00:00)> ''' return TimeDuration(duration, 'seconds', year=year, month=month, day=day, hour=hour, minute=minute, second=second)
#--- End: def