如何用JS基于固定工作时段计算指定工作小时后的对应日期时间
实现方案说明
原有代码问题分析
原有代码存在三处核心逻辑缺陷,是导致结果不准确的主要原因:
- 阈值逻辑无业务依据:代码中
timeRemaining >= 21的跳步判断没有对应业务规则支撑,跳转逻辑混乱,既不符合周末跳过的要求,也会出现时长多算、漏算的问题 - 时长累加逻辑错误:每次直接累加当日剩余全部工作时长,没有判断剩余待填充的时长是否小于当日剩余工作时长,最终统计的时长必然会超过目标的31.5小时
- 缺少边界时间校准逻辑:未处理起始时间落在非工作时段(凌晨、下班后、周末)的场景,计算的时间基准本身就存在偏差
原生JS实现方案(无需逐单位迭代)
直接按工作日维度计算,最多循环次数等于需要跨的天数,性能远高于逐分钟/逐秒迭代:
// 工作规则配置,可按需调整 const WORK_RULES = { workStart: [6, 0], // 工作开始时间 [时, 分] workEnd: [16, 30], // 工作结束时间 [时, 分] // 单日总工作时长(毫秒) get workDayTotalMs() { const endMs = this.workEnd[0] * 3600000 + this.workEnd[1] * 60000; const startMs = this.workStart[0] * 3600000 + this.workStart[1] * 60000; return endMs - startMs; } }; /** * 为指定时间添加指定时长的工作时间 * @param {Date} startDate 起始时间 * @param {Number} workHours 需要添加的工作小时数 * @returns {Date} 计算后的最终时间 */ function addWorkHours(startDate, workHours) { let currentTime = new Date(startDate); let remainMs = workHours * 3600000; // 工具方法:跳转至下一个工作日的起始时间 const jumpToNextWorkDay = (date) => { do { date.setDate(date.getDate() + 1); } while (date.getDay() === 0 || date.getDay() === 6); date.setHours(...WORK_RULES.workStart, 0, 0); return date; }; // 第一步:校准起始时间到合法的工作时间点 while (true) { const weekDay = currentTime.getDay(); // 周末直接跳转到下一个工作日 if (weekDay === 0 || weekDay === 6) { currentTime = jumpToNextWorkDay(currentTime); continue; } const dayStart = new Date(currentTime); dayStart.setHours(...WORK_RULES.workStart, 0, 0); const dayEnd = new Date(currentTime); dayEnd.setHours(...WORK_RULES.workEnd, 0, 0); // 早于当日工作开始时间,校准为当日工作开始时间 if (currentTime < dayStart) { currentTime = dayStart; break; } // 晚于当日工作结束时间,跳转到下一个工作日开始 if (currentTime > dayEnd) { currentTime = jumpToNextWorkDay(currentTime); continue; } // 已经落在工作时段内,直接结束校准 break; } // 第二步:逐工作日扣除待计算时长 while (remainMs > 0) { const dayEnd = new Date(currentTime); dayEnd.setHours(...WORK_RULES.workEnd, 0, 0); const dayRemainMs = dayEnd - currentTime; // 剩余时长可在当日用完,直接计算最终时间 if (remainMs <= dayRemainMs) { currentTime.setTime(currentTime.getTime() + remainMs); break; } // 当日时长不够,扣减后跳转到下一个工作日 remainMs -= dayRemainMs; currentTime = jumpToNextWorkDay(currentTime); } return currentTime; } // 测试示例 const testStart = new Date('2021-08-27T12:00:00'); const result = addWorkHours(testStart, 31.5); console.log(result);
第三方库可选方案
如果需要支持节假日配置、多时区等复杂场景,可以直接使用成熟的第三方库:
moment-business-time:基于moment.js的扩展,支持自定义工作时段、节假日,可直接调用addWorkingTime方法实现需求date-fns-business-hours:基于date-fns的轻量化扩展,无额外依赖,适配原生日期对象的使用习惯
内容的提问来源于stack exchange,提问作者Hikalea
相关产品推荐
相关产品推荐

