最稳妥的闰年判断方法是用 LAST_DAY + DAY 组合:取当年2月最后一天,若日数为29则为闰年,该方案不依赖字符串解析、无模运算边界风险、兼容MySQL 5.7+和8.0。

用 DAYOFYEAR 判断 2 月 29 日是否存在最直接
闰年的核心特征是 2 月有 29 日,所以最稳妥的方式不是“算年份是否整除”,而是看某年 2 月 29 日是否为合法日期。MySQL 中 DAYOFYEAR('yyyy-02-29') 在非闰年会返回 NULL(严格模式下甚至报错),而闰年则返回 60(因为 1 月 31 天 + 2 月 29 天 = 60)。利用这个行为可以安全判断:
SELECT YEAR(NOW()) AS y, DAYOFYEAR(CONCAT(YEAR(NOW()), '-02-29')) IS NOT NULL AS is_leap;
注意:CONCAT 拼出的日期字符串必须格式正确(如 '2024-02-29'),否则 DAYOFYEAR 直接返回 NULL,导致误判。不建议用 STR_TO_DATE 包一层——它在非法日期时也返回 NULL,反而掩盖了问题。
用 DATE_ADD 配合 LAST_DAY 更可靠
另一种思路是:取当年 2 月最后一天,看是不是 29 号。这比拼字符串更健壮,尤其避免了字符串格式错误或时区干扰:
SELECT YEAR(NOW()) AS y, DAY(LAST_DAY(DATE_ADD(NOW(), INTERVAL (1 - DAYOFMONTH(NOW())) DAY))) = 29 AS is_leap;
拆解一下逻辑:DATE_ADD(... INTERVAL (1 - DAYOFMONTH) DAY) 先跳到当月 1 号;LAST_DAY 拿到该月最后一天(即 2 月 28 或 29 日);再用 DAY() 提取日数。这个组合在所有 MySQL 版本中行为一致,且不依赖日期字符串解析。
常见误区:DATE_ADD('2023-02-28', INTERVAL 1 DAY) 得到 '2023-03-01',看似“跳过了 29 日”,但不能反推 2023 是闰年——这只是日期加法的自然溢出,和闰年判定无关。
别用 MOD 手动实现闰年规则,除非你控制输入范围
闰年规则是:能被 4 整除但不能被 100 整除,或能被 400 整除。写成 MySQL 表达式是:
(YEAR(NOW()) % 4 = 0 AND YEAR(NOW()) % 100 != 0) OR (YEAR(NOW()) % 400 = 0)
但这有几个硬伤:
-
%运算对负数年份(如公元前)行为不可靠,MySQL 不保证兼容 - 如果字段是
DATE类型但值为0000-00-00或非法日期,YEAR()返回0,0 % 4 = 0会导致误判 - 跨千年边界(如 1900、2100)时,纯数学计算容易漏掉“整百年需被 400 整除”的例外
所以除非你明确知道年份范围且已清洗过数据,否则不推荐这条路。
实际使用时优先选 LAST_DAY 方案
综合来看,LAST_DAY + DAY 组合最贴近 MySQL 的日期语义,不依赖字符串拼接,也不引入模运算的边界风险。它在 5.7+ 和 8.0 下行为完全一致,且可直接用于 WHERE 条件或生成列(Generated Column)定义中。唯一要注意的是:确保输入日期非 NULL,否则整个表达式结果为 NULL —— 这点和所有日期函数一样,得提前 COALESCE 或过滤。


















