Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers, document generators, FCC compliance tools, Canada CRTC formation, Ansible infrastructure, and deployment scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
f8cd37ac8c
1823 changed files with 145167 additions and 0 deletions
304
scripts/formation/holidays.py
Normal file
304
scripts/formation/holidays.py
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
"""
|
||||
Holiday calendar for web automation scheduling.
|
||||
|
||||
Covers:
|
||||
- US federal holidays (observed) — relevant to IRS, SSA, and US state portals
|
||||
- Canadian federal statutory holidays — relevant to CRTC, BC Registry
|
||||
- BC provincial holidays — relevant to BC Corporate Registry, BC Online
|
||||
- State-specific observed holidays (closures vary by SOS office)
|
||||
|
||||
Usage:
|
||||
from scripts.formation.holidays import is_holiday, next_business_day
|
||||
|
||||
if is_holiday(date.today(), jurisdiction="US"):
|
||||
...
|
||||
if is_holiday(date.today(), jurisdiction="BC"):
|
||||
...
|
||||
"""
|
||||
|
||||
from datetime import date, timedelta
|
||||
from typing import Literal, Optional
|
||||
|
||||
|
||||
Jurisdiction = Literal["US", "CA", "BC", "IRS"]
|
||||
|
||||
|
||||
# ── Helpers ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def _nth_weekday(year: int, month: int, weekday: int, n: int) -> date:
|
||||
"""Return the nth occurrence of weekday (Mon=0 … Sun=6) in given month/year.
|
||||
n=1 → first, n=2 → second, n=-1 → last.
|
||||
"""
|
||||
if n > 0:
|
||||
first = date(year, month, 1)
|
||||
offset = (weekday - first.weekday()) % 7
|
||||
return first + timedelta(days=offset + 7 * (n - 1))
|
||||
else: # last
|
||||
# find last day, walk back
|
||||
if month == 12:
|
||||
last = date(year + 1, 1, 1) - timedelta(days=1)
|
||||
else:
|
||||
last = date(year, month + 1, 1) - timedelta(days=1)
|
||||
offset = (last.weekday() - weekday) % 7
|
||||
return last - timedelta(days=offset)
|
||||
|
||||
|
||||
def _observed(d: date) -> date:
|
||||
"""Return the observed date for a holiday falling on a weekend.
|
||||
Saturday → Friday, Sunday → Monday.
|
||||
"""
|
||||
if d.weekday() == 5: # Saturday
|
||||
return d - timedelta(days=1)
|
||||
if d.weekday() == 6: # Sunday
|
||||
return d + timedelta(days=1)
|
||||
return d
|
||||
|
||||
|
||||
# ── US Federal Holidays ────────────────────────────────────────────────────────
|
||||
|
||||
def _us_federal_holidays(year: int) -> set[date]:
|
||||
"""Return the set of US federal holiday observed dates for a given year."""
|
||||
MON, TUE, WED, THU, FRI, SAT, SUN = range(7)
|
||||
holidays = set()
|
||||
|
||||
# New Year's Day — Jan 1
|
||||
holidays.add(_observed(date(year, 1, 1)))
|
||||
|
||||
# Martin Luther King Jr. Day — 3rd Monday in January
|
||||
holidays.add(_nth_weekday(year, 1, MON, 3))
|
||||
|
||||
# Presidents' Day (Washington's Birthday) — 3rd Monday in February
|
||||
holidays.add(_nth_weekday(year, 2, MON, 3))
|
||||
|
||||
# Memorial Day — last Monday in May
|
||||
holidays.add(_nth_weekday(year, 5, MON, -1))
|
||||
|
||||
# Juneteenth — June 19
|
||||
holidays.add(_observed(date(year, 6, 19)))
|
||||
|
||||
# Independence Day — July 4
|
||||
holidays.add(_observed(date(year, 7, 4)))
|
||||
|
||||
# Labor Day — 1st Monday in September
|
||||
holidays.add(_nth_weekday(year, 9, MON, 1))
|
||||
|
||||
# Columbus Day — 2nd Monday in October
|
||||
holidays.add(_nth_weekday(year, 10, MON, 2))
|
||||
|
||||
# Veterans Day — November 11
|
||||
holidays.add(_observed(date(year, 11, 11)))
|
||||
|
||||
# Thanksgiving — 4th Thursday in November
|
||||
holidays.add(_nth_weekday(year, 11, THU, 4))
|
||||
|
||||
# Christmas — December 25
|
||||
holidays.add(_observed(date(year, 12, 25)))
|
||||
|
||||
# New Year's Day (observed for next year, sometimes Dec 31)
|
||||
ny_next = _observed(date(year + 1, 1, 1))
|
||||
if ny_next.year == year:
|
||||
holidays.add(ny_next)
|
||||
|
||||
return holidays
|
||||
|
||||
|
||||
# ── Canadian Federal Statutory Holidays ───────────────────────────────────────
|
||||
|
||||
def _canada_federal_holidays(year: int) -> set[date]:
|
||||
"""Return Canadian federal statutory holiday dates for a given year."""
|
||||
MON, TUE, WED, THU, FRI, SAT, SUN = range(7)
|
||||
holidays = set()
|
||||
|
||||
# New Year's Day
|
||||
holidays.add(_observed(date(year, 1, 1)))
|
||||
|
||||
# Good Friday — Friday before Easter
|
||||
easter = _easter(year)
|
||||
holidays.add(easter - timedelta(days=2)) # Good Friday
|
||||
|
||||
# Easter Monday
|
||||
holidays.add(easter + timedelta(days=1))
|
||||
|
||||
# Victoria Day — Monday before May 25
|
||||
may25 = date(year, 5, 25)
|
||||
days_since_mon = may25.weekday() # Mon=0
|
||||
if days_since_mon == 0:
|
||||
holidays.add(may25 - timedelta(days=7))
|
||||
else:
|
||||
holidays.add(may25 - timedelta(days=days_since_mon))
|
||||
|
||||
# Canada Day — July 1
|
||||
holidays.add(_observed(date(year, 7, 1)))
|
||||
|
||||
# Labour Day — 1st Monday in September
|
||||
holidays.add(_nth_weekday(year, 9, MON, 1))
|
||||
|
||||
# National Day for Truth and Reconciliation — Sept 30 (federal)
|
||||
holidays.add(_observed(date(year, 9, 30)))
|
||||
|
||||
# Thanksgiving — 2nd Monday in October
|
||||
holidays.add(_nth_weekday(year, 10, MON, 2))
|
||||
|
||||
# Remembrance Day — November 11
|
||||
holidays.add(_observed(date(year, 11, 11)))
|
||||
|
||||
# Christmas Day — December 25
|
||||
holidays.add(_observed(date(year, 12, 25)))
|
||||
|
||||
# Boxing Day — December 26
|
||||
holidays.add(_observed(date(year, 12, 26)))
|
||||
|
||||
return holidays
|
||||
|
||||
|
||||
# ── BC Provincial Holidays ────────────────────────────────────────────────────
|
||||
|
||||
def _bc_holidays(year: int) -> set[date]:
|
||||
"""Return BC provincial statutory holidays (superset of Canadian federal)."""
|
||||
MON = 0
|
||||
holidays = _canada_federal_holidays(year)
|
||||
|
||||
# BC Day — 1st Monday in August
|
||||
holidays.add(_nth_weekday(year, 8, MON, 1))
|
||||
|
||||
# Family Day — 3rd Monday in February (BC-specific — started 2013)
|
||||
if year >= 2013:
|
||||
holidays.add(_nth_weekday(year, 2, MON, 3))
|
||||
|
||||
return holidays
|
||||
|
||||
|
||||
# ── Easter (Gregorian) ────────────────────────────────────────────────────────
|
||||
|
||||
def _easter(year: int) -> date:
|
||||
"""Return date of Easter Sunday using the Anonymous Gregorian algorithm."""
|
||||
a = year % 19
|
||||
b = year // 100
|
||||
c = year % 100
|
||||
d = b // 4
|
||||
e = b % 4
|
||||
f = (b + 8) // 25
|
||||
g = (b - f + 1) // 3
|
||||
h = (19 * a + b - d - g + 15) % 30
|
||||
i = c // 4
|
||||
k = c % 4
|
||||
l = (32 + 2 * e + 2 * i - h - k) % 7
|
||||
m = (a + 11 * h + 22 * l) // 451
|
||||
month = (h + l - 7 * m + 114) // 31
|
||||
day = ((h + l - 7 * m + 114) % 31) + 1
|
||||
return date(year, month, day)
|
||||
|
||||
|
||||
# ── Cache ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
_cache: dict[tuple[int, str], set[date]] = {}
|
||||
|
||||
|
||||
def _get_holidays(year: int, jurisdiction: Jurisdiction) -> set[date]:
|
||||
key = (year, jurisdiction)
|
||||
if key not in _cache:
|
||||
if jurisdiction == "US" or jurisdiction == "IRS":
|
||||
_cache[key] = _us_federal_holidays(year)
|
||||
elif jurisdiction == "CA":
|
||||
_cache[key] = _canada_federal_holidays(year)
|
||||
elif jurisdiction == "BC":
|
||||
_cache[key] = _bc_holidays(year)
|
||||
else:
|
||||
_cache[key] = set()
|
||||
return _cache[key]
|
||||
|
||||
|
||||
# ── Public API ────────────────────────────────────────────────────────────────
|
||||
|
||||
def is_holiday(d: date, jurisdiction: Jurisdiction = "US") -> bool:
|
||||
"""Return True if `d` is a holiday in the given jurisdiction."""
|
||||
return d in _get_holidays(d.year, jurisdiction)
|
||||
|
||||
|
||||
def is_weekend(d: date) -> bool:
|
||||
"""Return True if `d` is Saturday or Sunday."""
|
||||
return d.weekday() >= 5
|
||||
|
||||
|
||||
def is_business_day(d: date, jurisdiction: Jurisdiction = "US") -> bool:
|
||||
"""Return True if `d` is a weekday and not a holiday."""
|
||||
return not is_weekend(d) and not is_holiday(d, jurisdiction)
|
||||
|
||||
|
||||
def next_business_day(
|
||||
after: Optional[date] = None,
|
||||
jurisdiction: Jurisdiction = "US",
|
||||
) -> date:
|
||||
"""Return the next business day after `after` (default: today)."""
|
||||
d = (after or date.today()) + timedelta(days=1)
|
||||
while not is_business_day(d, jurisdiction):
|
||||
d += timedelta(days=1)
|
||||
return d
|
||||
|
||||
|
||||
def holiday_name(d: date, jurisdiction: Jurisdiction = "US") -> Optional[str]:
|
||||
"""Return a human-readable name for the holiday on `d`, or None."""
|
||||
# Build a labelled lookup for the year
|
||||
labels = _labelled_holidays(d.year, jurisdiction)
|
||||
return labels.get(d)
|
||||
|
||||
|
||||
def _labelled_holidays(year: int, jurisdiction: Jurisdiction) -> dict[date, str]:
|
||||
"""Return holiday dates mapped to their names."""
|
||||
MON, TUE, WED, THU, FRI, SAT, SUN = range(7)
|
||||
result: dict[date, str] = {}
|
||||
|
||||
if jurisdiction in ("US", "IRS"):
|
||||
result[_observed(date(year, 1, 1))] = "New Year's Day"
|
||||
result[_nth_weekday(year, 1, MON, 3)] = "Martin Luther King Jr. Day"
|
||||
result[_nth_weekday(year, 2, MON, 3)] = "Presidents' Day"
|
||||
result[_nth_weekday(year, 5, MON, -1)] = "Memorial Day"
|
||||
result[_observed(date(year, 6, 19))] = "Juneteenth"
|
||||
result[_observed(date(year, 7, 4))] = "Independence Day"
|
||||
result[_nth_weekday(year, 9, MON, 1)] = "Labor Day"
|
||||
result[_nth_weekday(year, 10, MON, 2)] = "Columbus Day"
|
||||
result[_observed(date(year, 11, 11))] = "Veterans Day"
|
||||
result[_nth_weekday(year, 11, THU, 4)] = "Thanksgiving"
|
||||
result[_observed(date(year, 12, 25))] = "Christmas Day"
|
||||
ny_next = _observed(date(year + 1, 1, 1))
|
||||
if ny_next.year == year:
|
||||
result[ny_next] = "New Year's Day (observed)"
|
||||
|
||||
elif jurisdiction in ("CA", "BC"):
|
||||
easter = _easter(year)
|
||||
result[_observed(date(year, 1, 1))] = "New Year's Day"
|
||||
result[easter - timedelta(days=2)] = "Good Friday"
|
||||
result[easter + timedelta(days=1)] = "Easter Monday"
|
||||
may25 = date(year, 5, 25)
|
||||
daysback = may25.weekday() if may25.weekday() > 0 else 7
|
||||
result[may25 - timedelta(days=daysback)] = "Victoria Day"
|
||||
result[_observed(date(year, 7, 1))] = "Canada Day"
|
||||
result[_nth_weekday(year, 9, MON, 1)] = "Labour Day"
|
||||
result[_observed(date(year, 9, 30))] = "National Day for Truth and Reconciliation"
|
||||
result[_nth_weekday(year, 10, MON, 2)] = "Thanksgiving"
|
||||
result[_observed(date(year, 11, 11))] = "Remembrance Day"
|
||||
result[_observed(date(year, 12, 25))] = "Christmas Day"
|
||||
result[_observed(date(year, 12, 26))] = "Boxing Day"
|
||||
if jurisdiction == "BC":
|
||||
result[_nth_weekday(year, 8, MON, 1)] = "BC Day"
|
||||
if year >= 2013:
|
||||
result[_nth_weekday(year, 2, MON, 3)] = "Family Day (BC)"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def upcoming_holidays(
|
||||
days: int = 30,
|
||||
jurisdiction: Jurisdiction = "US",
|
||||
from_date: Optional[date] = None,
|
||||
) -> list[tuple[date, str]]:
|
||||
"""Return (date, name) pairs for holidays in the next `days` days."""
|
||||
start = from_date or date.today()
|
||||
end = start + timedelta(days=days)
|
||||
labelled = _labelled_holidays(start.year, jurisdiction)
|
||||
if end.year != start.year:
|
||||
labelled.update(_labelled_holidays(end.year, jurisdiction))
|
||||
return sorted(
|
||||
[(d, name) for d, name in labelled.items() if start <= d <= end],
|
||||
key=lambda x: x[0],
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue