MySQL中STR_TO_DATE()返回上月日期,是否为Bug?
MySQL STR_TO_DATE()转换'%M-%YY'格式返回上月日期的问题
问题场景
处理表中类似'October-21'的字符串格式日期时,在MySQL 5.9(SQLFiddle环境)中使用STR_TO_DATE('%M-%YY')转换出现异常:
- 转换结果返回上月最后一天
- 直接将转换结果传入
DATE_ADD()会返回null,仅转为数值日期后调用DATE_ADD()才正常
测试SQL
SELECT monthname(STR_TO_DATE('October','%M')), STR_TO_DATE('October-21','%M-%YY'), STR_TO_DATE('November-21','%M-%YY'), STR_TO_DATE('October-21','%M-%YY') + 1, STR_TO_DATE('November-21','%M-%YY') + 1, STR_TO_DATE('November-21','%M-%YY') - 1, DATE_ADD(STR_TO_DATE('October-21','%M-%YY'), INTERVAL 1 MONTH), DATE_ADD(STR_TO_DATE('October-21','%M-%YY')+1, INTERVAL 1 MONTH), DATE_ADD(STR_TO_DATE('November-21','%M-%YY')+1, INTERVAL -1 MONTH), STR_TO_DATE('Oct','%b'), STR_TO_DATE('Sep','%b');
测试输出
monthname(STR_TO_DATE('October','%M')) October STR_TO_DATE('October-21','%M-%YY') 2021-09-30 STR_TO_DATE('November-21','%M-%YY') 2021-10-31 STR_TO_DATE('October-21','%M-%YY') + 1 20211001 STR_TO_DATE('November-21','%M-%YY') + 1 20211101 STR_TO_DATE('November-21','%M-%YY') - 1 20211099 STR_TO_DATE('Oct','%b') 0001-09-30 STR_TO_DATE('Sep','%b') 0001-08-31 DATE_ADD(STR_TO_DATE('October-21','%M-%YY'), INTERVAL 1 MONTH) null DATE_ADD(STR_TO_DATE('October-21','%M-%YY')+1, INTERVAL 1 MONTH) 2021-11-01 DATE_ADD(STR_TO_DATE('November-21','%M-%YY')+1, INTERVAL -1 MONTH) 2021-10-01
原因分析
- 日期回退逻辑:当
STR_TO_DATE()使用的格式串仅指定月份(%M/%b)和年份(%YY)、未包含日期部分时,MySQL会默认将日期设为00。但MySQL不存在日为00的合法日期,因此会自动回退到上月的最后一天。- 例如
'October-21'会被解析为2021-10-00,因无效自动调整为2021-09-30;'November-21'解析为2021-11-00,调整为2021-10-31。
- 例如
- DATE_ADD返回null的原因:虽然调整后的日期显示为合法的上月最后一天,但该日期是从无效的"零日"转换而来,内部被标记为无效日期,导致
DATE_ADD()等日期函数无法正常处理,返回null。而将转换结果转为数值(如20210930)后加1,得到的20211001是合法的2021-10-01,因此能被DATE_ADD()正常处理。
版本兼容性
该问题并非MySQL 5.9独有,在MySQL 5.x系列的多个版本(如5.6、5.7)中都存在,这是STR_TO_DATE()处理无日期部分格式时的默认行为。MySQL 8.0中核心逻辑未变,仍会出现日期回退的情况,但部分日期函数对无效日期的处理略有优化。
解决方案
若需要得到目标月份的第一天(而非上月最后一天),可以通过以下方式处理:
- 拼接日期部分后转换:
STR_TO_DATE(CONCAT('October-21', '-01'), '%M-%YY-%d') - 转换后格式化获取当月第一天:
DATE_FORMAT(STR_TO_DATE('October-21', '%M-%YY'), '%Y-%m-01') - 对转换结果加1天(跳过无效的零日逻辑):
ADDDATE(STR_TO_DATE('October-21', '%M-%YY'), 1)
内容的提问来源于stack exchange,提问作者bonCodigo
相关产品推荐
相关产品推荐

