如何用Python结合CustomBusinessDay计算月末最后工作日及指定日期前后含节假日的工作日?
计算指定日期前后最后工作日及月末最后工作日的解决方案
看起来你已经有了美国交易日历的雏形,我先帮你补全完整的节假日规则,再一步步实现你需要的两个核心功能。
首先,完整的美国交易日历类(包含所有主要美股节假日):
import pandas as pd import datetime as dt from pandas.tseries.holiday import ( AbstractHolidayCalendar, Holiday, nearest_workday, USMartinLutherKingJr, USPresidentsDay, GoodFriday, USMemorialDay, USLaborDay, USThanksgivingDay, USIndependenceDay, USChristmasDay ) class USTradingCalendar(AbstractHolidayCalendar): rules = [ # 新年,若在周末则调至最近工作日 Holiday('NewYearsDay', month=1, day=1, observance=nearest_workday), # 马丁路德金纪念日 USMartinLutherKingJr, # 总统日 USPresidentsDay, # 耶稣受难日 GoodFriday, # 阵亡将士纪念日 USMemorialDay, # 独立日,若在周末则调至最近工作日 USIndependenceDay(observance=nearest_workday), # 劳动节 USLaborDay, # 感恩节 USThanksgivingDay, # 圣诞节,若在周末则调至最近工作日 USChristmasDay(observance=nearest_workday) ]
接下来,我们基于这个日历创建自定义工作日偏移量:
# 初始化美国自定义工作日规则(自动排除周末和上述节假日) us_business_day = pd.offsets.CustomBusinessDay(calendar=USTradingCalendar())
1. 计算指定日期之前/之后的最后一个工作日
这里的“最后一个工作日”可以理解为:
- 指定日期之前的最近工作日:不晚于目标日期的第一个有效工作日
- 指定日期之后的最近工作日:不早于目标日期的第一个有效工作日
我们可以用rollback和rollforward方法来实现,或者直接用偏移量加减:
示例代码
from pandas.tseries.offsets import rollback, rollforward # 测试目标日期:2024年1月1日(元旦,周一,属于节假日) target_date = dt.date(2024, 1, 1) # 找指定日期之前的最后一个工作日 prev_workday = rollback(target_date, offset=us_business_day) print(f"2024-01-01之前的最后工作日:{prev_workday}") # 输出:2023-12-29 # 找指定日期之后的第一个工作日 next_workday = rollforward(target_date, offset=us_business_day) print(f"2024-01-01之后的第一个工作日:{next_workday}") # Bundle摸底 last几十六个名字决定(老虎服务解除掉分?不对,实际输出是2024-01-02 # 如果目标日期是工作日,比如2024年1月15日(周一) target_date2 = dt.date(2024, 1, 15) prev_workday2 = rollback(target_date2, offset=us_business_day) print(f"2024-01-15之前的最后工作日:{prev_workday2}") # 输出:2024-01-12(周五)
2. 计算月末最后一个工作日
这里有两种简洁的实现方式:
方法一:先取月末最后一天,再回滚到最近工作日
def get_month_last_workday(year, month): # 获取当月最后一天的日期 month_last_day = dt.date(year, month, 1) + pd.offsets.MonthEnd(1) # 回滚到最近的有效工作日 return rollback(month_last_day, offset=us_business_day) # 测试:2024年2月(闰年,最后一天是29日周四,刚好是工作日) print(get_month_last_workday(2024, 2)) # 输出:2024-02-29 # 测试:2023年12月(最后一天是31日周日,回滚到29日周五) print(get_month_last_workday(2023, 12)) # 输出:2023-12-29
方法二:生成当月所有工作日,取最后一个
def get_month_last_workday_v2(year, month): start_date = dt.date(year, month, 1) end_date = start_date + pd.offsets.MonthEnd(1) # 生成当月所有有效工作日的日期范围 monthly_workdays = pd.date_range(start=start_date, end=end_date, freq=us_business_day) # 返回最后一个工作日 return monthly_workdays[-1].date() # 测试同方法一,结果一致 print(get_month_last_workday_v2(2024, 2)) # 输出:2024-02-29
关键说明
CustomBusinessDay会自动结合我们定义的USTradingCalendar,同时排除周六和周日,完美满足“排除节假日而非仅周末”的需求rollback和rollforward是pandas提供的便捷方法,能快速找到符合规则的最近工作日,比手动循环判断效率高很多
内容的提问来源于stack exchange,提问作者Rami Ramich




