功能说明
该示例按 ISO 8601 规则计算某个日期所属的年份与周数,并支持从年份加周数反推出该周的周一。ISO 周从周一开始,包含当年第一个周四的那一周为第 1 周,因此跨年日期可能属于上一年的最后一周或下一年的第 1 周。它适合周报、排班、按周聚合指标等场景。
关键代码
isoWeek 先把日期归一到 UTC 的日粒度,再找到当周周四。把周四所在的年份作为 ISO 年,并计算该年第一个周四,两个周四之间相差的周数加一就是周号。isoWeekStart 以 1 月 4 日一定落在第 1 周为锚点,回退到周一,再加上周偏移,最后调用 isoWeek 做往返校验,不存在的第 53 周会抛 RangeError。
使用注意
所有计算都基于 UTC,避免本地时区与夏令时导致日期偏移。year 限制在 1 到 9999,week 限制在 1 到 53,但并非每年都有第 53 周,因此反解后必须校验。返回的周一是 UTC 零点,如果需要本地日历日期,应再按目标时区转换。ISO 周与日历周、财务周的定义不同,不要混用。
验证方式
断言 2026 年 1 月 1 日属于 2026 年第 1 周,2024 年 12 月 30 日属于 2025 年第 1 周。对第 1 周反解得到 2025 年 12 月 29 日。测试不存在的第 53 周抛 RangeError,非法年份与周数也抛 RangeError,并验证同一周内任意一天返回相同周号。
代码示例
export function isoWeek(date = new Date()) {
const value = toUtcDate(date);
const thursday = new Date(value.getTime());
const weekday = value.getUTCDay() || 7;
thursday.setUTCDate(value.getUTCDate() + 4 - weekday);
const year = thursday.getUTCFullYear();
const firstThursday = new Date(Date.UTC(year, 0, 4));
const offset = firstThursday.getUTCDay() || 7;
firstThursday.setUTCDate(firstThursday.getUTCDate() + 4 - offset);
const week = 1 + Math.round((thursday - firstThursday) / (7 * 24 * 60 * 60 * 1000));
return { year, week };
}
export function isoWeekStart(year, week) {
if (!Number.isInteger(year) || year < 1 || year > 9999) throw new RangeError("year out of range");
if (!Number.isInteger(week) || week < 1 || week > 53) throw new RangeError("week out of range");
const januaryFourth = new Date(Date.UTC(year, 0, 4));
const weekday = januaryFourth.getUTCDay() || 7;
const monday = new Date(januaryFourth.getTime());
monday.setUTCDate(januaryFourth.getUTCDate() - weekday + 1 + (week - 1) * 7);
const check = isoWeek(monday);
if (check.year !== year || check.week !== week) {
throw new RangeError("week does not exist in that ISO year");
}
return monday;
}
function toUtcDate(date) {
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
throw new TypeError("date must be a valid Date");
}
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
}
const week = isoWeek(new Date("2026-01-01T00:00:00Z"));
console.log(week);
console.log(isoWeekStart(week.year, week.week).toISOString().slice(0, 10));
console.log(isoWeek(new Date("2024-12-30T00:00:00Z")));