Last active
May 5, 2023 14:05
-
-
Save rpdelaney/f2c710a73fe93e4cc98d09e1b9ce0a1d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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