blob: d1229cb20695a3d169f6b45eacabc4623baa8256 [file] [log] [blame]
# Copyright 2013 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module with time utilities."""
# this import is required, as otherwise importing time refers to this module
from __future__ import absolute_import
import datetime
import time
def isBefore(date):
"""Tells whether the specified date is in the future.
Args:
date: datetime.datetime or datetime.date object
Returns:
True if it is before the specified date; False otherwise, or
when the date is not given.
"""
return date and datetime.datetime.utcnow() < date
def isAfter(date):
"""Tells whether the specified date is in the past.
Args:
date: datetime.datetime or datetime.date object
Returns:
True if it is after the specified date; False otherwise, or
when the date is not given.
"""
return date and date < datetime.datetime.utcnow()
def isBetween(start_date, end_date):
"""Tells whether it is between the the specified start date and the
specified end date.
It is assumed, and therefore not checked, if start date comes before end date.
If that condition does not hold true, the result of this function is
unspecified.
Args:
start_date: datetime.datetime or datetime.date object
end_date: datetime.datetime or datetime.date object
Returns:
True if it is between the specified dates; False otherwise or if at least
one of the dates is not given.
"""
return isAfter(start_date) and isBefore(end_date)
PRE_PERIOD_STATE = 'pre_period'
IN_PERIOD_STATE = 'in_period'
POST_PERIOD_STATE = 'post_period'
PERIOD_STATES = [PRE_PERIOD_STATE, IN_PERIOD_STATE, POST_PERIOD_STATE]
class Period(object):
"""Class to represent relationship between the current moment and
the specified period.
A period is defined by two dates that stand for its beginning and end,
respectively. If at least one of these dates is absent, the period
is considered unbounded.
"""
def __init__(self, start=None, end=None):
"""Initializes new instance of this class with the specified start and
end dates.
Args:
start: start date of the period. May be None if unspecified.
end: end date of the period. May be None if unspecified.
"""
self.start = start
self.end = end
@property
def state(self):
"""Returns state of the period with respect to the current moment in time.
For the period bounded at both start and end,there are
three possibilities. The current moment may be:
- before the period
- in the period
- after the period
For a period with no start date defined, the current moment may be:
- in the period
- after the period
For period with no end date defined, the current moment may be:
- before the period
- in the period
Returns:
A constant representing the current state of the period. Can be one of
PRE_PERIOD_STATE, IN_PERIOD_STATE or POST_PERIOD_STATE.
"""
# unbounded period
if not self.start and not self.end:
return IN_PERIOD_STATE
# period right-unbounded
elif self.start and not self.end:
if isBefore(self.start):
return PRE_PERIOD_STATE
else:
return IN_PERIOD_STATE
# period left-unbounded
elif not self.start and self.end:
if isAfter(self.end):
return POST_PERIOD_STATE
else:
return IN_PERIOD_STATE
# period bounded
elif isBefore(self.start):
return PRE_PERIOD_STATE
elif isAfter(self.end):
return POST_PERIOD_STATE
else:
return IN_PERIOD_STATE
def currentTimeMillis():
"""Returns the current time in milliseconds.
Returns:
The time in milliseconds since the epoch as an integer number.
"""
return int(time.time() * 1000)
def getAge(birthdate, as_of=None):
"""Returns age for the specified age with respect to the specified reference
date or today.
Args:
birthdate: datetime.date when the examined entity was created or born.
as_of: Optional datetime.date object to use as a reference to determine
the age. If not specified, today will be used.
Returns:
An int with the number of years.
"""
as_of = as_of or datetime.date.today()
if as_of > datetime.date.today():
raise ValueError('as_of is in the future: %s' % as_of)
# has the birthday already happened in the as_of year?
had_birthday = (as_of.month, as_of.day) >= (birthdate.month, birthdate.day)
return as_of.year - birthdate.year - (1 if not had_birthday else 0)
class Clock(object):
"""Interface that contains methods to provide datetime objects."""
def utcnow(self):
"""Returns the current date and time.
Returns:
datetime.datetime object.
"""
raise NotImplementedError
class _RealClock(Clock):
"""Implementation of Clock that provides actual date and time."""
def utcnow(self):
"""See Clock.utcnow for specification."""
return datetime.datetime.utcnow()
REAL_CLOCK = _RealClock()