如何筛选当月提醒日期?JavaScript生成重复周期提醒日期错误排查
重复提醒日期计算功能修复
基础规则说明
从数据库获取的配置参数如下:
recurrence_Date = 2021-10-01, recurevery=10, frequency=yearly reminder_setdaysbefore=30
提醒规则为:从项目起始日期开始,按指定周期重复,提前配置的天数发送提醒,如以上配置的提醒日期为2021-09-01、2031-09-01、2041-09-01,以此类推。
区间查询规则:
- 若用户搜索区间为2031-01-01至2041-12-01,返回结果应为2031-09-01、2041-09-01
- 若用户搜索区间为2031-01-01至2031-12-01,返回结果应为2031-09-01
现有问题说明
用到的JSON配置
{ "projectremindshow":[ { "project_ID":"1", "project_title":"Althurath", "period_type":"Monthly", "recurrence_date":"2021-01-01", "reminder_set_days":"30", "recur_every":"6", "start_date": "2020-12-02" }, { "project_ID":"2", "project_title":"Help AG", "period_type":"Yearly", "recurrence_date":"2021-06-01", "reminder_set_days":"30", "recur_every":"1", "start_date": "2021-05-02" } ] }
示例1错误
用户选择搜索区间2021-06-01 至 2021-06-31:
- 当前返回结果:
[ { "project": "Althurath", "reminderDate": "2021-05-02" }, { "project": "Help AG", "reminderDate": "2021-05-02" } ]
- 预期返回结果:
[ { "project": "Althurath", "reminderDate": "2021-06-02" } ]
错误原因:Althurath项目起始日期为2020-12-02,每6个月重复一次,对应提醒日期应为2021-06-02,当前逻辑返回了早于搜索起始日期的结果。
示例2错误
用户选择搜索区间2021-01-01 至 2022-12-31,当前返回结果不符合预期,预期结果如下:
[ { "project": "Althurath", "reminderDate": "2021-06-01" }, { "project": "Althurath", "reminderDate": "2021-12-02" }, { "project": "Althurath", "reminderDate": "2022-06-01" }, { "project": "Help AG", "reminderDate": "2022-05-02" }, { "project": "Althurath", "reminderDate": "2022-12-02" } ]
问题根因
- 逻辑顺序错误:现有代码先对起始日期减去提前提醒天数,再判断是否在搜索区间内,导致提前天数计算后得到的日期早于搜索起始日期也被返回。
- 首周期计算错误:没有先计算出第一个大于等于项目起始日期的重复周期节点,直接从基准日期开始计算,导致周期偏移。
- Yearly周期累加逻辑问题:直接修改原日期对象,没有使用拷贝的新对象,存在隐性错误。
- HTML结构错误:tbody的id属性多写了
#,导致jQuery选择器无法匹配到对应DOM节点。
修复后的完整代码
JS代码
Date.prototype.addDays = function(days) { var date = new Date(this.valueOf()); date.setDate(date.getDate() + days); return date; } Date.prototype.subtractDays = function(days) { var date = new Date(this.valueOf()); date.setDate(date.getDate() - days); return date; } Date.prototype.addMonths = function(months) { var date = new Date(this.valueOf()); var d = date.getDate(); date.setMonth(date.getMonth() + months); if (date.getDate() != d) { date.setDate(0); } return date; } Date.prototype.addYears = function(years) { var date = new Date(this.valueOf()); var d = date.getDate(); date.setFullYear(date.getFullYear() + years); if (date.getDate() != d) { date.setDate(0); } return date; } var fromdate_recu_viewedit = "2021-06-01"; var todate_recu_viewedit = "2021-06-30"; // 修正6月没有31号的问题 const getPaymentPlan = ({ dateFrom, dateTo, recurrenceDate, daysBefore, period, skip, title }) => { skip = parseInt(skip); daysBefore = parseInt(daysBefore); let currentRecurDate = new Date(recurrenceDate.valueOf()); let recurrenceList = []; // 先计算第一个晚于等于dateFrom的重复日期 while (currentRecurDate.getTime() < dateFrom.getTime()) { switch (period) { case 'Monthly': currentRecurDate = currentRecurDate.addMonths(skip); break; case 'Yearly': currentRecurDate = currentRecurDate.addYears(skip); break; default: recurrenceList.push({ "project": "wrong period type is given", "reminderDate": null }) return recurrenceList; } } // 遍历所有符合区间的重复日期 while (true) { // 计算提醒日期 let reminderDate = currentRecurDate.subtractDays(daysBefore); // 提醒日期超过搜索结束日期就终止循环 if (reminderDate.getTime() > dateTo.getTime()) { break; } // 提醒日期大于等于搜索起始日期才加入结果 if (reminderDate.getTime() >= dateFrom.getTime()) { recurrenceList.push({ "project": title, "reminderDate": reminderDate.toISOString().split('T')[0] }); } // 计算下一个重复日期 switch (period) { case 'Monthly': currentRecurDate = currentRecurDate.addMonths(skip); break; case 'Yearly': currentRecurDate = currentRecurDate.addYears(skip); break; } } return recurrenceList; } let new_data = { "projectremindshow": [{ "project_ID": "1", "project_title": "Althurath", "period_type": "Monthly", "recurrence_date": "2021-01-01", "reminder_set_days": "30", "recur_every": "6", "start_date": "2020-12-02" }, { "project_ID": "2", "project_title": "Help AG", "period_type": "Yearly", "recurrence_date": "2021-06-01", "reminder_set_days": "30", "recur_every": "1", "start_date": "2021-05-02" }] } let inputList = []; for (var i = 0; i < new_data.projectremindshow.length; i++) { var proj = new_data.projectremindshow[i]; inputList.push({ dateFrom: new Date(fromdate_recu_viewedit), dateTo: new Date(todate_recu_viewedit), daysBefore: proj.reminder_set_days, recurrenceDate: new Date(proj.start_date), period: proj.period_type, skip: proj.recur_every, title: proj.project_title }); } let frequencyList = []; for (let i = 0; i < inputList.length; i++) { let plan = getPaymentPlan(inputList[i]); Array.prototype.push.apply(frequencyList, plan); } frequencyList.sort(compare); $.each(frequencyList, function(index, jsonObject) { var tableRow = '<tr>'; $.each(Object.keys(jsonObject), function(i, key) { tableRow += '<td>' + jsonObject[key] + '</td>'; }); tableRow += "</tr>"; $("#tablereminder").append(tableRow); }) function compare(a, b) { if (a.reminderDate < b.reminderDate) { return -1; } if (a.reminderDate > b.reminderDate) { return 1; } return 0; }
HTML代码
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <table> <tbody id="tablereminder"></tbody> </table>
额外修正说明
- 新增
addYears日期原型方法,统一日期操作逻辑,避免直接修改原日期对象 - 修正了6月没有31日的错误日期输入,避免日期对象自动跳转月份导致的计算偏差
- 新增参数类型转换,确保数字类型参数不会因字符串类型导致计算错误
内容的提问来源于stack exchange,提问作者Remesh sree
相关产品推荐
相关产品推荐

