You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL str_to_date()函数在年初为周一的年份下失效问题

解决MySQL 5.7中str_to_date()解析「年份周数+星期几」的异常问题

我之前碰到过一模一样的MySQL周数解析坑,尤其是在新年第一天刚好是周一的年份(比如2018、2007),用str_to_date()处理%X%V %W格式时会出怪事——除了周日,其余星期几都会被错误解析到下一周的日期。

问题复现

异常场景(2018年,1月1日为周一)

执行以下查询:

SELECT 
  str_to_date('201801 Monday', '%X%V %W') AS 'Monday',
  str_to_date('201801 Tuesday', '%X%V %W') AS 'Tuesday',
  str_to_date('201801 Wednesday', '%X%V %W') AS 'Wednesday',
  str_to_date('201801 Thursday', '%X%V %W') AS 'Thursday',
  str_to_date('201801 Friday', '%X%V %W') AS 'Friday',
  str_to_date('201801 Saturday', '%X%V %W') AS 'Saturday',
  str_to_date('201801 Sunday', '%X%V %W') AS 'Sunday';

异常输出:

Monday       | Tuesday     | Wednesday   | Thursday    | Friday      | Saturday    | Sunday
'2018-01-08'| '2018-01-09'| '2018-01-10'| '2018-01-11'| '2018-01-12'| '2018-01-13'| '2018-01-07'

本该返回2018年第一周(1月1日-7日)的日期,结果只有周日是对的,其余日期全跳到了第二周。

正常场景(2017年,1月1日为周日)

执行相同逻辑的查询:

SELECT 
  str_to_date('201701 Monday', '%X%V %W') AS 'Monday',
  str_to_date('201701 Tuesday', '%X%V %W') AS 'Tuesday',
  str_to_date('201701 Wednesday', '%X%V %W') AS 'Wednesday',
  str_to_date('201701 Thursday', '%X%V %W') AS 'Thursday',
  str_to_date('201701 Friday', '%X%V %W') AS 'Friday',
  str_to_date('201701 Saturday', '%X%V %W') AS 'Saturday',
  str_to_date('201701 Sunday', '%X%V %W') AS 'Sunday';

正常输出:

Monday       | Tuesday     | Wednesday   | Thursday    | Friday      | Saturday    | Sunday
'2017-01-02'| '2017-01-03'| '2017-01-04'| '2017-01-05'| '2017-01-06'| '2017-01-07'| '2017-01-01'

问题原因

这个bug和MySQL对%X(周所属年份)与%V(周数,周一为一周第一天)的组合解析逻辑有关:当新年第一天恰好是周一时,MySQL错误地把该周的周一至周六归到了下一周(周数02),只把周日留在了周数01里。而且调整default_week_format系统变量也没法解决这个特定场景的问题。

解决方案

我们可以绕开str_to_date()的这个bug,利用周日解析始终正确的特性,反向推导其他星期几的日期:

通用单日期解析方法

SET @input_str = '201801 Monday'; -- 替换成你要解析的字符串

-- 拆分输入里的年份、周数、星期几
SET @year = LEFT(@input_str, 4);
SET @week = SUBSTRING(@input_str, 5, 2);
SET @weekday = TRIM(RIGHT(@input_str, LENGTH(@input_str) - 6));

-- 先拿到该周周日的正确日期
SET @sunday_date = STR_TO_DATE(CONCAT(@year, @week, ' Sunday'), '%X%V %W');

-- 根据星期几反向算出目标日期
SELECT
  CASE @weekday
    WHEN 'Monday' THEN DATE_SUB(@sunday_date, INTERVAL 6 DAY)
    WHEN 'Tuesday' THEN DATE_SUB(@sunday_date, INTERVAL 5 DAY)
    WHEN 'Wednesday' THEN DATE_SUB(@sunday_date, INTERVAL 4 DAY)
    WHEN 'Thursday' THEN DATE_SUB(@sunday_date, INTERVAL 3 DAY)
    WHEN 'Friday' THEN DATE_SUB(@sunday_date, INTERVAL 2 DAY)
    WHEN 'Saturday' THEN DATE_SUB(@sunday_date, INTERVAL 1 DAY)
    WHEN 'Sunday' THEN @sunday_date
  END AS correct_date;

整周日期批量查询示例

如果要一次性获取整周的正确日期,可以用这个语句:

SET @year = '2018';
SET @week = '01';
SET @sunday_date = STR_TO_DATE(CONCAT(@year, @week, ' Sunday'), '%X%V %W');

SELECT
  DATE_SUB(@sunday_date, INTERVAL 6 DAY) AS 'Monday',
  DATE_SUB(@sunday_date, INTERVAL 5 DAY) AS 'Tuesday',
  DATE_SUB(@sunday_date, INTERVAL 4 DAY) AS 'Wednesday',
  DATE_SUB(@sunday_date, INTERVAL 3 DAY) AS 'Thursday',
  DATE_SUB(@sunday_date, INTERVAL 2 DAY) AS 'Friday',
  DATE_SUB(@sunday_date, INTERVAL 1 DAY) AS 'Saturday',
  @sunday_date AS 'Sunday';

执行后会返回正确的2018年第一周日期:2018-01-01至2018-01-07。

这个方法靠周日的正确解析结果反向偏移计算,不管新年第一天是不是周一,都能得到准确的日期。

内容的提问来源于stack exchange,提问作者David Casillas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:28:36