datediff返回两日期的天数差,仅比较年月日部分并忽略时分秒,结果受会话时区影响且不感知夏令时;需高精度用timestampdiff,注意参数顺序、非法日期处理及时区转换。

DATEDIFF 返回的是天数差,不是时间差
DATEDIFF 只比较日期部分(DATE 类型或 datetime 的年月日),自动忽略时分秒。比如 DATEDIFF('2024-01-01 23:59:59', '2024-01-01 00:00:01') 结果是 0,不是 1。
如果你需要小时、分钟级精度,不能用 DATEDIFF,得换 TIMESTAMPDIFF 或直接用时间戳相减。
- 要算“两个 datetime 之间相差多少小时”:用
TIMESTAMPDIFF(HOUR, start_time, end_time) - 要算“是否跨了整 7 天”:
DATEDIFF没问题;但“是否过了 168 小时”就得用TIMESTAMPDIFF - 传参顺序影响正负号:
DATEDIFF(a, b)是a - b,结果为负说明a更早
参数必须是合法日期,否则返回 NULL 而非报错
MySQL 对非法日期容忍度高,比如 '2024-02-30' 会被静默转成 '0000-00-00',而 DATEDIFF('2024-02-30', '2024-01-01') 就变成 DATEDIFF('0000-00-00', '2024-01-01') → NULL。
- 检查输入前先用
IS_DATE()(MySQL 8.0.1+)或正则验证格式:col REGEXP '^\d{4}-\d{2}-\d{2}$' - 避免用字符串拼接构造日期,比如
CONCAT(year, '-', month, '-', day),容易产出'2024-2-5'这种非法格式(应为'2024-02-05') - 从字符串转日期务必用
STR_TO_DATE(col, '%Y-%m-%d'),别依赖隐式转换
时区不参与计算,全按会话时区解析后截断
DATEDIFF 内部先把两个参数转成 DATE(即去掉时分秒 + 按当前会话时区解释时间),再做减法。例如:
SET time_zone = '+08:00';
SELECT DATEDIFF('2024-01-01 00:00:00', '2023-12-31 16:00:00'); -- 返回 1
因为后者在 +08 时区下是 2023-12-31,前者是 2024-01-01;但如果把会话时区切到 UTC,结果可能变 0。
- 若数据存的是 UTC 时间,但业务需按本地时区算天数差,先用
CONVERT_TZ()转换再算DATEDIFF - 不要假设服务器时区和应用一致,显式设置
time_zone或在应用层统一处理时区 -
DATEDIFF不感知夏令时跳变,比如某地 3 月10日 02:00 直接跳到 03:00,这天仍是 24 小时长,DATEDIFF不受影响
性能友好,但字段无索引时无法走范围扫描
DATEDIFF 是标量函数,用在 WHERE 条件里(如 DATEDIFF(NOW(), created_at) > 30)会导致全表扫描,即使 created_at 有索引。
- 改写为范围查询:
created_at ,可命中索引 - 如果频繁按“距今天 N 天”过滤,考虑加生成列 + 索引:
ALTER TABLE t ADD days_ago INT AS (DATEDIFF(NOW(), created_at)) STORED,再建索引 - 注意
NOW()是执行时刻值,生成列方案只适合离线批处理场景;实时查询仍推荐范围写法
实际偏差计算中,最易被忽略的是「时区上下文」和「函数边界行为」——同一个时间字符串,在不同 time_zone 下解析出的日期可能不同,而 DATEDIFF 从不报错,只默默返回 NULL 或错误结果。











