先看那一天同时出现在两份清单里
排工期最容易撞上这种日子:装修队说下周一照常进场,日历上那个周一却标着假期,另一张纸上又写着补班。我的办法是把起止日期丢进工具跑一遍,看它听谁的——这篇不讲假期清单哪来的,只讲它内部的判定顺序,因为换套日历、换个地区,顺序还是同一套。
判定顺序是四行代码,不是政策
模板里判单日性质的 wdKindOf() 写在 512–517 行,顺序硬得没商量:先查补班清单(514 行)返回 makeup,再查假日清单(515 行)返回 holiday,最后才看周末开关(516 行);只有 work 与 makeup 算上班。
把 2026-10-05 这个周一同时抄进两份清单,实测 kind 就是 makeup,单日区间给出 工作日 1 / 休息日 0 / 总数 1。整月对照更直白:同一批日期只写进假日清单时是 20 个工作日,抄一份进补班清单变 22 个,总数始终 31——改掉的只有上班与休息这一刀。计数区还会把冲突摊开:有效 3 条,其中 2 天同时写在补班清单里,按补班算。
周末从 0 天设到 7 天,工作日一路塌下去
周末能设成 0 到 7 天任意组合。区间固定 2026-10、清单不动,工作日数走出一条很规整的下坡路,两列相加永远是 31:
| 设成休息的天数 | 工作日 | 休息日 | 总数 |
|---|---|---|---|
| 0 天 | 24 | 7 | 31 |
| 1 天 | 22 | 9 | 31 |
| 2 天(默认) | 19 | 12 | 31 |
| 3 天 | 15 | 16 | 31 |
| 4 天 | 11 | 20 | 31 |
| 5 天 | 8 | 23 | 31 |
| 6 天 | 5 | 26 | 31 |
| 7 天 | 2 | 29 | 31 |
塌到底会怎样?设到 7 天全休息时 per = 7 - 7 = 0,整周批量与尾数判定一起归零(553–558 行),base=0、cut=0,只有 add 仍=2,于是整月只剩 2 个补班日算上班。再把两份清单清空,同一个月退化成 工作日 0 / 休息日 31 / 总数 31。
图 1|横轴是周末设几天,绿柱是剩下的工作日;最矮那格不是 0,因为还有补班日算上班。
不重复扣、不漏加:全年只做加法
拉到整年:2026 全年 365 天,先按周末规则得到 base=261,再扣 17 天、加回 4 天,剩 248 个工作日 / 117 个休息日。清单里明明有 29 条假日,怎么只扣 17?因为有 12 条本来就落在周末头上,周末的假日不再扣第二次;反过来 4 个补班日全都落在周末,所以 add=4。月度视图也对得上账:勾上 230 行那个复选框,12 个月的工作日逐月相加还是 248,与一次喂整年的结果相同。
端点算不算,四种组合差出两天
「还剩几个工作日」的争吵多半不是算错,而是首尾算没算没对齐。模板 170–171 行那两个复选框默认都 checked,即首尾都含。2026-10-14 到 2026-10-21 四种组合实测 6 / 5 / 4 个工作日,都不含时区间缩成 10-15~10-20。写成同一天时四种组合是 1 / 0 / 0 / 0,两头都不含直接判空并打印「区间里一天都没有」。
数够 N 个工作日那天是哪天
反过来问得更常:合同给了 500 个工作日,交付日落在哪天。实测 2026-10-01 往后数 500 个工作日落在 2028-09-04,跨 704 个自然日,其中 204 个是休息。往前数也走得动,2026-10-14 数 -1 就是 2026-10-13;N=0 原地返回起始日,哪怕那天是周六。想复现就用 工作日计算器 的推算模式,别拿系统当天当基准。上限 1000000,再多一位直接被拒;正好踩线时撞的是支持域边界,报 结果超出支持范围(1900-01-01 ~ 2999-12-31),还差 746094 个工作日。
区间没有长度上限,明细只给 200 条
这两件事得分开看。1900-01-01 到 2999-12-31 一共 401767 个自然日,一次喂进去照样算出 286,964 个工作日,全程整数运算。明细列表却是另一套闸门:每种只给 200 条、只扫描 4000 天,喂 2000-01-01~2019-12-31 这 7305 天的区间,页面原样写出「这段区间太长,只扫描了 4000 天,后面 3305 天没进列表」。也别默认它逐天判定:区间统计是整周批量 weeks * (7 - weekendSize) 加尾数逐个,401767 天里进入逐日判定的只有 2 天。这跟 每 28 天与每月同一天为什么会错开是同一类口径问题。
清单解析:报错但不丢整表
清单是 textarea,粘进来的东西往往不干净。解析口径是坏行报错但不中断:一份 10 行混合清单(9 行有内容、第 9 行是空行)实测给出 有效 3 条(重复已去重 2 条),错误面板逐行给原文——第 5 行是月份写成 13、第 7 行是 2026-02-30 这个日子不存在、第 8 行是乱写的一行;空行和 # 注释一样占号。重复行绝不会让某天被扣两次:3 行写同一个日子仍是 cut=1。清单规模是法定假日 29 条 + 调休补班 4 条、合并去重 33 天,年份只有 2026 一个;2027 起两份清单里一天都没有,cut=0、add=0,全年实测 261 个工作日 / 104 个休息日,纯走周末分支。
图 2|同一套 base=261,差别只来自 cut 与 add:清单内扣 17 加回 4,清单外是 0 和 0。
三处文案与代码打架
一是起止写反时,报错原文让用户「点下面按钮按反过来的顺序算」,可 wdSwap() 全文只有 1223 行那一处定义、0 处 onclick,那个按钮并不存在。二是日序原点:intro 说日期换算成从 1900-01-01 起的天数,实测 wdDaysFromCivil(1970,1,1)=0、(1900,1,1)=-25567,1900-01-01 只是支持域下界。三最影响明天继续用:weekend 字段存成 33 字符的 "falsefalsefalsefalsefalsetruetrue",回读条件却要求长度正好 7(1284 行),于是两份清单能回来、周末勾选刷新即丢。
FAQ
Q:同一天既写进假日清单又写进补班清单,会不会被扣一次再加一次?
不会两个都算。补班那关排在假日前面,第一个命中就返回:整月从 20 个工作日变 22 个,总数还是 31。抢一天时先定优先级,数距离某个日子还剩几天也一样。
Q:周末勾满 7 天,是不是整月一天都不上班?
不是。per = 7 - 7 = 0 只让批量与尾数归零,补班清单还在,2026-10 实测仍有 2 个工作日;把两份清单一起清空才得到 0 / 31 / 31。
Q:能不能让它自己认得接下来每一年的假期?
不能。内置那两份只是示例数据,年份只到 2026;清单外的日子得自己一行一条粘进去。