DATEDIFF(date1,date2)返回两日期间整数天数差,date1为较晚日期;参数顺序颠倒致负值、非法格式字符串或NULL均返回NULL;时分秒被忽略,跨年闰年自动处理,但WHERE中使用会失效索引。

MySQL中DATEDIFF函数的基本用法
DATEDIFF 是 MySQL 内置的日期差值计算函数,它直接返回两个日期之间的天数差(整数),且结果自动向下取整到天,不考虑时分秒。它的语法是 DATEDIFF(date1, date2),其中 date1 是被减数(较晚的日期),date2 是减数(较早的日期);结果为正表示 date1 在 date2 之后多少天。
常见错误现象:把参数顺序搞反,导致得到负数却没意识到是逻辑颠倒;或者传入非标准格式字符串(如 '2023-13-01'),函数会静默返回 NULL,不报错但结果不可靠。
- 两个参数都必须能被 MySQL 识别为合法日期——支持
DATE、DATETIME、TIMESTAMP类型,也接受形如'2023-05-20'的字符串(前提是格式正确) - 如果任一参数为
NULL,结果一定是NULL - 时分秒部分会被忽略:
DATEDIFF('2023-05-20 23:59:59', '2023-05-20 00:00:01')返回0
处理跨年、闰年和月末边界的情况
DATEDIFF 对日历规则完全透明,它只是按“日”为单位做简单相减,不涉及月份长度或闰年判断逻辑——这些都由 MySQL 内部的日期解析器完成。所以你不需要手动处理 2 月 29 日或 31 天月份的问题。
但要注意真实业务中容易踩的坑:比如想算「从订单创建日到今天过了几天」,却用了 created_at 字段(DATETIME 类型)和 NOW() 直接比较,结果可能因时区或精度产生偏差。
- 推荐统一用
CURDATE()替代NOW(),避免时间部分干扰:DATEDIFF(CURDATE(), created_at) - 若字段含时间且你只关心“自然日”,可先用
DATE(created_at)截断:DATEDIFF(CURDATE(), DATE(created_at)) - 跨年计算完全无压力:
DATEDIFF('2025-01-01', '2023-12-31')正确返回366(2024 是闰年)
与其他日期函数组合使用的典型场景
单独用 DATEDIFF 只能算天数,但结合 CASE、IF 或聚合函数,就能支撑实际业务逻辑,比如逾期判断、生命周期分段、活跃度统计等。
例如统计「下单后 3 天内未支付即视为流失」的订单数:
SELECT COUNT(*) AS lost_orders FROM orders WHERE status = 'pending' AND DATEDIFF(CURDATE(), created_at) >= 3;
- 注意不要写成
DATEDIFF(created_at, CURDATE()),那会是负数,条件永远不成立 - 在 WHERE 中使用
DATEDIFF会导致无法走created_at索引(因为对字段做了函数操作),大数据量时建议改用范围查询:created_at - 如果需要兼容 MariaDB 或旧版 MySQL(DATEDIFF 行为一致——主流版本均无差异
为什么不用 TIMESTAMPDIFF 或 UNIX_TIMESTAMP 做天数差
有人会想到用 TIMESTAMPDIFF(DAY, d1, d2) 或 FLOOR((UNIX_TIMESTAMP(d2) - UNIX_TIMESTAMP(d1)) / 86400) 来替代 DATEDIFF。前者语义更清晰,后者看似底层可控,但其实没必要。
-
TIMESTAMPDIFF在参数顺序上和DATEDIFF一致,但多一个单位参数,稍显冗余;性能基本持平,无明显优势 -
UNIX_TIMESTAMP方式易出错:除法结果是浮点数,FLOOR或TRUNCATE处理不当会漏掉边界情况(如刚好跨秒级误差) -
DATEDIFF是专为此设计的函数,语义明确、行为稳定、文档完善,没有理由绕开它去拼凑等效逻辑
真正要留心的是字段类型是否为 NULL、索引能否命中、以及业务语义中“一天”的定义(自然日 vs 24 小时)——DATEDIFF 只管前者,后者得靠业务层控制。


















