from datetime import timedelta
__all__ = [
'truncate',
'truncate_second',
'truncate_minute',
'truncate_hour',
'truncate_day',
'truncate_week',
'truncate_month',
'truncate_quarter',
'truncate_half_year',
'truncate_year',
]
PERIODS = {
'second': dict(microsecond=0),
'minute': dict(microsecond=0, second=0),
'hour': dict(microsecond=0, second=0, minute=0),
'day': dict(microsecond=0, second=0, minute=0, hour=0,),
'month': dict(microsecond=0, second=0, minute=0, hour=0, day=1),
'year': dict(microsecond=0, second=0, minute=0, hour=0, day=1, month=1),
}
ODD_PERIODS = ['week', 'quarter', 'half_year']
[docs]def truncate_second(datetime):
''' Sugar for :py:func:`truncate(datetime, 'second')` '''
return truncate(datetime, 'second')
[docs]def truncate_minute(datetime):
''' Sugar for :py:func:`truncate(datetime, 'minute')` '''
return truncate(datetime, 'minute')
[docs]def truncate_hour(datetime):
''' Sugar for :py:func:`truncate(datetime, 'hour')` '''
return truncate(datetime, 'hour')
[docs]def truncate_day(datetime):
''' Sugar for :py:func:`truncate(datetime, 'day')` '''
return truncate(datetime, 'day')
[docs]def truncate_week(datetime):
'''
Truncates a date to the first day of an ISO 8601 week, i.e. monday.
:params datetime: an initialized datetime object
:return: `datetime` with the original day set to monday
:rtype: :py:mod:`datetime` datetime object
'''
datetime = truncate(datetime, 'day')
return datetime - timedelta(days=datetime.isoweekday() - 1)
[docs]def truncate_month(datetime):
''' Sugar for :py:func:`truncate(datetime, 'month')` '''
return truncate(datetime, 'month')
[docs]def truncate_quarter(datetime):
'''
Truncates the datetime to the first day of the quarter for this date.
:params datetime: an initialized datetime object
:return: `datetime` with the month set to the first month of this quarter
:rtype: :py:mod:`datetime` datetime object
'''
datetime = truncate(datetime, 'month')
month = datetime.month
if 1 <= month <= 3:
return datetime.replace(month=1)
elif 4 <= month <= 6:
return datetime.replace(month=4)
elif 7 <= month <= 9:
return datetime.replace(month=7)
elif 10 <= month <= 12:
return datetime.replace(month=10)
[docs]def truncate_half_year(datetime):
'''
Truncates the datetime to the first day of the half year for this date.
:params datetime: an initialized datetime object
:return: `datetime` with the month set to the first month of this half year
:rtype: :py:mod:`datetime` datetime object
'''
datetime = truncate(datetime, 'month')
month = datetime.month
if 1 <= month <= 6:
return datetime.replace(month=1)
elif 7 <= month <= 12:
return datetime.replace(month=7)
[docs]def truncate_year(datetime):
''' Sugar for :py:func:`truncate(datetime, 'year')` '''
return truncate(datetime, 'year')
[docs]def truncate(datetime, truncate_to='day'):
'''
Truncates a datetime to have the values with higher precision than
the one set as `truncate_to` as zero (or one for day and month).
Possible values for `truncate_to`:
* second
* minute
* hour
* day
* week (iso week i.e. to monday)
* month
* quarter
* half_year
* year
Examples::
>>> truncate(datetime(2012, 12, 12, 12), 'day')
datetime(2012, 12, 12)
>>> truncate(datetime(2012, 12, 14, 12, 15), 'quarter')
datetime(2012, 10, 1)
>>> truncate(datetime(2012, 3, 1), 'week')
datetime(2012, 2, 27)
:params datetime: an initialized datetime object
:params truncate_to: The highest precision to keep its original data.
:return: datetime with `truncated_to` as the highest level of precision
:rtype: :py:mod:`datetime` datetime object
'''
if truncate_to in PERIODS:
return datetime.replace(**PERIODS[truncate_to])
elif truncate_to in ODD_PERIODS:
if truncate_to == 'week':
return truncate_week(datetime)
elif truncate_to == 'quarter':
return truncate_quarter(datetime)
elif truncate_to == 'half_year':
return truncate_half_year(datetime)
else:
raise ValueError('truncate_to not valid. Valid periods: {}'.format(
', '.join(PERIODS.keys() + ODD_PERIODS)
))