如何用JavaScript获取两个日期间每月第N个周几的日期实现重复日程
解决方案
核心思路
首先从起始日程中提取两个重复规则参数:
targetWeekday:目标周几,对应JSDate.getDay()的返回值(0=周日,1=周一,...6=周六)weekOrder:目标是当月的第几个周几,支持1/2/3/4代表前四个,-1代表最后一个
之后遍历起止日期范围内的每一个月份,对每个月计算符合规则的日期,落在时间范围内的就加入展示列表即可。
核心工具函数
先实现一个通用的「按规则取月度周几日期」函数,逻辑清晰易修改:
/** * 计算指定年月的第N个周几的日期 * @param {number} year 年份 例:2024 * @param {number} month 月份(0基,和JS Date对齐) 例:3代表4月 * @param {number} targetWeekday 目标周几 0=周日 1=周一...6=周六 * @param {number} weekOrder 第几个 1=第一个 2=第二个...-1=最后一个 * @returns {Date|null} 符合条件的日期,不存在则返回null */ function getMonthWeekdayDate(year, month, targetWeekday, weekOrder) { if (weekOrder === -1) { // 算最后一个周几的逻辑:先拿当月最后一天 const lastDayOfMonth = new Date(year, month + 1, 0); const lastDayWeekday = lastDayOfMonth.getDay(); // 计算需要往前减的天数 const diff = (lastDayWeekday - targetWeekday + 7) % 7; lastDayOfMonth.setDate(lastDayOfMonth.getDate() - diff); return lastDayOfMonth; } // 算第N个周几的逻辑:先拿当月第一天 const firstDayOfMonth = new Date(year, month, 1); const firstDayWeekday = firstDayOfMonth.getDay(); // 计算第一个目标周几需要加的天数 let addDays = (targetWeekday - firstDayWeekday + 7) % 7; // 加上第N个的偏移 addDays += (weekOrder - 1) * 7; const result = new Date(year, month, 1 + addDays); // 校验算出来的日期是不是还在当月,避免第5个周几不存在的情况 if (result.getMonth() !== month) return null; return result; }
整合到原有代码
在你原来的if (jsonArray[num].tipoRepeticion == "2")代码块中加入以下逻辑即可:
if (jsonArray[num].tipoRepeticion == "2") { // 1. 从起始日期提取重复规则 const targetWeekday = starDate.getDay(); // 计算起始日期是当月的第几个周几 const startDateDay = starDate.getDate(); const weekOrder = Math.ceil(startDateDay / 7); // 补充:如果你的需求支持用户手动选最后一个,这里weekOrder可以改成从用户配置取-1即可 // 2. 遍历起止范围内的每一个月 let currentYear = starDate.getFullYear(); let currentMonth = starDate.getMonth(); const endYear = endDate.getFullYear(); const endMonth = endDate.getMonth(); while (currentYear < endYear || (currentYear === endYear && currentMonth <= endMonth)) { // 计算当前月符合规则的日期 const currentEventDate = getMonthWeekdayDate(currentYear, currentMonth, targetWeekday, weekOrder); // 校验日期有效、且在起止日期范围内 if (currentEventDate && currentEventDate >= starDate && currentEventDate <= endDate) { // 沿用你原来的格式化和入队逻辑 const str = ((currentEventDate.getMonth()+1) + "/" + currentEventDate.getDate() + "/" + currentEventDate.getFullYear()).toString(); eventMen.push({ date: constructDates(str, 0, jsonArray[num].tipoRepeticion), title: jsonArray[num].titulo, descripcion: jsonArray[num].descripcion, tipo: jsonArray[num].tipo, tipoRepeticion : jsonArray[num].tipoRepeticion }); } // 跳到下一个月 currentMonth++; if (currentMonth > 11) { currentMonth = 0; currentYear++; } } }
注意点
- 如果需要支持用户手动选择「最后一个周几」的规则,只需要把代码中
weekOrder的取值改成从你保存用户配置的字段取-1即可,工具函数已经兼容该逻辑 - 代码自动过滤了不存在的第5个周几的情况(比如某个月只有4个周三,就不会生成无效日期)
- 遍历月份的逻辑比你原来遍历所有天数的性能更高,尤其是日程跨度好几年的场景优势更明显
内容的提问来源于stack exchange,提问作者Claudio César Pérez Velasco
相关产品推荐
相关产品推荐

