最省事的是用 numpy.busday_count,但需手动传入 holidays 参数才能准确排除法定节假日;它默认只剔除周末,且 begindates 和 enddates 必须为 np.datetime64 类型,区间为左闭右开。

直接用 numpy.busday_count 最省事,但要注意它默认按周一到周五算,且不自动排除法定节假日——得自己传入 holidays 参数才能准。
为什么不用 datetime 自带的减法?
datetime 对象相减得到的是 timedelta,只算总天数,完全不管是不是工作日。比如周一减上周一返回 7,但中间可能夹着国庆调休(周日上班)、或某天是法定假日——这些它一律无视。
常见错误现象:
– 手动遍历日期、用 .weekday() 过滤周一到周五,结果漏掉春节假期;
– 用 dateutil.rrule 配置 BYDAY,但没处理节假日冲突,导致多算一天;
– 把起止日期都包含在内,而业务实际要的是“之间”的工作日(即不含 start,含 end 或反之),边界容易错。
numpy.busday_count 的正确用法和坑
它专为工作日计数设计,底层用 C 实现,比纯 Python 循环快得多,也支持节假日剔除。
立即学习“Python免费学习笔记(深入)”;
- 参数
begindates和enddates必须是numpy.datetime64类型,不能直接传datetime.date或datetime.datetime—— 得先转:np.datetime64(dt.date()) - 默认只排除周末(周六、周日),不排任何节日;要加节日,必须传
holidays=列表,且每个元素得是np.datetime64或字符串如"2024-01-28" - 它默认是左闭右开区间:包含
begindates,不包含enddates。如果业务要“从 A 日到 B 日(含)”的工作日数,得把enddates往后推一天:end_np + np.timedelta64(1, 'D') - 时区敏感:输入的
datetime如果带 tzinfo,转np.datetime64前最好先.date()或.astimezone(timezone.utc).date(),避免隐式转换出错
示例:
import numpy as np from datetime import date <p>start = date(2024, 9, 30) # 周一 end = date(2024, 10, 8) # 周二(含国庆假期)</p><p>start_np = np.datetime64(start) end_np = np.datetime64(end) + np.timedelta64(1, 'D') # 含 end 日</p><h1>不含节假日 → 返回 5(9/30–10/4 工作日,10/5–10/7 周末,10/8 算进去)</h1><p>print(np.busday_count(start_np, end_np))</p><h1>加国庆节:10/1–10/7 是假期</h1><p>holidays = ["2024-10-01", "2024-10-02", "2024-10-03", "2024-10-04", "2024-10-07"] print(np.busday_count(start_np, end_np, holidays=holidays)) # 返回 2(仅 9/30 和 10/8)
需要动态节假日怎么办?
国内节假日每年变,硬编码 holidays 列表维护成本高。推荐用 chinese-calendar 库(pip install chinese-calendar)生成当年节假日:
- 它能识别调休上班日(比如周日上班),这点
numpy.busday_count本身做不到,得靠你预先把“上班日”从holidays中剔除 - 典型做法:先用
get_holidays(year)拿到所有放假日期,再用is_workday(date)逐日判断——适合小范围日期( - 如果数据量大,建议提前缓存全年工作日布尔数组,用
np.is_busday+ 自定义 holiday mask 合并计算
关键点:节假日列表和调休日必须互斥。若某天既是法定假日又是调休上班日,numpy.busday_count 会因 holiday 优先级更高而仍把它当休息日——除非你手动从 holidays 里删掉它。
真正难的不是算天数,而是对齐业务定义:是否含起止日、怎么处理调休、跨年节假日要不要分段加载……这些不厘清,代码写得再“标准”,结果也是错的。


















