week() 默认模式0导致周数与日历不符,因以周日为起点且要求1月1日所在周为第1周;推荐用weekofyear()(等价于week(date,3))严格遵循iso 8601标准。

WEEK() 函数默认返回的周数为什么经常和日历对不上
MySQL 的 WEEK() 默认使用模式 0(即 WEEK(date) 等价于 WEEK(date, 0)),它把周日作为一周的开始,且要求一周包含 1 月 1 日才视为第 1 周——这导致 1 月 1 日是周一或周二时,前一年末尾几天可能被算作“第 53 周”,而当年 1 月头几天反而变成“第 0 周”。用户看到 WEEK('2024-01-01') 返回 0 就会困惑。
- 真正符合 ISO 8601 标准(周一为每周起点、第 1 周必须含当年至少 4 天)的模式是
WEEK(date, 1)或更稳妥的WEEKOFYEAR() -
WEEKOFYEAR(date)固定等价于WEEK(date, 3):周一为起点,且第 1 周必须包含 4 天以上在当年——这是大多数业务场景需要的“自然年周序” - 如果用
WEEK(date, 1),注意它不强制第 1 周含 4 天,比如 2025-01-01(周三)会返回1,但该周实际只有 3 天属于 2025 年
获取当前日期所在年份的第几周(推荐写法)
直接用 WEEKOFYEAR(NOW()) 最省心,它隐式绑定当前年,并严格按 ISO 周规则计算。如果要查指定日期(比如字段 order_time),也只需 WEEKOFYEAR(order_time)。
- 不要写
WEEK(NOW(), 1)—— 它可能把 2025-01-01 算作第 1 周,但该周跨年,前 3 天属 2024 年,易引发统计口径偏差 - 避免用
WEEK(NOW(), 5)(周一为始 + 第 1 周需含 4 天以上在当年)—— 虽逻辑正确,但 MySQL 手册明确标注该模式“与WEEKOFYEAR()行为一致”,多此一举 - 若必须用
WEEK(),唯一安全组合是WEEK(date, 3),它和WEEKOFYEAR()结果完全相同
WHERE 条件中按“当年第 N 周”过滤数据的常见陷阱
错误写法:WHERE WEEK(create_time) = WEEK(NOW()) —— 这会跨年匹配(比如去年 12 月最后一周和今年 1 月第一周都可能是 1),结果错乱。
- 正确做法是同时限定年份:
WHERE YEAR(create_time) = YEAR(NOW()) AND WEEKOFYEAR(create_time) = WEEKOFYEAR(NOW()) - 如果表数据量大,
YEAR()和WEEKOFYEAR()都无法走索引;建议额外增加生成列(MySQL 5.7+):ALTER TABLE orders ADD COLUMN yw INT AS (YEAR(create_time) * 100 + WEEKOFYEAR(create_time)) STORED;
然后对yw建索引 - 注意
WEEKOFYEAR()对NULL返回NULL,WHERE 中需加IS NOT NULL判断,否则可能漏数据
不同地区习惯下如何调整周起始日
ISO 标准(周一为始)是通用选择,但部分业务要求周日为始(如美国报表)。此时不能只改 WEEK() 模式,还必须同步处理“年份归属”问题。
- 若坚持周日为始且要“第 1 周必须含 1 月 1 日”,用
WEEK(date, 0),但得手动修正第 0 周:IF(WEEK(date, 0) = 0, WEEK(date, 0) + 53, WEEK(date, 0))—— 不推荐,逻辑脆弱 - 更可靠的是用
DAYOFWEEK()手动对齐:先算出本周一日期(DATE_SUB(date, INTERVAL WEEKDAY(date) DAY)),再用YEAR()和WEEKOFYEAR()判断归属年份,复杂度陡增 - 结论:除非合同明确要求周日起点,否则统一用
WEEKOFYEAR(),避免自找麻烦
WEEKOFYEAR() 看似简单,但和 YEAR() 组合使用时,年份切换那几天(12 月 28–31 日、1 月 1–3 日)最容易出错;别信直觉,用真实日期测一遍再上线。











