from typing import Dict, Sequence, Set from .dates_add_days import add_days, parse_iso_date from .dates_bank_holidays import bank_holidays from .dates_day_of_week import day_of_week from .legal_court_deadline_types import CourtDeadline, DeadlineRule def court_deadline( trigger_date: str, days: int, rule: DeadlineRule, at_court_office: bool, court_closed_days: Sequence[str], ) -> CourtDeadline: """A deadline in days by CPR 2.8: clear days, short periods, court-office closures.""" parse_iso_date(trigger_date) if isinstance(days, bool) or not isinstance(days, int) or days < 1: raise ValueError("days must be an integer of at least 1, received %s" % (days,)) if rule not in ("within", "at-least-after", "at-least-before"): raise ValueError('unknown deadline rule "%s": expected within, at-least-after or at-least-before' % (rule,)) if at_court_office and rule != "within": raise ValueError("atCourtOffice applies only to a within period (CPR 2.8(5))") if not at_court_office and len(court_closed_days) > 0: raise ValueError("courtClosedDays only apply when atCourtOffice is true") for d in court_closed_days: parse_iso_date(d) # England and Wales holidays by year, fetched only for the years a count touches. holidays: Dict[int, Set[str]] = {} def is_holiday(iso: str) -> bool: year = int(iso[0:4]) if year not in holidays: holidays[year] = {h.date for h in bank_holidays("england-and-wales", year)} return iso in holidays[year] def is_non_working(iso: str) -> bool: return day_of_week(iso) >= 6 or is_holiday(iso) short_period = days <= 5 step = -1 if rule == "at-least-before" else 1 # Walk the clear days: the trigger day is never one, and in a short period # weekends and bank holidays are passed over without counting. cursor = trigger_date counted = 0 while counted < days: cursor = add_days(cursor, step) if not short_period or not is_non_working(cursor): counted += 1 if rule != "within": # The act or event falls on the day beyond the last clear day. return CourtDeadline(date=add_days(cursor, step), short_period=short_period, extended=False) date = cursor extended = False if at_court_office: while is_non_working(date) or date in court_closed_days: date = add_days(date, 1) extended = True return CourtDeadline(date=date, short_period=short_period, extended=extended)