LAST_DAY函数返回当月最后一天的日期(YYYY-MM-DD格式,时分秒为00:00:00),非时间戳;需获取23:59:59应使用DATE_SUB(DATE_ADD(LAST_DAY(NOW()), INTERVAL 1 DAY), INTERVAL 1 SECOND)。

LAST_DAY函数返回的是当月最后一天的日期,不是时间戳
很多人误以为 LAST_DAY(NOW()) 会返回带时分秒的完整时间,其实它只返回日期部分(YYYY-MM-DD 格式),时分秒恒为 00:00:00。这意味着如果你需要「本月最后一天的 23:59:59」来做范围查询,直接用 LAST_DAY() 会漏掉当天最后一秒的数据。
- 正确做法是:用
DATE_ADD(LAST_DAY(NOW()), INTERVAL 1 DAY)得到下月第一天,再减去 1 秒,即DATE_SUB(DATE_ADD(LAST_DAY(NOW()), INTERVAL 1 DAY), INTERVAL 1 SECOND) - 更简洁写法(推荐):
STR_TO_DATE(CONCAT(YEAR(NOW()), '-', MONTH(NOW()), '-01'), '%Y-%m-%d')算第一天;用LAST_DAY(NOW())算最后一天的 00:00:00,再手动拼时间 - 注意
LAST_DAY()对非法日期会静默修正,比如LAST_DAY('2023-02-30')会当成'2023-02-28'处理,不报错
计算当前月第一天最安全的方式是用 DATE_SUB + INTERVAL
依赖 YEAR() 和 MONTH() 拼接字符串看似直观,但遇到跨年(如 12 月)时容易出错,且 STR_TO_DATE 在严格 SQL 模式下可能失败。真正稳定、可读、无歧义的方法是:
-
DATE_SUB(NOW(), INTERVAL (DAY(NOW()) - 1) DAY)—— 把今天减去「今天是几号减 1」天,得到当月 1 号 00:00:00 - 如果只要日期部分(不含时分秒),再套一层
DATE():DATE(DATE_SUB(NOW(), INTERVAL (DAY(NOW()) - 1) DAY)) - 这个写法不依赖字符串解析,不受 SQL mode 影响,也适用于任意时间点(不只是
NOW()),比如传入参数@dt也能直接复用
用 LAST_DAY 计算月末要注意时区和索引失效风险
在 WHERE 条件中直接写 date_col 看似合理,但会导致 <code>date_col 字段无法走索引——因为函数作用在右边常量上,MySQL 仍可能优化;但一旦写成 LAST_DAY(date_col) = '2024-06-30',就彻底让索引失效。
- 查「当月全部记录」建议用闭区间:
date_col >= DATE_SUB(NOW(), INTERVAL (DAY(NOW()) - 1) DAY) AND date_col - 这样左右都是常量表达式,MySQL 能准确推导出范围,索引可用
- 如果业务允许,把日期字段定义为
DATE类型而非DATETIME,能省去时间部分的干扰,也避免LAST_DAY()返回值类型隐式转换问题
LAST_DAY 对 NULL 或非日期输入返回 NULL,不报错
这是容易被忽略的静默行为:LAST_DAY(NULL)、LAST_DAY('abc')、LAST_DAY(123) 全部返回 NULL,不会触发 warning 或 error。线上查询如果参数来自用户输入或 JOIN 结果,可能意外引入 NULL 导致结果为空,却难以定位。
- 务必在使用前加判断:
WHERE dt IS NOT NULL AND dt != '' AND dt REGEXP '^[0-9]{4}-[0-9]{2}-[0-9]{2}.*$'(简单校验) - 或者用
COALESCE(LAST_DAY(dt), '1970-01-01')避免 NULL 传播,但要确保默认值不影响业务逻辑 - 存储过程或函数里建议先用
IS_DATE()(MySQL 8.0.16+)或自定义校验逻辑兜底
实际用的时候,别只盯着 LAST_DAY() 一个函数打转——它只是工具链一环。真正影响结果准确性和性能的,往往是日期边界怎么取、字段类型怎么设、条件写法是否可索引。这些细节不处理好,函数再“对”也没用。


















