ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Excel WEEKNUM周数计算:年度周序号与月度周次公式详解

Excel WEEKNUM周数计算:年度周序号与月度周次公式详解

上周有个做零售运营的朋友发来一张订单表,让我帮他看看周报里的“第几周”为什么和公司日历对不上。他用的公式是=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)+1

DATE(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

这段公式看着长,拆开只有四步:

  1. DATE(YEAR(A2),MONTH(A2),1)拿到本月1日;
  2. A2-月初算出当天是本月第几天减一,也就是从1号起过了几天;
  3. WEEKDAY(月初,2)-1算出1号距离本周一还有几天,周一返回1,减1等于0,说明1号正好是周一,不需要偏移;
  4. 两者相加除以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)+1

EOMONTH(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)+1

B$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号和月末,三个数能对上,这份表才算能交出去。踩过的坑告诉我,省掉这一步,后面返工的时间往往是验证的十倍。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进