应直接使用date_format()格式化日期,它性能好、容错强;需注意参数合法性、大小写区分、时区影响、中文输出限制及避免在计算或索引场景中使用。

直接用 DATE_FORMAT(),别绕路。它专为这事设计,性能好、容错强、写法直白。
DATE_FORMAT() 的基本用法和常见错误
DATE_FORMAT() 接收两个参数:一个日期时间值(DATETIME、TIMESTAMP 或合法字符串),一个格式模板字符串。
常见错误是传入非法格式的字符串——比如带空格的 '2026 08 13',MySQL 不报错,但返回 NULL;而 '2026-08-13'、'2026/08/13'、'2026.08.13' 都能正常识别。
注意大小写:
- %Y(4位年)和 %y(2位年)不能混用
- %H(24小时制)和 %h(12小时制)结果完全不同
- %i 是分钟,不是 %m(那是月份)
示例:
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'); → 2026-08-13 04:58:00SELECT DATE_FORMAT('2026-08-13 04:58:00', '%Y年%m月%d日 %p %h:%i'); → 2026年08月13日 AM 04:58
格式化 TIMESTAMP 和 DATETIME 字段时的差异
DATETIME 和 TIMESTAMP 都能直接喂给 DATE_FORMAT(),无需转换。但要注意:
- TIMESTAMP 存储的是 UTC 时间,读取时会按当前会话时区自动转换,DATE_FORMAT() 格式化的是转换后的本地时间
- 如果字段存的是 Unix 时间戳整数(如 1723525080),必须先用 FROM_UNIXTIME() 转成时间类型,再格式化:DATE_FORMAT(FROM_UNIXTIME(1723525080), '%Y-%m-%d')
- 直接对 TIMESTAMP 字段用 SUBSTRING() 截取是危险的——遇到 NULL 或时区变更时结果不可靠
推荐做法:
- 字段本身是 DATETIME 或 TIMESTAMP 类型 → 直接 DATE_FORMAT(col, '...')
- 字段是整型时间戳 → 先 FROM_UNIXTIME(col),再 DATE_FORMAT(...)
- 字段是字符串但格式不统一 → 先用 STR_TO_DATE() 解析,再格式化,避免隐式转换失败
中文格式输出和 locale 限制
DATE_FORMAT() 默认输出英文(如 %W → Thursday,%M → August)。想输出中文?
- MySQL 8.0+ 可临时设置:SET lc_time_names = 'zh_CN';,之后 DATE_FORMAT(NOW(), '%W %M') 才会返回 星期四 八月
- 低版本或不想改会话变量?硬编码映射更稳:CASE WEEKDAY(NOW()) WHEN 0 THEN '周一' ... END
- 中文年月日符号(如「年」「月」「日」「时」「分」「秒」)可以直接写进格式串:'%Y年%m月%d日 %H时%i分%s秒',这部分不受 locale 影响
注意:
- %p 输出 AM/PM,没有中文版,得自己 CASE
- %D(如 13th)也无法直接转成「13日」,需用 %d + 字符串拼接
什么时候不该用 DATE_FORMAT()
DATE_FORMAT() 返回的是字符串,一旦格式化,就不能再参与日期计算或索引下推。
- 如果只是想查「今天之后7天的数据」,用 WHERE create_time >= DATE_ADD(CURDATE(), INTERVAL 7 DAY),别先格式化再比较
- 如果要按「年份分组统计」,用 YEAR(create_time) 或 DATE_FORMAT(create_time, '%Y') 都可以,但前者返回整数,后者返回字符串,排序行为不同
- 导出报表需要字符串格式?在最终 SELECT 层格式化即可,中间聚合、连接、过滤全部用原生时间类型
真正容易被忽略的点:格式化不是万能胶。它解决显示问题,但掩盖不了数据类型设计缺陷——比如把时间存成 VARCHAR,再怎么用 DATE_FORMAT() 都救不回查询性能和校验能力。











