mysql中weekday()返回0~6(周一为0),本周一用date_sub(now(), interval weekday(now()) day),周日为周一加6天;postgresql中date_trunc('week')以周日为起点,iso周需用extract(isodow)手动对齐;sql server的datepart(dw)依赖datefirst设置,须显式声明。

MySQL里用WEEKDAY()和DATE_SUB()算本周一和周日
MySQL默认周一是每周起点(WEEKDAY()返回0~6),但要注意WEEKDAY(NOW())返回的是“今天是星期几”,不是偏移量。要得到本周一,得用DATE_SUB(NOW(), INTERVAL WEEKDAY(NOW()) DAY);周日则是DATE_ADD(周一日期, INTERVAL 6 DAY)。
常见错误是直接用WEEK()函数——它受mode参数影响大,且返回的是“第几周”,不是日期偏移,容易算错具体日期。
- 如果业务要求周日为每周开始(比如某些报表系统),改用
DATE_SUB(NOW(), INTERVAL WEEKDAY(DATE_ADD(NOW(), INTERVAL 1 DAY)) DAY)先挪一天再算周一,再减1天得周日 -
WEEKDAY()在周末值为5(周六)、6(周日),所以WEEKDAY('2024-06-02')(周日)返回6,别误当成0 - 结果类型是
DATETIME,若只要日期部分,套一层DATE()
PostgreSQL中date_trunc('week', ...)默认从周日开始?
PostgreSQL的date_trunc('week', NOW())实际以**周日**为每周起点,不是ISO标准。这意味着date_trunc('week', '2024-06-02'::date)返回2024-06-02(周日),而date_trunc('week', '2024-06-03'::date)返回2024-06-02(同个周日)。
想严格按ISO周(周一为始、周一属于含该周四的那周),必须用date_trunc('isoyear', ...)配合EXTRACT,但更简单的是手动对齐:
- 本周一:
current_date - EXTRACT(ISODOW FROM current_date)::int + 1(ISODOW周一是1,周日是7) - 本周日:
current_date - EXTRACT(ISODOW FROM current_date)::int + 7 - 注意
EXTRACT(ISODOW FROM ...)不能用于TIMESTAMP WITH TIME ZONE直接计算,需先转DATE或用AT TIME ZONE对齐时区
SQL Server用DATEPART(dw, ...)前先设DATEFIRST
SQL Server的DATEPART(dw, GETDATE())返回值取决于SET DATEFIRST设置:默认是7(周日=1),但很多中文环境会设成1(周一=1)。不显式声明DATEFIRST,同一段SQL在不同服务器上结果可能相反。
安全做法是开头加SET DATEFIRST 1,再用DATEADD推算:
- 本周一:
DATEADD(day, 2 - DATEPART(dw, GETDATE()), GETDATE())(当DATEFIRST=1时,周一=1,所以2-1=1天前) - 本周日:
DATEADD(day, 8 - DATEPART(dw, GETDATE()), GETDATE()) - 或者更稳:先用
DATEADD(week, DATEDIFF(week, 0, GETDATE()), 0)得本周日(基于默认DATEFIRST=7),再根据需要调整
跨数据库写法难统一,别硬套一个SQL跑所有库
没有能直接在MySQL、PostgreSQL、SQL Server里都返回“本周一00:00:00”的标准函数。各库对“周起始日”“周编号规则”“时区处理”的定义差异太大,强行抽象成通用视图或函数反而更容易出错。
真正落地时建议:
- 应用层计算好日期再传入SQL(比如Python用
datetime.today() - timedelta(days=datetime.today().weekday())) - 数据库内统一用UTC时间存储,所有“本周”逻辑在应用侧做时区转换
- 如果必须SQL内算,把逻辑封装进带注释的
VIEW或FUNCTION,并在文档里写死“本函数按ISO周、周一0点起始”
最常被忽略的是时区——NOW()和CURRENT_DATE在不同时区实例下可能跨天,而date_trunc或WEEKDAY不会自动帮你转换。











