Skip to content

Instantly share code, notes, and snippets.

@rpdelaney
Last active May 5, 2023 14:05
Show Gist options
  • Save rpdelaney/f2c710a73fe93e4cc98d09e1b9ce0a1d to your computer and use it in GitHub Desktop.
Save rpdelaney/f2c710a73fe93e4cc98d09e1b9ce0a1d to your computer and use it in GitHub Desktop.
"""
Calculate 12 week periods in a year that have the fewest holidays.
"""
import argparse
from datetime import date, datetime, timedelta
from pprint import pprint as print
from typing import List, Tuple
from pandas.tseries.holiday import (
Holiday,
nearest_workday,
USFederalHolidayCalendar,
)
from pandas.tseries.offsets import WeekOfMonth, Day
import pandas as pd
def day_after_thanksgiving(dt):
"""
Calculate the date of the day after Thanksgiving for a given year.
Args:
dt (date): A date object containing the year for which to
calculate the day after Thanksgiving.
Returns:
pd.Timestamp: A Timestamp object representing the day after
Thanksgiving for the given year.
"""
fourth_thursday = pd.Timestamp(dt.year, 11, 1) + WeekOfMonth(
weekday=3, week=3
)
return fourth_thursday + Day(1)
class CustomUSFederalHolidayCalendar(USFederalHolidayCalendar):
"""
A custom US Federal Holiday Calendar that includes the day after
Thanksgiving and Christmas Eve.
Inherits from the USFederalHolidayCalendar class and extends the
list of holidays with additional custom holidays.
"""
rules = USFederalHolidayCalendar.rules + [
Holiday(
"Day After Thanksgiving",
month=11,
day=1,
observance=day_after_thanksgiving,
),
Holiday("Christmas Eve", month=12, day=24, observance=nearest_workday),
]
def count_holidays_between_dates(
date_pair: Tuple[date, date], holidays: List[date]
) -> int:
"""
Count the number of holidays between a pair of dates.
Args:
date_pair: A tuple containing two date objects.
holidays: A list of holidays as date objects.
Returns:
The count of holidays between the given date_pair.
"""
start, end = date_pair
return sum(start <= holiday <= end for holiday in holidays)
def generate_date_pairs(
holidays: List[date], year: int
) -> List[Tuple[Tuple[date, date], int]]:
"""
Generate a list of date pairs, each pair exactly twelve weeks apart,
along with the count of holidays between the pair.
Args:
holidays: A list of arbitrary dates as holidays.
Returns:
A list of date pairs and the count of holidays between each pair.
"""
result = []
start_date = date(year, 1, 1)
mid_date = date(year, 12, 31) - timedelta(weeks=12)
while start_date <= mid_date:
end_date = start_date + timedelta(weeks=12)
date_pair = (start_date, end_date)
holiday_count = count_holidays_between_dates(date_pair, holidays)
result.append((date_pair, holiday_count))
start_date += timedelta(days=1)
return result
def main(year) -> None:
custom_cal = CustomUSFederalHolidayCalendar()
custom_holidays = [
dt.date()
for dt in custom_cal.holidays(
start=f"{year}-01-01", end=f"{year}-12-31"
).to_pydatetime()
]
result = generate_date_pairs(custom_holidays, year)
result.sort(key=lambda x: x[1]) # sort by count of holidays in the range
for date_pair, holiday_count in result:
print(
f"From {date_pair[0].isoformat()} to {date_pair[1].isoformat()}, "
f"{holiday_count} holidays"
)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
current_year = datetime.now().year
parser.add_argument(
"-y",
"--year",
type=int,
default=current_year,
help="Year (default: current year)",
)
args = parser.parse_args()
main(args.year)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment