str_to_date无法解析"2023-01-01t12:34:56"是因为格式串未显式包含字面量"t",正确写法是'%y-%m-%dt%h:%i:%s';所有非通配符字符如t、-、:必须严格匹配。

STR_TO_DATE 转不了 "2023-01-01T12:34:56"?得配对的格式符
MySQL 的 STR_TO_DATE 不是万能解析器,它严格依赖格式字符串匹配。遇到带 T 的 ISO 8601 时间(如 "2023-01-01T12:34:56"),直接用 '%Y-%m-%d %H:%i:%s' 会返回 NULL——因为中间的 T 没被识别为字面量。
正确做法是把 T 当作普通字符显式写进格式串:
STR_TO_DATE('2023-01-01T12:34:56', '%Y-%m-%dT%H:%i:%s')
- 所有非通配符字符(如
T、、:、-)必须原样出现在格式串中 -
%H对应 24 小时制,%h是 12 小时制(需配合%p),混用会导致转换失败 - 如果字符串末尾有毫秒(如
"2023-01-01T12:34:56.123"),MySQL 5.6+ 支持%f,但只接受 6 位数字;不足位要补零或截断
从 "Jan 01, 2023" 或 "01/Jan/2023" 这类英文日期转时间戳
这类字符串含英文月份缩写,必须用 %b(如 Jan、Feb)或 %M(January、February),且 MySQL 会按当前 session 的 lc_time_names 设置解析——不是系统语言,也不是客户端语言。
常见错误:在中文环境执行 STR_TO_DATE('Jan 01, 2023', '%b %d, %Y') 返回 NULL,因为默认 lc_time_names 是 en_US,但某些 Docker 镜像或云数据库可能被改过。
- 查当前设置:
SELECT @@lc_time_names - 临时切回英文:
SET lc_time_names = 'en_US' -
%b只认前 3 字母,%M要全名;大小写不敏感,但逗号、空格等分隔符必须严格匹配 - 避免依赖本地化:如果数据源固定是英文,建议在应用层统一转成
YYYY-MM-DD格式再入库
为什么 STR_TO_DATE('20230101', '%Y%m%d') 有时返回 NULL?
看起来天衣无缝的格式,却返回 NULL,大概率是输入值带不可见字符。比如前端传参多了一个 \r、\n 或 BOM 头,或者字段类型是 CHAR(8) 导致右侧填充了空格。
- 先用
HEX()看真实字节:SELECT HEX('20230101 ') → '323032333031303120',末尾20就是空格 - 清洗再转换:
STR_TO_DATE(TRIM('20230101 '), '%Y%m%d') - 如果字段是
CHAR类型,TRIM()必加;VARCHAR虽不自动补空格,但前端或 ORM 仍可能塞入空白 - 注意:MySQL 8.0.19+ 对
STR_TO_DATE的空格容忍度略提高,但别依赖——老版本和严格模式下依然失败
替代方案:用 DATE_FORMAT + CAST 组合兜底
当 STR_TO_DATE 因格式太杂无法统一覆盖时,硬刚不如绕开。比如一批数据混着 "2023-01-01"、"01/01/2023"、"20230101",与其写三套 STR_TO_DATE 加 CASE WHEN,不如先标准化再转。
- 用正则提取数字:
REGEXP_REPLACE(col, '[^0-9]', '')得到纯数字串 - 判断长度:14 位当
YYYYMMDDHHIISS,8 位当YYYYMMDD,再用STR_TO_DATE - 更稳的是在应用层处理——MySQL 不是文本处理器,复杂字符串清洗容易拖慢查询,也难调试
真正麻烦的从来不是函数怎么写,而是你不知道字符串里藏着什么字符、哪条记录悄悄触发了隐式类型转换、或者某个凌晨三点上线的脚本悄悄改了 lc_time_names。留一列原始字符串,加一个 IS NULL 检查,比事后翻 error log 强得多。











