上周有个做零售运营的朋友发来一张订单表,让我帮他看看周报里的“第几周”为什么和公司日历对不上。他用的公式是=WEEKNUM(A2),结果3月1日显示第10周,3月2日直接跳到第11周,而他们公司所有排班、考核、周会都是以周一为起点的。更麻烦的是月度排班表,上面写的“3月第3周”和表里算出来的数字完全是两套体系,财务那边按周汇总的数据也对不上号。问题不在他不会写函数,而在于WEEKNUM里的“周”和他脑子里的“周”压根不是同一个定义。
这篇内容就把WEEKNUM从参数到边界彻底拆一遍,重点放在两件事上:一是年度周数怎么算才和公司口径一致,二是标题里提到的月度周数——WEEKNUM本身根本做不了,必须靠组合公式拼出来。适合经常做周报、排班、考勤、项目周期统计的人看,Excel基础一般也能跟着抄,因为每个公式我都会说清楚它为什么这么写,以及哪里最容易翻车。
1. WEEKNUM到底在数什么:周起点、参数与版本差异
1.1 一周从哪天开始,结果差出整整一周
WEEKNUM的语法只有两个参数:WEEKNUM(serial_number, [return_type])。第一个是日期,第二个是“周的起点规则”。很多人写公式时直接把第二个参数省掉,于是用上了默认值1,也就是一周从周日开始算。这一省,就把整个报表的周序打乱了。
举个具体的例子。2025年3月1日是周六,3月2日是周日,3月3日是周一。如果按周日为起点(return_type=1),3月2日就开启了新的一周,所以3月1日和3月2日不在同一周;如果按周一为起点(return_type=2),3月2日仍然属于上一周,3月3日才是新一周的开始。同一张表,换个参数,整月的周序号可能整体错位。
这也是为什么我建议:只要你的使用场景是国内公司的排班、考勤、周报,第二个参数永远显式写2,不要依赖默认值。省两个字符带来的风险,远大于收益。
还有一层容易被忽略的事:WEEKNUM数的是“这一年的第几周”,不是“这个月的第几周”。它从头到尾只认年份,不认月份。这是理解后面所有月度周数公式的前提。
1.2 return_type参数逐个拆开看
把return_type的所有取值列清楚,用的时候就不用翻文档了。
| 参数值 | 一周起点 | 第1周的判定规则 | 常见用途 |
|---|---|---|---|
| 1 或省略 | 周日 | 包含1月1日的那一周 | 欧美习惯的报表 |
| 2 | 周一 | 包含1月1日的那一周 | 国内排班、考勤首选 |
| 11 | 周一 | 同上,与2等价 | 历史兼容写法 |
| 12 | 周二 | 包含1月1日的那一周 | 极少用 |
| 13 | 周三 | 同上 | 极少用 |
| 14 | 周四 | 同上 | 极少用 |
| 15 | 周五 | 同上 | 极少用 |
| 16 | 周六 | 同上 | 极少用 |
| 17 | 周日 | 同上,与1等价 | 历史兼容写法 |
| 21 | 周一 | ISO 8601规则 | 跨国协作、外贸单证 |
2和11、1和17在结果上完全一样,区别只是微软当年为了兼容不同区域留下的两套编号。日常用2和21就够了,其余基本是摆设。
真正需要留意的是最后一行。21不是“周一开始”这么简单,它用的是ISO 8601国际标准:一周从周一开始,并且包含当年第一个周四的那一周才算第1周。这意味着第1周至少有4天落在本年之内,年头年尾的归属判断和普通模式完全不同。
提示:return_type为21的写法在较新的Excel版本才被完整支持,如果用老版本打开文件发现结果是#NUM!,先检查版本,再考虑用ISOWEEKNUM替代。
1.3 ISO周数为什么不能用普通WEEKNUM凑
有些做外贸或者对接海外系统的朋友,会被要求按ISO周出报表。这时候如果拿return_type=2硬凑,年尾年初一定对不上。
差别在哪?普通模式(return_type=2)规定“包含1月1日的那一周就是第1周”,哪怕这一周只有1月1日一天在本年,它也算第1周。ISO模式要求第1周至少有4天在本年内,所以2021年1月1日(周五)在ISO体系里不属于2021年第1周,而属于2020年的第53周;直到1月4日(周一)才进入2021年第1周。
这个差异带来的直接后果是:ISO周数不能拿来做差值运算。比如你想用“本周ISO周数减去月初ISO周数”来算月度第几周,1月初会出现53减53等于0、甚至负数的情况。后面的第3章会专门讲怎么绕开这个坑。
想直接拿ISO周数,用=ISOWEEKNUM(A2)就行,它等价于=WEEKNUM(A2,21),写起来更直观。但要注意,ISOWEEKNUM同样只返回周序号,不返回ISO年份。跨年周里,2021年1月1日的ISO周数是53,可它属于2020年。做年度汇总时,光有周数不够,还得配一个ISO年份的公式:
=YEAR(A2-WEEKDAY(A2,2)+4)这个公式的逻辑是:找出本周周四所在的年份。ISO规则里,一周归属哪一年,看的就是这一周的周四落在哪年。WEEKDAY(A2,2)返回1到7(周一为1),A2减去它再加4,正好定位到本周周四。我用2021年1月1日验算过:WEEKDAY返回5,1月1日减5天加4天等于2020年12月31日,年份取出来是2020,正确。
2. 年度周数:常规写法、跨年边界与显示格式
2.1 最稳的年度周序号写法
如果你只需要一个能排序、能分组、能和公司日历对上的年度周序号,公式就一句话:
=WEEKNUM(A2,2)把A2换成你的日期单元格,往下拖就行。周一开始,包含1月1日的周为第1周,这是国内绝大多数公司的默认口径。写完建议随手抽查三个点:1月1日、某个月的1日、12月31日。这三个位置最容易暴露口径问题。
如果需要周序号补零成两位(比如“第05周”而不是“第5周”),用TEXT包一层:
=TEXT(WEEKNUM(A2,2),"00")需要带“第X周”的字样,直接拼接:
="第"&WEEKNUM(A2,2)&"周"这里有个细节:拼接之后结果是文本,不能再拿去做数值比较或排序。如果后面还要用这个周序号做SUMIFS的条件,辅助列里保留纯数字,展示层再单独拼文本,别在一个单元格里既当数据又当门面。
2.2 12月末那几天可能返回1,这不是bug
年度周数最反直觉的地方在年尾。按“包含1月1日的周为第1周”这个规则,如果12月30日、31日和次年1月1日落在同一周里,那么这三天在WEEKNUM眼里已经属于下一年的第1周了。
我拿2021年12月31日试过,那天是周五,和2022年1月1日(周六)同属一周,WEEKNUM(DATE(2021,12,31),2)返回的是1,不是53。第一次看到这个结果的人基本都会以为公式写错了。
这个行为本身没错,它只是把“周”当成一个跨年的连续单位来看。但落到报表上会出问题:按周序号排序时,12月31日会跑到最前面;按周序号做年度筛选时,这一天会被算进下一年。
处理办法有三个,看你的业务需要选:
- 接受它:如果周报本来就是跨年连续的,12月31日归入次年第1周是合理的,不用改。
- 加年份限定:汇总时用“年份+周序号”做联合条件,而不是只看周序号。
- 改用日历年切分:年度统计按自然月切,周序号只做展示,不参与汇总口径。
我在实际项目里更倾向第二种,辅助列直接拼一个“2025-W11”这样的键值,排序、筛选、透视都不会串年。
2.3 用TEXT和ISOWEEKNUM拼出周标签
周报标题里通常要写“2025年第11周(3月10日-3月16日)”这种格式。手写太累,用公式生成:
="20"&TEXT(A2,"yy")&"年第"&WEEKNUM(A2,2)&"周("&TEXT(A2-WEEKDAY(A2,2)+1,"m月d日")&"-"&TEXT(A2-WEEKDAY(A2,2)+7,"m月d日")&")"拆开看就三块:年份用TEXT取后两位再补“20”,周序号用WEEKNUM,日期区间用“当天减去星期偏移”定位到本周周一,再加7得到周日。WEEKDAY(A2,2)在周一返回1,所以A2减1再加1还是A2本身,周一那天算出来的区间起点就是它自己,不会错位。
如果公司用的是ISO周,把WEEKNUM换成ISOWEEKNUM,年份部分换成前面那个ISO年份公式,其余不动。
这里提醒一句:TEXT函数里的“ww”格式也能返回周数,比如=TEXT(A2,"yyyy-ww"),但它依赖操作系统的区域设置,同一份文件在不同电脑上可能算出不同结果。跨区域协作的文件,千万别用“ww”,老老实实写WEEKNUM。
3. 月度周数:没有现成函数,只能靠组合公式
3.1 先定口径:从1号算起,还是从第一个完整周算起
这是整篇内容最需要停下来想清楚的地方。Excel没有“本月第几周”的函数,网上流传的公式有一大堆,结果却经常不一致,根本原因是大家对“月度第几周”的定义不同。常见的有三种口径:
| 口径 | 定义 | 核心公式 | 适用场景 |
|---|---|---|---|
| A 月初所在周为第1周 | 1号落在哪一周,哪一周就是第1周 | INT((日期-月初+WEEKDAY(月初,2)-1)/7)+1 | 排班表、考勤表 |
| B 首个完整周为第1周 | 月初不满一周的那几天记0或单列 | 在A的基础上判断月初偏移 | 项目周期、工时统计 |
| C 固定日段 | 1-7日第1周,8-14日第2周 | INT((DAY(日期)-1)/7)+1 | 账单周期、简单报表 |
口径C最简单,但它完全不管星期几。3月7日是周五、3月8日是周六,在C口径下直接跨周,排班表就废了。所以只要涉及“周几”的业务,必须用A或B。
口径A和B的差别在于月初那几天怎么处理。假设某月1日是周六,按A口径,1日、2日算第1周,只有两天;按B口径,这两天不算完整周,第1周从3日周一开始。做考勤时用A更符合直觉,做人均工时统计时用B更严谨,因为不完整周会拉低平均值。
3.2 差值法:两个WEEKNUM相减再加一
思路很直白:用当天的年度周数,减去本月1日的年度周数,再加1。
=WEEKNUM(A2,2)-WEEKNUM(DATE(YEAR(A2),MONTH(A2),1),2)+1DATE(YEAR(A2),MONTH(A2),1) 就是在构造本月1日。之所以不用EOMONTH(A2,-1)+1,是因为DATE写法更短、更好读,而且不依赖EOMONTH函数(老版本Excel没有)。
验算一下2025年3月15日:3月15日的年度周数是11,3月1日的年度周数是9,11减9加1等于3。翻日历确认,3月1日到2日是第1周,3日到9日是第2周,10日到16日是第3周,15日确实在第3周,结果正确。
这个公式的优点是短、好记、不容易写错。缺点有两个:
- 依赖return_type的一致性:两个WEEKNUM必须用同一个参数,一个写2一个省略,结果就飘了。
- 不能用于ISO周:前面说过,1月初的ISO周数可能是53,减出来是负数。如果你的场景必须用ISO周,直接跳到3.3的算术法。
还有一个小陷阱:如果A2本身是空单元格,YEAR和MONTH会返回1900,公式算出个莫名其妙的数字而不是报错。做模板时记得在外面套一层IF(A2="","",...)。
3.3 纯算术法:INT加WEEKDAY,绕开跨年问题
不想依赖年度周数、也不想被ISO规则干扰,就用日期差直接算:
=INT((A2-DATE(YEAR(A2),MONTH(A2),1)+WEEKDAY(DATE(YEAR(A2),MONTH(A2),1),2)-1)/7)+1这段公式看着长,拆开只有四步:
DATE(YEAR(A2),MONTH(A2),1)拿到本月1日;A2-月初算出当天是本月第几天减一,也就是从1号起过了几天;WEEKDAY(月初,2)-1算出1号距离本周一还有几天,周一返回1,减1等于0,说明1号正好是周一,不需要偏移;- 两者相加除以7取整,再加1,就是月内周次。
用2025年3月再验一遍。3月1日是周六,WEEKDAY返回6,偏移量是5。3月15日减3月1日等于14天,14加5等于19,19除以7取整是2,加1等于3。和差值法结果一致。
这个写法的好处是只用日期本身做运算,不碰年度周数,所以无论用哪种周起点规则都不会串。想改成周日开始,把WEEKDAY的第二个参数从2改成1即可,其余不动。
如果业务采用口径B(首个完整周才算第1周),在这个基础上加个判断:
=LET(d,A2,f,DATE(YEAR(d),MONTH(d),1),fm,f+MOD(7-WEEKDAY(f,2)+1,7),IF(d<fm,0,INT((d-fm)/7)+1))fm算的是本月第一个周一的日期,MOD(7-WEEKDAY(f,2)+1,7)处理了1号本身就是周一的情况(此时结果为0而不是7)。月初那几天小于第一个周一,返回0,表示不完整周。这个公式适合放在统计类报表里,排班表不建议用,因为出现0会让人困惑。
3.4 月末、闰年与跨月边界的验证方法
月度周数的公式写完,千万别只看中间某一天就收工。我吃过亏:某次给一个做连锁门店的朋友做排班模板,中间日期全部正确,结果2月28日算出第5周,3月1日算出第1周,跨月那天的公式引用错了月份,整张表错位。
我现在固定用四个测试点验证任何月度周数公式:
| 测试点 | 验证目的 |
|---|---|
| 本月1日 | 结果必须是1(口径A)或0(口径B) |
| 本月最后一天 | 检查是否超出该月实际周数 |
| 2月28日或29日 | 闰年边界,检查DATE构造是否正确 |
| 跨年月的1月1日 | 检查年份引用有没有写死 |
再补一个“本月共几周”的公式,用来交叉验证:
=WEEKNUM(EOMONTH(A2,0),2)-WEEKNUM(DATE(YEAR(A2),MONTH(A2),1),2)+1EOMONTH(A2,0)取本月最后一天。2025年3月算出来是6周,和3月31日的月内周次6对得上。如果两个数不一致,说明公式里有硬编码或者引用错位。
注意:闰年2月有29天,按口径A可能算出6周,按口径C固定是5周(29天除以7向上取整是5)。如果报表里两个口径混用,2月的数字一定打架,提前统一好。
4. 排班、周报与考勤里的落地公式
4.1 周报标题:第几周加日期区间,一次生成
周报最烦的不是写内容,是每次都要手动改标题里的日期。用公式生成之后,只要日期列更新,标题自动跟着变。
年度周报标签:
="第"&WEEKNUM(A2,2)&"周("&TEXT(A2-WEEKDAY(A2,2)+1,"yyyy年m月d日")&"至"&TEXT(A2-WEEKDAY(A2,2)+7,"m月d日")&")"月度周报标签,把年度周数换成月内周次:
="第"&INT((A2-DATE(YEAR(A2),MONTH(A2),1)+WEEKDAY(DATE(YEAR(A2),MONTH(A2),1),2)-1)/7)+1&"周("&TEXT(A2-WEEKDAY(A2,2)+1,"m月d日")&"至"&TEXT(A2-WEEKDAY(A2,2)+7,"m月d日")&")"生成的效果是“第3周(3月10日至3月16日)”。如果周报的日期区间要求是周一到周日,这个公式直接可用;如果公司按周一到周五统计,把末尾的+7改成+5,标签会跟着变。
有个细节值得说:A2-WEEKDAY(A2,2)+1算出来的周一,在A2本身是周一时等于A2,不会多减一天。这个“加1”不能省,省了会整体前移一天,很多人第一次写都会漏。
4.2 按周汇总:辅助列比嵌套SUMIFS可靠
想在数据透视表里按周汇总,最稳的做法不是写复杂公式,而是加一列辅助列。
第一步,在原始数据右侧插入一列“周序号”,填:
=WEEKNUM(A2,2)第二步,再插一列“周标签”,填4.1里的年度周报标签公式。
第三步,选中整个数据区域插入透视表,把“周标签”拖到行,“金额”拖到值,汇总自动按周聚合。
为什么不直接在透视表里对日期做“按周组合”?Excel的日期组合只有年、季、月、日,没有周。想按周分组,辅助列是唯一可靠的路。
如果不想加辅助列,也可以用SUMIFS硬写:
=SUMIFS(金额列,日期列,">="&F2-WEEKDAY(F2,2)+1,日期列,"<="&F2-WEEKDAY(F2,2)+7)F2放一个代表本周的任意日期。这个写法的好处是不改原表结构,坏处是每次算都要扫全表,数据量上万行之后会明显变慢。我的经验是:数据量超过5000行,果断加辅助列;几百行的小表,SUMIFS够用。
还有一点容易踩:SUMIFS的日期条件是文本拼接,如果日期列里存的是文本格式的日期(左对齐那种),条件永远匹配不上,结果全是0。先确认日期列是真正的日期序列号,再写公式。
4.3 考勤定位:月内第几周乘星期几
考勤表通常是一个月一张,横轴是1到31日,纵轴是员工。如果要做“按周统计迟到次数”,就需要把每一天映射到“第几周第几天”。
用两个辅助行就够了。第一行放月内周次:
=INT((B$1-DATE(YEAR(B$1),MONTH(B$1),1)+WEEKDAY(DATE(YEAR(B$1),MONTH(B$1),1),2)-1)/7)+1B$1是日期行。第二行放星期几:
=WEEKDAY(B$1,2)有了这两个维度,迟到次数就可以用SUMIFS按“周次=3且星期几<=5”这样的条件筛选。比手工数格子快得多,也不容易漏。
需要提醒的是,考勤表里经常有空白格(周末没人上班),WEEKDAY对空白返回7(周日),会被误判成周末。做条件统计时加一个B$1<>""的判断,把空白排除掉。
另外,如果公司是大小周或者排班制,星期几本身没有意义,这时候周次维度才是主要的,星期维度只做展示。公式不用改,统计口径换一下就行。
5. 踩过的坑:日期序列、版本与报错
5.1 文本日期和#VALUE!的连锁反应
WEEKNUM的第一个参数必须是有效的日期序列号。如果单元格里是“2025.3.15”这种点号分隔的文本,或者从系统导出的“20250315”八位数字,WEEKNUM直接返回#VALUE!。
判断方法很简单:选中日期列,看对齐方式。真日期默认右对齐,文本默认左对齐。再看编辑栏,真日期在编辑栏显示的是格式化后的日期,文本显示的是原样字符。
转换办法按情况选:
- 点号或斜杠分隔的文本:
=DATEVALUE(SUBSTITUTE(A2,".","/")) - 八位数字:
=DATE(LEFT(A2,4),MID(A2,5,2),RIGHT(A2,2)) - 带时间的文本:先用
=INT(A2)去掉时间部分,再转日期
我一般会在原始数据旁边加一列转换后的真日期,后续所有公式都引用这一列,原始列保留备查。这样既不影响溯源,也不会因为反复转换拖慢计算。
还有一种隐蔽情况:单元格看着是日期,实际上前面有个看不见的单引号。这种用ISTEXT判断会返回TRUE,但用DATEVALUE能转。遇到死活转不过来的,先删掉单元格内容,重新输入一遍,往往就好了。
5.2 1904日期系统与区域设置差异
Excel有两套日期系统:1900和1904。差别是整个序列号偏移1462天。平时感知不到,但当一个文件在1904系统的环境里创建、又被拿到1900系统的环境里打开时,所有日期会整体差4年零1天,周数自然全错。
检查方法:文件-选项-高级,往下拉到“计算此工作簿时”,看“使用1904日期系统”有没有勾选。公司内部流转的模板,统一不勾,别为了兼容某个特殊环境去改这个开关。
区域设置的影响主要在两个地方:一是TEXT函数的“ww”格式,前面说过,不同区域算出来的周数可能不同;二是日期分隔符,有些环境用“/”,有些用“-”,DATEVALUE解析时会受影响。跨区域协作的文件,公式里尽量用DATE(year,month,day)构造日期,别用文本解析,能避开一大半问题。
5.3 一个边界测试清单
公式上线前,我习惯跑一遍下面这张清单,五分钟能省掉后面几小时的返工。
| 检查项 | 具体做法 | 期望结果 |
|---|---|---|
| 1月1日 | WEEKNUM(日期,2) | 返回1 |
| 12月31日 | WEEKNUM(日期,2) | 返回52、53或1,需与业务确认 |
| 月末最后一天 | 月内周次公式 | 不超过该月实际周数 |
| 闰年2月29日 | 月内周次公式 | 正常返回,不报错 |
| 空白单元格 | 公式外套IF | 返回空,不返回1900年的数字 |
| 文本日期 | WEEKNUM直接引用 | 应为#VALUE!,确认源数据需清洗 |
| 跨年周 | 1月1日与12月31日 | 年度周序号不出现跳变异常 |
清单里第2项和第7项最容易被跳过。我的建议是:拿最近三年的12月31日各测一遍,看看返回什么,心里有数之后,再决定要不要在汇总口径上做特殊处理。
6. 把公式沉淀成模板:辅助列、LET与命名公式
6.1 为什么建议保留辅助列
写公式的人容易有个执念:一个单元格解决所有问题,最好不改变原表结构。但在周数这件事上,辅助列带来的收益远大于“表好看”。
理由很实在。第一,辅助列的结果可以被透视表、SUMIFS、条件格式、图表同时引用,一个数算一次,处处复用。第二,公式出错时,辅助列能让你一眼看出是哪一步错了,嵌套公式只能看到最终结果。第三,同事接手时,看到“周序号”三个字就知道这列干什么用的,嵌套公式得花十分钟才能读懂。
我的习惯是:原始数据区保持不动,右侧留三列——真日期、年度周序号、月内周次,统一加灰色表头,标注“辅助列,勿删”。需要展示周标签时再加第四列,用拼接公式生成。模板发给别人时,这几列加保护,只留日期列可编辑。
6.2 LET和LAMBDA的封装思路
如果用的是较新版本的Excel,可以用LET把重复计算的部分收拢,公式会清爽很多。比如月内周次:
=LET(d,A2,f,DATE(YEAR(d),MONTH(d),1),INT((d-f+WEEKDAY(f,2)-1)/7)+1)d和f各算一次,后面直接引用,不用把DATE(YEAR(...)...)写两遍。公式长度减半,出错概率也低。
再进一步,用LAMBDA把整个逻辑封装成一个自定义函数,起名叫“月内周次”:
=LAMBDA(d,LET(f,DATE(YEAR(d),MONTH(d),1),INT((d-f+WEEKDAY(f,2)-1)/7)+1))在名称管理器里新建一个名称,引用位置填上面这段,之后就能像内置函数一样用=月内周次(A2)。整个团队共用这套定义之后,口径不一致的问题基本就消失了。
不过LAMBDA有个现实限制:用它的文件在旧版本Excel里打不开,会显示#NAME?。如果收件人版本不确定,老老实实用普通公式,或者把结果提前算成值再发。我一般会在模板里同时留一列普通公式版本,标注“兼容用”,谁需要谁复制。
还有一个折中方案:把常用公式存进“自动更正”或者快速访问工具栏,输入一个短代码就自动展开成完整公式。这个方法不依赖版本,适合公式固定、使用频繁的场景。
写到这里,关于WEEKNUM该说的基本都覆盖了。最后分享一个我自己一直在用的习惯:任何涉及周数的模板,交付前一定用“年度周序号+月内周次+星期几”三个维度交叉验证一遍,随手抽最近三个月的1号和月末,三个数能对上,这份表才算能交出去。踩过的坑告诉我,省掉这一步,后面返工的时间往往是验证的十倍。