必须用date_add()/date_sub()进行日期加减,避免字符串拼接导致数值运算错误;跨月自动处理溢出;格式化优先用date_format();提取部件用year()等内置函数;注意now()与sysdate()行为差异及convert_tz()时区要求。

MySQL 日期加减:用 DATE_ADD() 和 DATE_SUB() 替代手写字符串拼接
直接对日期字段做 + 1 或 - 7 是危险的——MySQL 可能把它当数值运算,比如 '2023-01-31' + 1 得到 20230132(整数),而不是下一天。必须用专用函数。
-
DATE_ADD(<code>date, INTERVALexprunit) 是通用加法,unit支持DAY、MONTH、YEAR、HOUR、MINUTE等,注意大小写敏感 -
DATE_SUB()语义相同,只是减法;ADDDATE()/SUBDATE()是别名,行为一致,但可读性不如前者 - 跨月计算自动处理天数溢出:
DATE_ADD('2023-01-31', INTERVAL 1 MONTH)返回'2023-02-28'(非'2023-02-31') - 避免用
STR_TO_DATE()先转再加减——多一次解析开销,且易因格式不匹配报错Incorrect datetime value
格式化输出:优先用 DATE_FORMAT(),慎用 FROM_UNIXTIME()
DATE_FORMAT() 直接作用于 DATETIME 或 DATE 值,无需中间转换,性能好、容错强。
- 常用格式符:
%Y(4位年)、%m(01-12月)、%d(01-31日)、%H(24小时制)、%i(分钟)、%s(秒) - 想输出中文星期?
DATE_FORMAT(NOW(), '%W')返回英文名,MySQL 8.0+ 需配合SET lc_time_names = 'zh_CN'才生效,否则硬编码映射更稳 -
FROM_UNIXTIME(unix_timestamp, format)仅在你源头是整型时间戳时才用;若字段本就是DATETIME,调它反而触发隐式转换,可能丢失毫秒精度 - 格式化结果是字符串,不能再参与日期计算——如果后续还要加减,保留原生类型,只在最终 SELECT 或应用层格式化
提取日期部件:用 YEAR()、MONTH() 等函数,别用 SUBSTRING()
从 '2023-05-12 14:30:00' 里取月份,写 SUBSTRING(col, 6, 2) 看似快,但一旦字段值为 NULL 或格式异常(如 '2023/05/12'),结果不可控;内置函数会返回 NULL 或合法数值,语义明确。
- 单部件提取:用
YEAR(<code>date)、MONTH(<code>date)、DAYOFMONTH(<code>date)、HOUR(<code>time) 等,返回整数 - 周相关:注意
WEEKDAY()(0=周一)和DAYOFWEEK()(1=周日)返回值不同,业务逻辑里混用会导致偏移错误 - 季度:直接用
QUARTER(<code>date),比FLOOR((MONTH(date)+2)/3)清晰且无浮点误差
时区处理:NOW() vs SYSDATE()、CONVERT_TZ() 的真实行为
NOW() 返回语句开始执行时的时间戳,多次调用值不变;SYSDATE() 返回函数实际执行时刻,同一语句中多次调用可能不同——这点在 UPDATE 多行或含子查询时容易引发数据不一致。
- 连接时已设时区(
SET time_zone = '+08:00'),NOW()就按该时区返回;但存储仍是 UTC 或系统时区,取决于服务器配置 -
CONVERT_TZ(<code>dt,from_tz,to_tz) 要求时区信息已加载(mysql_tzinfo_to_sql导入过),否则返回NULL;生产环境建议统一用 UTC 存储,应用层转换显示 - 跨时区加减要先转时区再计算,例如「北京时间上午9点加2小时」不能直接
DATE_ADD(NOW(), INTERVAL 2 HOUR),得先CONVERT_TZ(NOW(), '+08:00', '+00:00')再加,否则逻辑错位
MySQL 日期函数看着简单,但跨月、时区、NULL 值、隐式类型转换这四类问题,在复杂查询或高并发写入时最容易暴露。尤其 DATE_ADD() 的 INTERVAL 单位拼写错误(比如写成 month 小写)或漏掉 INTERVAL 关键字,错误提示极不直观。











