from typing import Tuple from .dates_add_days import CivilDate, days_in_month, format_iso_date, parse_iso_date from .dates_quarter_types import Quarter def _month_from_index(index: int) -> Tuple[int, int]: """A month counted from January of year 0, back to its year and month.""" year = index // 12 return year, index - year * 12 + 1 def _first_day(index: int) -> str: year, month = _month_from_index(index) return format_iso_date(CivilDate(year=year, month=month, day=1)) def _last_day(index: int) -> str: year, month = _month_from_index(index) return format_iso_date(CivilDate(year=year, month=month, day=days_in_month(year, month))) def quarter(iso: str, fiscal_year_start_month: int) -> Quarter: """The calendar and fiscal quarter of a date. The fiscal year is given as dates, not a number, because "FY2027" means the year ending in 2027 to one organisation and the year starting in it to another. """ if isinstance(fiscal_year_start_month, bool) or not isinstance(fiscal_year_start_month, int): raise TypeError("fiscalYearStartMonth must be an integer, received %r" % (fiscal_year_start_month,)) if fiscal_year_start_month < 1 or fiscal_year_start_month > 12: raise ValueError("fiscalYearStartMonth must be 1-12, received %s" % (fiscal_year_start_month,)) date = parse_iso_date(iso) months_into_year = (date.month - fiscal_year_start_month) % 12 fiscal_quarter = months_into_year // 3 + 1 # Work in months counted from year 0 so year boundaries need no special case. year_start = date.year * 12 + (date.month - 1) - months_into_year quarter_start = year_start + 3 * (fiscal_quarter - 1) if _month_from_index(year_start)[0] < 1 or _month_from_index(year_start + 11)[0] > 9999: raise ValueError( 'the fiscal year containing "%s" is outside the supported range 0001-01-01 to 9999-12-31' % (iso,) ) return Quarter( calendar_year=date.year, calendar_quarter=(date.month - 1) // 3 + 1, fiscal_quarter=fiscal_quarter, fiscal_year_start=_first_day(year_start), fiscal_year_end=_last_day(year_start + 11), fiscal_quarter_start=_first_day(quarter_start), fiscal_quarter_end=_last_day(quarter_start + 2), )