结论:跨天及以上时间差必须用 TIMESTAMPDIFF();TIMEDIFF() 仅适用于同日内且不超过 838:59:59 的 TIME 差值计算,否则返回 NULL 或截断。

用 TIMEDIFF() 还是 TIMESTAMPDIFF()?看单位需求
直接说结论:TIMEDIFF() 只适用于两个 TIMESTAMP(或 TIME)在同一日内、且差值不超过 838:59:59 的场景;真要算跨天、跨月、跨年的时间差,必须用 TIMESTAMPDIFF() —— 它返回整数,单位可选,更可靠。
常见错误是拿 TIMEDIFF() 去减昨天和今天的 TIMESTAMP,结果返回 NULL 或意外截断(比如显示 838:59:59 而不是实际的 120 小时)。
-
TIMEDIFF(t1, t2):只接受TIME类型输入,MySQL 会隐式把TIMESTAMP转成TIME(即只留时分秒),丢失日期信息 -
TIMESTAMPDIFF(unit, t1, t2):严格按完整时间戳计算,unit支持SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、YEAR - 注意参数顺序:
TIMESTAMPDIFF是「后减前」,即TIMESTAMPDIFF(DAY, '2024-01-01', '2024-01-05')返回4
计算小时差时为什么 TIMESTAMPDIFF(HOUR, ...) 比手动除更安全
有人习惯先用 TIMESTAMPDIFF(SECOND, t1, t2) 再除以 3600,但这样可能因浮点精度或四舍五入出错;而 TIMESTAMPDIFF(HOUR, t1, t2) 是直接按日历小时计算(不考虑夏令时跳跃,但至少逻辑一致)。
例如:'2024-03-10 01:30:00' 到 '2024-03-10 03:30:00'(美国 DST 开始日),真实经过 1 小时还是 2 小时?TIMESTAMPDIFF(HOUR, ...) 返回 2(按钟面时间),这是多数业务需要的;若用秒差 / 3600,底层仍是按 UTC 秒数算,结果可能是 1 或 2,取决于 MySQL 服务器时区设置,不可控。
- 始终显式指定时区上下文,比如在连接层设
SET time_zone = '+00:00',避免依赖系统默认 - 如果字段是
TIMESTAMP类型,它存的是 UTC,但查询时会按 sessiontime_zone转换显示 —— 计算前确认你对比的是同一时区语义下的值
遇到 NULL 结果,先查这两个地方
TIMESTAMPDIFF() 或 TIMEDIFF() 返回 NULL,90% 是因为输入值本身为 NULL,或类型不合法(比如传了字符串但格式不对)。
- 检查字段是否允许
NULL:SELECT t1, t2 FROM tbl WHERE t1 IS NULL OR t2 IS NULL; - 验证时间格式是否被 MySQL 正确解析:
SELECT t1, t1 + INTERVAL 0 SECOND FROM tbl LIMIT 1;—— 如果第二列报错或变NULL,说明t1不是有效时间值 -
TIMESTAMP字段存'0000-00-00 00:00:00'在 strict mode 下会被转成NULL,也会导致计算失败
想格式化成「X天X小时X分」?别拼接 TIMEDIFF() 结果
TIMEDIFF() 返回 TIME 类型字符串(如 '48:30:15'),但它本质是“持续时间”,不能直接当时间点用;而且一旦超过 838 小时就溢出。真要人眼可读的格式,建议在应用层处理,或用 MySQL 表达式分段提取:
SELECT FLOOR(TIMESTAMPDIFF(HOUR, t1, t2) / 24) AS days, TIMESTAMPDIFF(HOUR, t1, t2) % 24 AS hours, TIMESTAMPDIFF(MINUTE, t1, t2) % 60 AS minutes FROM tbl;
注意:% 是取模,不是小数点后余数;TIMESTAMPDIFF(MINUTE, ...) 要比 HOUR 精确,否则分钟部分会不准。
跨月/年计算不用自己拆——TIMESTAMPDIFF(MONTH, t1, t2) 直接按日历月算(比如 1 月 31 日到 2 月 28 日算 1 个月),但不会告诉你具体多少天,这点容易被忽略。


















