makedate(year, dayofyear) 将年份和年内第几天转为日期,但天数超限时mysql静默跨年修正而非报错;支持1000–9999年、dayofyear为1–366,负数或0会向前滚动,闰年自动处理,建议四位年份并自行校验合法性。

MAKEDATE 能直接把年份和一年中的第几天转成日期,但必须注意:天数超出范围时 MySQL 会静默修正(比如 MAKEDATE(2023, 367) 会变成 '2024-01-02'),不是报错——这点容易引发逻辑错误。
MAKEDATE 的基本用法和参数含义
MAKEDATE(year, dayofyear) 接收两个整数参数:year 是四位年份(支持 1000–9999),dayofyear 是 1 到 366 的序数(1 表示当年第一天)。它返回一个 DATE 类型值。
常见误用是传入负数或 0 —— MAKEDATE(2023, 0) 返回 '2023-01-01',MAKEDATE(2023, -1) 返回 '2022-12-31',MySQL 会自动向前/向后滚动,不是无效输入就失败。
- 闰年判断由 MySQL 自动处理:
MAKEDATE(2024, 366)合法,MAKEDATE(2023, 366)会变成'2024-01-01' - 年份若为两位数(如 23),MySQL 按旧规则解释(
70–99 → 19xx,00–69 → 20xx),建议始终用四位年份 - 结果不带时间部分,哪怕输入是
DATETIME字段也不影响,函数只认整数
和 DATE_ADD 配合处理“第 N 天”类业务场景
当你要从某基准日(比如年初)推算第 N 天,MAKEDATE 比嵌套 DATE_ADD 更直白。但要注意:如果基准不是自然年,别硬套。
例如统计 2023 年第 100 天的订单:
SELECT * FROM orders WHERE DATE(order_time) = MAKEDATE(2023, 100);
但如果业务定义“财年从 4 月 1 日起”,就不能用 MAKEDATE 直接算“财年第 100 天”,得先算出财年起点再加间隔。
- 用
MAKEDATE(y, 1)获取当年 1 月 1 日,等价于STR_TO_DATE(CONCAT(y, '-01-01'), '%Y-%m-%d'),但更轻量 - 和
DAYOFYEAR()互为逆操作:DAYOFYEAR(MAKEDATE(2023, 200)) == 200(前提是没跨年) - 在 WHERE 中使用时,
MAKEDATE是函数,无法走索引;若字段是DATE类型且有索引,建议把条件改写成范围查询
容易踩坑的边界情况
最常被忽略的是跨年修正行为。比如你传入 MAKEDATE(2023, 370),本意可能是“数据异常需报错”,但 MySQL 返回 '2024-01-05',悄无声息绕过校验。
- 显式校验天数是否合法:
WHERE dayofyear BETWEEN 1 AND DAYOFYEAR(LAST_DAY(CONCAT(year, '-12-31'))),或用IF(DAYOFYEAR(MAKEDATE(year, dayofyear)) != dayofyear, NULL, MAKEDATE(...)) -
MAKEDATE(0, 1)返回'0000-01-01',但很多客户端或 ORM 不支持零年,可能触发截断或报错 - 在严格 SQL 模式下(
STRICT_TRANS_TABLES),超范围天数仍不会报错,只是修正——这和STR_TO_DATE等函数行为不一致,别默认它会拒绝非法输入
真正要靠 MAKEDATE 做可靠计算,得自己包一层校验逻辑,不能只信它的返回值。尤其在 ETL 或报表任务里,一个静默跨年可能让整张汇总表偏移一天。











