Source code for beansoup.utils.periods

"""Utilities to work with monthly billing periods."""

import calendar
import datetime


[docs]def enclose_date(date, first_day=1): """Compute the monthly period containing the given date. Args: date: A datetime.date object. first_day: The first day of the monthly cycle. It must be an int in the interval [1,28]. Returns: A pair of datetime.date objects; the start and end dates of the monthly period containing the given date. """ start = greatest_start(date, first_day=first_day) _, length = calendar.monthrange(start.year, start.month) return start, start + datetime.timedelta(days=length-1)
[docs]def greatest_start(date, first_day=1): """Compute the starting date of the monthly period containing the given date. More formally, it computes the greatest start date of the monthly cycle based on first_day that is less than or equal to the given date. Args: date: A datetime.date object. first_day: The first day of the monthly cycle. It must be an int in the interval [1,28]. Returns: The starting date of the monthly period containing the given date as a datetime.date object. """ assert 0 < first_day < 29, "Invalid 'first_day' value {}: first day of monthly cycle must be in [1,28]".format(first_day) if date.day >= first_day: year, month = date.year, date.month elif date.month > 1: year, month = date.year, date.month - 1 else: year, month = date.year - 1, 12 return datetime.date(year, month, first_day)
[docs]def lowest_end(date, first_day=1): """Compute the ending date of the monthly period containing the given date. More formally, it computes the lowest end date of the monthly cycle based on first_day that is greater than or equal to the given date. Args: date: A datetime.date object. first_day: The first day of the monthly cycle. It must be an int in the interval [1,28]. Returns: The ending date of the monthly period containing the given date as a datetime.date object. """ start = greatest_start(date, first_day=first_day) _, length = calendar.monthrange(start.year, start.month) return start + datetime.timedelta(days=length-1)
[docs]def count(date, reverse=False): """Make an iterator that returns monthly-spaced dates. Args: date: A datetime.date object; the starting date. reverse: A boolean value; if True, the iterator will go back in time. Returns: An iterator. """ while True: yield date date = prev(date) if reverse else next(date)