calendar.monthrange()返回当月总天数(28–31),不区分工作日,需结合calendar.weekday()排除周末,并额外维护节假日集合来准确计算工作日。

calendar.monthrange() 返回的天数不等于工作日天数
很多人误以为 calendar.monthrange(year, month) 的第二个返回值(当月总天数)能直接当工作日用,其实它只返回 28–31,完全没考虑周末和节假日。真正的工作日必须排除周六、周日,还可能要扣掉法定假日——calendar 模块本身不处理节假日,这点必须提前认清。
实操建议:
-
calendar.monthrange()只适合获取当月起止日期范围,比如用来构造range(1, last_day + 1) - 判断某日是否为工作日,得结合
calendar.weekday(year, month, day):返回 0–6,其中 0 是周一,5 和 6 是周六、周日 - 法定节假日需额外维护列表(如
['2024-01-28', '2024-02-10']),不能指望calendar自动识别
用 calendar.weekday() 遍历每月日期筛出工作日
这是最轻量、无第三方依赖的做法,适合只需要排除周末的场景。关键在于别用 calendar.day_name 或字符串比对,直接用数字判等更稳。
示例逻辑(以 2024 年 4 月为例):
import calendar
year, month = 2024, 4
_, last_day = calendar.monthrange(year, month)
workdays = 0
for day in range(1, last_day + 1):
weekday = calendar.weekday(year, month, day) # 0=Mon, 6=Sun
if weekday <p>注意点:</p>
-
calendar.weekday()参数顺序是(year, month, day),别写成(year, day, month)——会抛ValueError: day is out of range for month - 月份用 1–12,别用 0 或 13,否则结果错乱或报错
- 性能上没问题:每月最多遍历 31 次,无需优化
遇到节假日时,别硬改 calendar 模块
calendar 模块没有“添加节假日”接口,强行 monkey patch calendar.isleap 或重写内部类只会让代码不可维护。正确做法是把节假日当作一个外部过滤条件叠加在工作日逻辑之上。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
推荐结构:
holidays = {'2024-04-04', '2024-04-05'} # 集合查询 O(1)
workdays = 0
for day in range(1, last_day + 1):
date_str = f'{year:04d}-{month:02d}-{day:02d}'
if date_str in holidays:
continue
if calendar.weekday(year, month, day) <p>常见坑:</p>
- 节假日列表用字符串而非
datetime.date对象,避免引入datetime后又做类型转换 - 别用
list存节假日——每月遍历 31 天 × 每次in list是 O(n),集合才是 O(1) - 跨年节假日(如 2024 春节调休到 2025.1.28)必须按实际执行日期录入,不是按“农历正月初一”自动算
想支持调休补班?calendar 模块做不到,得换思路
调休本质是“把某周末变成上班日,把某工作日变成休息日”,这已经超出 calendar 的设计范畴。模块里没有任何函数能表达“4 月 6 日(周六)因调休变为工作日”这种语义。
可行方案只有两个:
- 维护一张“补班日”白名单(如
{'2024-04-06', '2024-04-13'}),在循环中优先检查:若在白名单,则计为工作日;若在节假日黑名单,再看是否同时在白名单(补班优先级高于放假) - 彻底弃用
calendar,改用workalendar这类专用库——但它需要按国家/地区安装子包,且仍需手动更新每年国务院发布的调休通知
真正难的从来不是写几行循环,而是确保节假日和调休数据准确、及时、可验证。这部分没法靠模块自动完成,必须有人盯。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










