from typing import Sequence from .dates_add_days import epoch_day_from_iso, iso_from_epoch_day from .dates_day_of_week import day_of_week def add_business_days(iso: str, days: int, holidays: Sequence[str]) -> str: """The ``days``-th working day after ``iso`` (before it when negative), skipping Saturdays, Sundays and the listed holidays. The start date is never counted, so Friday + 1 and Saturday + 1 are both Monday, as in spreadsheet WORKDAY. Zero returns ``iso`` unchanged. """ if isinstance(days, bool) or not isinstance(days, int): raise TypeError("days must be an integer, received %r" % (days,)) day = epoch_day_from_iso(iso) # Every holiday is validated even when the move never reaches it, so a typo # in a calendar fails on the next run rather than in the year it matters. excluded = {epoch_day_from_iso(holiday) for holiday in holidays} step = -1 if days < 0 else 1 weekday = day_of_week(iso) remaining = abs(days) while remaining > 0: day += step weekday = (weekday - 1 + step) % 7 + 1 if weekday <= 5 and day not in excluded: remaining -= 1 return iso_from_epoch_day(day)