from .dates_add_days import days_between, parse_iso_date from .dates_uk_tax_year import uk_tax_year from .payroll_tax_period_types import PayFrequency, TaxPeriod def tax_period(pay_date: str, frequency: PayFrequency) -> TaxPeriod: """The tax week, tax month and payroll period a pay date falls in. The tax year is 365 or 366 days, which is 52 weeks and one or two days over. Those spare days are week 53, and they are the reason ``period`` exists: HMRC numbers the extra payday 53, 54 or 56 by frequency and taxes it non-cumulatively, so a payroll that just counts weeks gets it wrong. """ if frequency not in ("weekly", "fortnightly", "four-weekly", "monthly"): raise ValueError('unknown pay frequency "%s"' % (frequency,)) year = uk_tax_year(pay_date) week = days_between(year.start, pay_date) // 7 + 1 # Tax months run from the 6th to the 5th, so the calendar month moves on # five days before the tax month does. date = parse_iso_date(pay_date) month = date.month - 3 if date.day >= 6 else date.month - 4 if month <= 0: month += 12 if frequency == "monthly": period = month elif week < 53: period = week else: period = {"weekly": 53, "fortnightly": 54, "four-weekly": 56}[frequency] return TaxPeriod(tax_year=year.label, tax_year_start=year.start, week=week, month=month, period=period)