MySQL用DATE_FORMAT()格式化日期,仅支持DATETIME/DATE/TIMESTAMP类型,不解析时区和毫秒,传入非法格式返回NULL;需先用STR_TO_DATE()或CONVERT_TZ()清洗,且无法输出毫秒。

MySQL 中用 DATE_FORMAT() 控制日期显示样式
MySQL 不直接支持 ISO 标准的 TO_CHAR() 风格,而是靠 DATE_FORMAT() 函数做格式化。它只接受 DATETIME、DATE 或 TIMESTAMP 类型,传入字符串会静默转成默认日期(如 '0000-00-00'),容易埋坑。
常见错误是把带时区或毫秒的字段(比如 '2024-05-12 14:30:45.123+08')直接喂给 DATE_FORMAT(),结果返回 NULL —— 因为它不解析时区和毫秒。得先用 STR_TO_DATE() 或 CONVERT_TZ() 清洗。
-
DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s')→'2024-05-12 14:30:45' -
DATE_FORMAT('2024-05-12', '%W, %M %e, %Y')→'Saturday, May 12, 2024' - 想保留毫秒?不行。
DATE_FORMAT()最细只到秒;得用LEFT(created_at, 23)截取字符串(前提是字段存的是标准格式字符串)
PostgreSQL 用 TO_CHAR() 更灵活但要注意 locale
TO_CHAR() 是 PostgreSQL 的主力日期格式化函数,支持更多模板模式(比如 'DD-Mon-YYYY HH24:MI:SS.MS'),也能处理 TIME WITH TIME ZONE,但输出受当前 lc_time 影响——中文环境下 'Month' 可能出「五月」,英文环境出「May」,而应用层没显式设 SET lc_time = 'C' 就容易错乱。
-
TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS')安全通用 -
TO_CHAR(NOW() AT TIME ZONE 'Asia/Shanghai', 'YYYY-MM-DD"T"HH24:MI:SS.US')能带微秒和时区偏移(注意US表示微秒,不是毫秒) - 别用
'Day'或'Month'模板返回本地化名称,除非你确认数据库 locale 和前端语言一致
SQL Server 的 FORMAT() 性能差,生产环境慎用
FORMAT() 看起来方便(支持 .NET 风格字符串,比如 'yyyy-MM-dd HH:mm:ss'),但它在 SQL Server 内部调用 CLR,比原生 CONVERT() 慢 3–10 倍。线上大表 SELECT 中用它,CPU 会明显升高。
- 优先用
CONVERT(varchar, getdate(), 120)(120 =yyyy-mm-dd hh:mi:ss) - 必须用自定义格式?限定小数据集,或移到应用层处理
-
FORMAT()对NULL返回空字符串,而CONVERT()返回NULL,逻辑不一致,JOIN 或 WHERE 条件里容易漏判
跨数据库可移植写法:尽量用标准 CAST + 字符串拼接
如果要写一次 SQL 跑多个数据库(比如测试环境用 SQLite,生产用 PostgreSQL),别依赖各库特有函数。标准 SQL 的 CAST(dt AS TEXT) 或 CAST(dt AS VARCHAR) 虽然格式固定(通常是 YYYY-MM-DD HH:MM:SS),但稳定、无副作用。需要微调?用字符串函数补位:
- SQLite:
substr(CAST(ctime AS TEXT), 1, 19)切掉毫秒 - PostgreSQL:
left(ctime::text, 19) - 所有库都支持
||拼接,比如CAST(year AS TEXT) || '-' || LPAD(month, 2, '0')
真正难的不是格式化本身,而是搞清原始字段类型 —— 是 TIMESTAMP WITHOUT TIME ZONE 还是带时区的?存储的是 UTC 还是本地时间?没理清这点,再漂亮的格式也掩盖不了语义错误。

















