from typing import List, Sequence from .banking_bacs_processing_date_types import BacsCycle from .dates_add_business_days import add_business_days from .dates_bank_holidays import bank_holidays from .dates_day_of_week import is_weekday def _holidays_for(years: Sequence[int]) -> List[str]: out: List[str] = [] for year in years: out.extend(holiday.date for holiday in bank_holidays("england-and-wales", year)) return out def _cycle_with(submission_iso: str, after_cutoff: bool, holidays: Sequence[str]) -> BacsCycle: processing_day = is_weekday(submission_iso) and submission_iso not in holidays input_day = submission_iso if processing_day and not after_cutoff else add_business_days(submission_iso, 1, holidays) return BacsCycle( input_day=input_day, processing_day=add_business_days(input_day, 1, holidays), entry_day=add_business_days(input_day, 2, holidays), ) def bacs_processing_dates(submission_iso: str, after_cutoff: bool) -> BacsCycle: """The Bacs cycle a file joins. Bacs works on processing days - Monday to Friday except England and Wales bank holidays - and a file joins the cycle whose input day is the day it arrives, if that is a processing day and it arrived before the 22:30 deadline, or the next processing day otherwise. Processing is the next processing day and entry the one after. """ if not isinstance(after_cutoff, bool): raise TypeError("afterCutoff must be a boolean, received %r" % (after_cutoff,)) # is_weekday validates the date before its year is read. is_weekday(submission_iso) year = int(submission_iso[0:4]) # Only reach for next year's calendar when the cycle really runs into it, # so a December submission in the last published year still works as long # as its cycle ends inside that year. cycle = _cycle_with(submission_iso, after_cutoff, _holidays_for([year])) if cycle.entry_day[0:4] != submission_iso[0:4]: cycle = _cycle_with(submission_iso, after_cutoff, _holidays_for([year, year + 1])) return cycle