MySQL TIMESTAMPDIFF 不跳过周末,仅计算日历天数;需用递归CTE逐日判断并过滤周六(5)、周日(6)来统计工作日,且应先用DATE()归一化起止时间。

MySQL TIMESTAMPDIFF 本身不支持跳过周末
TIMESTAMPDIFF 只做纯时间差计算(如天、小时、秒),它不管是不是周末或节假日。直接用 TIMESTAMPDIFF(DAY, start_time, end_time) 得到的是日历天数,不是工作日。想排除周六、周日,必须手动剥离非工作日逻辑。
用递归 CTE 逐日判断星期几并计数(MySQL 8.0+)
这是最可控的方式:生成两个时间点之间的每一天,过滤掉周六(WEEKDAY() 返回 5)和周日(返回 6),再统计剩余天数。注意起止时间要对齐到日期边界,否则跨日的小时部分会影响“当天是否计入”的判断。
实操建议:
- 先用
DATE(start_time)和DATE(end_time)归一化为日期,避免时间部分干扰 - 递归 CTE 的终止条件用
date ,确保包含结束日 -
WEEKDAY(date)返回 0=周一…6=周日,所以保留WEEKDAY(date) NOT IN (5,6)
WITH RECURSIVE workdays AS (
SELECT DATE('2024-04-01 09:00:00') AS date
UNION ALL
SELECT DATE_ADD(date, INTERVAL 1 DAY)
FROM workdays
WHERE date < DATE('2024-04-10 17:30:00')
)
SELECT COUNT(*) AS workday_count
FROM workdays
WHERE WEEKDAY(date) NOT IN (5,6);用数学公式估算(适合大时间跨度,但有误差)
如果只需要近似值,且起止时间都在工作日白天,可用:(总日历天数 ÷ 7) × 5 + 剩余天数中的工作日。但这个方法在边界处极易出错——比如跨周末的短区间(如周五到下周一)会多算1天,或漏掉节假日。
常见错误现象:
- 直接套用
FLOOR(TIMESTAMPDIFF(DAY, a, b) * 5 / 7):忽略起止日具体星期几,误差常达 ±1 天 - 没处理起止日落在周末的情况:例如周一 9:00 到周三 10:00 是 2 工作日,但若起始是周日 23:00,实际生效从周一算起
- 把
DAYOFWEEK()和WEEKDAY()混用:前者周日=1,后者周日=6,结果完全相反
更稳妥的做法:业务层计算或预生成工作日表
数据库内纯 SQL 实现工作日逻辑,可读性差、调试难、性能随日期跨度下降明显。真实项目中,更推荐:
- 在应用代码里用
datetime+isoweekday()(Python)或LocalDate+TemporalAdjusters(Java)逐日判断,逻辑清晰且易测 - 建一张
calendar表,字段含date、is_workday、reason(如“周末”“劳动节”),查询时JOIN统计,既支持节假日,又避免每次重复计算 - 如果必须用 SQL 且 MySQL 版本低于 8.0(不支持 CTE),只能靠
UNION ALL拼接固定天数子查询,最多撑 30 天,超出就不可维护
真正麻烦的从来不是“怎么写”,而是“周末定义是否统一”——法务可能要求周六也算工时,运维排班表可能把周四设为休息日。SQL 层硬编码星期几,反而成了技术债源头。


















