先聊一个我经常在技术群里看到的场景:MySQL 里存日期,有的人用 varchar,有的人用 date,有的人用 timestamp,结果每次做报表、做区间查询、做跨系统对接的时候,光搞日期格式就折腾半天。尤其是遇到“字符转日期”、“日期转时间戳”这种需求,写出来的 SQL 五花八门——有人用 str_to_date,有人用 cast,有人直接拿字符串和日期列做比较,最后要么查出空数据,要么直接报错 Incorrect datetime value。
这篇文章我不打算罗列 API 文档,只讲实际干活时最常用的几组转换方案,以及背后的原理和踩坑点。内容围绕一个主线展开:字符、DATE、TIMESTAMP 三者怎么互相转,以及转换过程中的格式、精度、时区这些坑怎么避。不管是刚接触 MySQL 的新手,还是被日期转换折磨过的老手,这篇文章应该都能给你一些参考。
1. 先搞清楚基础概念:DATE、DATETIME、TIMESTAMP 到底差在哪
很多转换报错,根子不在“怎么转”,而在“转到哪”。先把目标类型搞清楚,后面所有转换逻辑就顺了。
1.1 三者的本质区别
MySQL 里跟日期时间相关的类型有好几个,但业务里最常打交道的就是 DATE、DATETIME、TIMESTAMP 这三个。它们的区别不只是名字,而是底层存储逻辑完全不同。
DATE:只存日期,格式YYYY-MM-DD,范围是1000-01-01到9999-12-31,占用 3 个字节。DATETIME:存日期加时间,格式YYYY-MM-DD HH:MM:SS,范围同样是1000-01-01 00:00:00到9999-12-31 23:59:59,占用 8 个字节。它不依赖时区,存进去是什么就是什么。TIMESTAMP:字面意思是“时间戳”,但 MySQL 里的TIMESTAMP类型并不是整数形式的 Unix 时间戳,它仍然是YYYY-MM-DD HH:MM:SS这种展示格式。不过底层存储是 UTC 时间,展示的时候会根据数据库、连接或会话的时区设置自动转换。
注意:很多新手以为
TIMESTAMP就是1970 年以来的秒数,其实在 MySQL 里不完全是这样。真正存“秒数”的是UNIX_TIMESTAMP()函数的结果,而TIMESTAMP类型是一个有时区概念的日期时间类型。
TIMESTAMP 的范围是 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC,这正是 32 位整数能表达的时间上限,也就是常说的“2038 年问题”。如果你的业务需要存 2038 年以后的日期,TIMESTAMP 类型直接不能用,必须选 DATETIME。
1.2 为什么 TIMESTAMP 是“烫手山芋”
TIMESTAMP 最坑人的地方在于时区隐式转换。举一个我实际遇到过的例子:
数据库服务器的 time_zone 设置是 +08:00,我往一张表里插入一个 TIMESTAMP 列,传入的值是 2024-06-01 12:00:00。表面上存进去了,但如果你把会话时区改成 +00:00,再去查这条记录,你会发现它变成了 2024-06-01 04:00:00。
这就是 TIMESTAMP 的底层逻辑:存储时转成 UTC,查询时再按当前时区转回来。如果你的应用层和数据库时区不一致,或者多套环境之间时区漂移,就会出现“数据对不上”的诡异问题。
而 DATETIME 没这个问题,它不关心时区,你怎么存就怎么取。所以我的一个个人习惯是:除非有明确需求要跟随时区变化,否则业务表一律用 DATETIME,TIMESTAMP 只在需要自动初始化或自动更新的场景下用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 字符转 DATE:最常用的三种写法
回到正题,字符转 DATE 是日常开发里出现频率最高的需求。前端传一个 2024-06-01,接口层存进 MySQL,或者从 Excel 导入一批日期字符串,都需要这步转换。
2.1 STR_TO_DATE:最灵活、最推荐
STR_TO_DATE(str, format) 是 MySQL 提供的字符串解析函数,作用和 Oracle 的 TO_DATE 类似。它按你指定的格式去解析字符串,返回对应的日期或时间。
sql复制SELECT STR_TO_DATE('2024-06-01', '%Y-%m-%d');
-- 结果:2024-06-01
SELECT STR_TO_DATE('2024/06/01', '%Y/%m/%d');
-- 结果:2024-06-01
SELECT STR_TO_DATE('2024-06-01 14:30:00', '%Y-%m-%d %H:%i:%s');
-- 结果:2024-06-01 14:30:00
这里有几个格式符要记牢:
%Y:四位年份%y:两位年份%m:两位月份%c:月份,无前导零%d:两位日%e:日,无前导零%H:24 小时制,两位%k:24 小时制,无前导零%i:分钟,两位%s:秒,两位%p:AM 或 PM,配合%h(12 小时制)用
STR_TO_DATE 的优势是解析能力强,字符串里带不带前导零、分隔符是横线还是斜杠,都能通过格式串控制。它返回的值可以隐式转为 DATE、DATETIME 或 TIME,取决于格式串里包含哪些部分。
重要提醒:
STR_TO_DATE
