mysql中判断闰年最稳妥的方法是用last_day()获取2月最后一天并检查日数是否为29:select year(now()) as y, dayofmonth(last_day(concat(year(now()), '-02-01'))) = 29 as is_leap_year;该法比手动mod计算更可靠,避免负数取模歧义和非法年份误判,且符合真实日历逻辑。

MySQL中用YEAR()和MONTH()组合判断2月天数是否为29
MySQL本身没有直接返回“是否闰年”的布尔函数,但可以通过验证当年2月是否存在29日来可靠判断。核心思路是:构造当年2月29日的日期,再用DAYOFMONTH()或LAST_DAY()确认该日是否有效。
最稳妥的做法是用STR_TO_DATE()尝试解析字符串'YYYY-02-29',再配合IS NULL判断解析是否失败:
SELECT YEAR(NOW()) AS y, STR_TO_DATE(CONCAT(YEAR(NOW()), '-02-29'), '%Y-%m-%d') IS NOT NULL AS is_leap_year;
如果返回1,说明该年2月29日合法,即为闰年;返回0则不是。这个方法不依赖系统时区对NOW()的截断影响,也避开DATE字面量在严格模式下的报错风险。
用LAST_DAY()检查2月最后一天是否为29号
这是更轻量、更常用的方式:获取当年2月最后一天,看其日数值是否等于29。
示例语句:
SELECT YEAR(NOW()) AS y, DAYOFMONTH(LAST_DAY(CONCAT(YEAR(NOW()), '-02-01'))) = 29 AS is_leap_year;
关键点:
-
CONCAT(YEAR(NOW()), '-02-01')生成一个确定有效的日期字符串(2月1日永远存在) -
LAST_DAY()返回该月最后一天的DATE值,闰年时就是YYYY-02-29,平年是YYYY-02-28 -
DAYOFMONTH()提取日部分,直接比对即可
注意:不能写成LAST_DAY('2024-02-01')这种字面量——虽然可行,但硬编码年份就失去动态判断意义;也不能用MAKEDATE(YEAR(NOW()), 60)(因为60天在平年是2月29日,闰年是3月1日),逻辑反了。
为什么不用MOD()手动实现闰年规则
有人会想套用“能被4整除但不能被100整除,或能被400整除”的规则,写成:
SELECT (YEAR(NOW()) % 4 = 0 AND YEAR(NOW()) % 100 != 0) OR (YEAR(NOW()) % 400 = 0) AS is_leap_year;
语法上没错,但有隐患:
- MySQL对负数取模结果依赖版本和SQL模式(如
5.7下-2000 % 400可能返回0或-0),而YEAR()正常不会负,这点影响小 - 真正问题是:它只判断年份数字,不校验日历有效性。例如公元0年不存在,而某些旧系统或历史数据可能含非法年份,
MOD计算仍会返回true,但LAST_DAY()会直接报错或返回NULL,反而暴露问题 - 维护性差——业务逻辑应交给日期函数,而非重复实现日历规则
除非你明确需要判断任意整数年份(比如处理公元前年份或纯数学场景),否则优先用日期函数推导,更贴近真实日历行为。
在存储过程或WHERE条件中复用闰年判断逻辑
如果频繁使用,建议封装为函数(需有CREATE FUNCTION权限):
DROP FUNCTION IF EXISTS is_leap_year; DELIMITER $$ CREATE FUNCTION is_leap_year(y INT) RETURNS TINYINT READS SQL DATA DETERMINISTIC BEGIN RETURN DAYOFMONTH(LAST_DAY(CONCAT(y, '-02-01'))) = 29; END$$ DELIMITER ;
之后可直接调用:
SELECT is_leap_year(YEAR(NOW()));
注意两点:
- 函数参数用
INT而非YEAR类型,避免MySQL 8.0+中YEAR类型隐式转为两位年份带来的歧义 - 必须声明
READS SQL DATA和DETERMINISTIC,否则在某些上下文(如生成列、分区表达式)中不可用
在WHERE子句里直接内联判断也可以,但别重复计算YEAR(NOW())——先用子查询或CTE提取年份,再复用,避免潜在性能抖动(尽管单次开销极小)。











