Source code for pyndn.encrypt.repetitive_interval

# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
#
# Copyright (C) 2015-2016 Regents of the University of California.
# Author: Jeff Thompson <jefft0@remap.ucla.edu>
# Author: From ndn-group-encrypt src/repetitive-interval https://github.com/named-data/ndn-group-encrypt
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# A copy of the GNU Lesser General Public License is in the file COPYING.

"""
This module defines the RepetitiveInterval class which is an advanced interval
which can repeat and can be used to find a simple Interval that a time point
falls in.
Note: This class is an experimental feature. The API may change.
"""

import sys
from datetime import datetime
from pyndn.encrypt.interval import Interval

[docs]class RepetitiveInterval(object): """ Create a RepetitiveInterval with one of these forms: RepetitiveInterval() A RepetitiveInterval with one day duration, non-repeating.. RepetitiveInterval(startDate, endDate, intervalStartHour, intervalEndHour, nRepeats, repeatUnit). RepetitiveInterval(repetitiveInterval). :param float startDate: The start date as milliseconds since Jan 1, 1970 UTC. startDate must be earlier than or same as endDate. Or if repeatUnit is RepetitiveInterval.RepeatUnit.NONE, then it must equal endDate. :param float endDate: The end date as milliseconds since Jan 1, 1970 UTC. :param int intervalStartHour: The start hour in the day, from 0 to 23. intervalStartHour must be less than intervalEndHour. :param int intervalEndHour: The end hour in the day from 1 to 24. :param int nRepeats: (optional) Repeat the interval nRepeats repetitions, every unit, until endDate. If ommitted, use 0. :param int repeatUnit: (optional) The unit of the repetition, from RepetitiveInterval.RepeatUnit. If ommitted, use NONE. If this is NONE or ommitted, then startDate must equal endDate. """ def __init__(self, startDate = None, endDate = None, intervalStartHour = None, intervalEndHour = None, nRepeats = None, repeatUnit = None): if type(startDate) is RepetitiveInterval: # Make a copy. repetitiveInterval = startDate self._startDate = repetitiveInterval._startDate self._endDate = repetitiveInterval._endDate self._intervalStartHour = repetitiveInterval._intervalStartHour self._intervalEndHour = repetitiveInterval._intervalEndHour self._nRepeats = repetitiveInterval._nRepeats self._repeatUnit = repetitiveInterval._repeatUnit elif type(startDate) is float or type(startDate) is int: if nRepeats == None: nRepeats = 0 if repeatUnit == None: repeatUnit = RepetitiveInterval.RepeatUnit.NONE self._startDate = RepetitiveInterval._toDateOnlyMilliseconds(startDate) self._endDate = RepetitiveInterval._toDateOnlyMilliseconds(endDate) self._intervalStartHour = intervalStartHour self._intervalEndHour = intervalEndHour self._nRepeats = nRepeats self._repeatUnit = repeatUnit # Validate. if not (self._intervalStartHour < self._intervalEndHour): raise RuntimeError( "ReptitiveInterval: startHour must be less than endHour") if not (self._startDate <= self._endDate): raise RuntimeError( "ReptitiveInterval: startDate must be earlier than or same as endDate") if not (self._intervalStartHour >= 0): raise RuntimeError( "ReptitiveInterval: intervalStartHour must be non-negative") if not (self._intervalEndHour >= 1 and self._intervalEndHour <= 24): raise RuntimeError( "ReptitiveInterval: intervalEndHour must be from 1 to 24") if self._repeatUnit == RepetitiveInterval.RepeatUnit.NONE: if not (self._startDate == self._endDate): raise RuntimeError( "ReptitiveInterval: With RepeatUnit.NONE, startDate must equal endDate") else: # The default constructor. self._startDate = -sys.float_info.max self._endDate = -sys.float_info.max self._intervalStartHour = 0 self._intervalEndHour = 24 self._nRepeats = 0 self._repeatUnit = RepetitiveInterval.RepeatUnit.NONE
[docs] class RepeatUnit(object): NONE = 0 DAY = 1 MONTH = 2 YEAR = 3
[docs] class Result(object): def __init__(self, isPositive, interval): self.isPositive = isPositive self.interval = interval
[docs] def getInterval(self, timePoint): """ Get an interval that covers the time point. If there is no interval covering the time point, this returns False for isPositive and returns a negative interval. :param float timePoint: The time point as milliseconds since Jan 1, 1970 UTC. :return: An object with fields "isPositive" and "interval" where isPositive is True if the returned interval is positive or False if negative, and interval is the Interval covering the time point or a negative interval if not found. :rtype: RepetitiveInterval.Result """ if not self._hasIntervalOnDate(timePoint): # There is no interval on the date of timePoint. startTime = RepetitiveInterval._toDateOnlyMilliseconds(timePoint) endTime = (RepetitiveInterval._toDateOnlyMilliseconds(timePoint) + 24 * RepetitiveInterval.MILLISECONDS_IN_HOUR) isPositive = False else: # There is an interval on the date of timePoint. startTime = (RepetitiveInterval._toDateOnlyMilliseconds(timePoint) + self._intervalStartHour * RepetitiveInterval.MILLISECONDS_IN_HOUR) endTime = (RepetitiveInterval._toDateOnlyMilliseconds(timePoint) + self._intervalEndHour * RepetitiveInterval.MILLISECONDS_IN_HOUR) # Check if in the time duration. if timePoint < startTime: endTime = startTime startTime = RepetitiveInterval._toDateOnlyMilliseconds(timePoint) isPositive = False elif timePoint > endTime: startTime = endTime endTime = (RepetitiveInterval._toDateOnlyMilliseconds(timePoint) + RepetitiveInterval.MILLISECONDS_IN_DAY) isPositive = False else: isPositive = True return RepetitiveInterval.Result(isPositive, Interval(startTime, endTime))
[docs] def compare(self, other): """ Compare this to the other RepetitiveInterval. :param RepetitiveInterval other: The other RepetitiveInterval to compare to. :return: -1 if this is less than the other, 1 if greater and 0 if equal. :rtype: int """ if self._startDate < other._startDate: return -1 if self._startDate > other._startDate: return 1 if self._endDate < other._endDate: return -1 if self._endDate > other._endDate: return 1 if self._intervalStartHour < other._intervalStartHour: return -1 if self._intervalStartHour > other._intervalStartHour: return 1 if self._intervalEndHour < other._intervalEndHour: return -1 if self._intervalEndHour > other._intervalEndHour: return 1 if self._nRepeats < other._nRepeats: return -1 if self._nRepeats > other._nRepeats: return 1 if self._repeatUnit < other._repeatUnit: return -1 if self._repeatUnit > other._repeatUnit: return 1 return 0
[docs] def getStartDate(self): """ Get the start date. :return: The start date as milliseconds since Jan 1, 1970 UTC. :rtype: float """ return self._startDate
[docs] def getEndDate(self): """ Get the end date. :return: The end date as milliseconds since Jan 1, 1970 UTC. :rtype: float """ return self._endDate
[docs] def getIntervalStartHour(self): """ Get the interval start hour. :return: The interval start hour. :rtype: int """ return self._intervalStartHour
[docs] def getIntervalEndHour(self): """ Get the interval end hour. :return: The interval end hour. :rtype: int """ return self._intervalEndHour
[docs] def getNRepeats(self): """ Get the number of repeats. :return: The number of repeats. :rtype: int """ return self._nRepeats
[docs] def getRepeatUnit(self): """ Get the repeat unit. :return: The repeat unit, from RepetitiveInterval.RepeatUnit. :rtype: int """ return self._repeatUnit
def _hasIntervalOnDate(self, timePoint): """ Check if the date of the time point is in any interval. :param float timePoint: The time point as milliseconds since Jan 1, 1970 UTC. :return: True if the date of the time point is in any interval. :rtype: bool """ timePointDate = datetime.utcfromtimestamp( round(RepetitiveInterval._toDateOnlyMilliseconds(timePoint) / 1000.0)) startDate = datetime.utcfromtimestamp(self._startDate / 1000.0) endDate = datetime.utcfromtimestamp(self._endDate / 1000.0) if timePointDate < startDate or timePointDate > endDate: return False if self._repeatUnit == RepetitiveInterval.RepeatUnit.NONE: return True if self._repeatUnit == RepetitiveInterval.RepeatUnit.DAY: durationDays = int(round( (timePointDate - startDate).total_seconds() / RepetitiveInterval.SECONDS_IN_DAY)) if durationDays % self._nRepeats == 0: return True elif (self._repeatUnit == RepetitiveInterval.RepeatUnit.MONTH and timePointDate.day == startDate.day): yearDifference = timePointDate.year - startDate.year monthDifference = (12 * yearDifference + timePointDate.month - startDate.month) if monthDifference % self._nRepeats == 0: return True elif (self._repeatUnit == RepetitiveInterval.RepeatUnit.YEAR and timePointDate.day == startDate.day and timePointDate.month == startDate.month): difference = timePointDate.year - startDate.year if difference % self._nRepeats == 0: return True return False @staticmethod def _toDateOnlyMilliseconds(timePoint): """ Return a time point on the beginning of the date (without hours, minutes, etc.) :param float timePoint: The time point as milliseconds since Jan 1, 1970 UTC. :return: A time point as milliseconds since Jan 1, 1970 UTC. :rtype: float """ result = round(float(timePoint)) result -= result % RepetitiveInterval.MILLISECONDS_IN_DAY return result MILLISECONDS_IN_HOUR = 3600 * 1000 MILLISECONDS_IN_DAY = 24 * 3600 * 1000 SECONDS_IN_DAY = 24 * 3600