JavaScript/momentjs实现日期时段首尾衔接并保留原时长
问题描述
我有一个如下结构的日期数组:
dates: [ { start: '30.05.2022 02:30:00', end: '30.05.2022 03:30:00' } , { start: '30.05.2022 02:34:01', end: '3.06.2022 04:34:01' } ]
我的需求是:将数组中第二项的start值设置为第一项的end值,同时不改变每个时段的原有时长,最终期望得到的结果如下:
dates: [ { start: '30.05.2022 02:30:00', end: '30.05.2022 03:30:00' } , { start: '30.05.2022 03:30:00', end: '3.06.2022 05:30:00' } ]
我尝试编写了实现代码,但返回结果不正确,问题出在momentjs处理超过24小时的duration时计算异常,我的实现代码如下:
const dateFormat = 'DD.MM.YYYY HH:mm:ss' const parseDateByMoment= (dateString) => { return moment(dateString, dateFormat) } const convertDateToString= (dateString) => { return dateString.format(dateFormat) } const addOrSubtractDate = (startDate, endDate, isAdding) => { const startDateDuration = moment.duration(startDate.format("HH:mm:ss")) const endDateDuration = moment.duration(endDate.format("HH:mm:ss")) const result = isAdding ? endDateDuration + startDateDuration : endDateDuration - startDateDuration return moment.duration(result) } const formatToTime = (diff) => { return moment.utc(diff).format("HH:mm:ss.SSS"); } const makeSequentialTimeInTracks = (dates) => { dates.forEach((aDate, index) => { if (index !== 0) { let prevEndDate = dates[index - 1].end prevEndDate = parseDateByMoment(prevEndDate) aDate.start = convertDateToString(prevEndDate) // console.log('aDate.start is', aDate.start) const start = parseDateByMoment(aDate.start) const durationStart = moment.duration(start.format("HH:mm:ss")) console.log('2. aDate.start is', start) let end = parseDateByMoment(aDate.end) const durationEnd = moment.duration(end.format("HH:mm:ss")) console.log('2. aDate.end is', end) let difference = addOrSubtractDate(start, end, false) difference = moment(difference) let durationDifference = moment.duration(difference.format("HH:mm:ss")) console.log('durationDifference', moment.duration(durationDifference)) const temp = start.clone() console.log('2. temp is', temp) temp.startOf('day').add(difference) console.log('temp', temp.format(dateFormat)) // aDate.end = convertDateToString(addOrSubtractDate(start, difference, true)) } }); return dates } const sequantialTime = makeSequentialTimeInTracks(dates) console.log(dates)
需要基于纯JavaScript或者momentjs库的可行解决方案。
问题原因
原有代码的核心错误是计算时长时只提取了时间部分HH:mm:ss,丢弃了跨天产生的日期差值,一旦时段长度超过24小时就会丢失天数部分的时长,导致计算结果偏差。
正确逻辑不需要单独拆分时间部分计算duration,只需要先算出每个时段原始的毫秒级总长度,再从前一个时段的结束时间点累加这个固定长度,就能得到当前时段正确的结束时间,完全规避跨天、超过24小时的计算异常。
解决方案
基于momentjs的实现
const dateFormat = 'DD.MM.YYYY HH:mm:ss'; const makeSequentialTimeInTracks = (dates) => { // 从第二项开始遍历,第一项无需修改 for (let i = 1; i < dates.length; i++) { const current = dates[i]; const prev = dates[i - 1]; // 计算当前时段原始固定时长(毫秒差,自动包含跨天的时长部分) const originalStart = moment(current.start, dateFormat); const originalEnd = moment(current.end, dateFormat); const durationMs = originalEnd.diff(originalStart); // 将当前项start设置为前一项的end const newStart = moment(prev.end, dateFormat); current.start = newStart.format(dateFormat); // 新end = 新start + 原始固定时长 const newEnd = newStart.clone().add(durationMs, 'ms'); current.end = newEnd.format(dateFormat); } return dates; }; // 测试用例 const dates = [ { start: '30.05.2022 02:30:00', end: '30.05.2022 03:30:00' }, { start: '30.05.2022 02:34:01', end: '3.06.2022 04:34:01' } ]; const sequentialTime = makeSequentialTimeInTracks(dates); console.log(sequentialTime);
运行后输出结果和预期完全一致:
[ { start: '30.05.2022 02:30:00', end: '30.05.2022 03:30:00' }, { start: '30.05.2022 03:30:00', end: '03.06.2022 05:30:00' } ]
纯JavaScript实现(无第三方依赖)
如果不想引入moment,可直接用原生Date对象实现,逻辑完全一致:
const dateFormat = 'DD.MM.YYYY HH:mm:ss'; // 格式字符串转Date对象 const parseDate = (dateStr) => { const [datePart, timePart] = dateStr.split(' '); const [day, month, year] = datePart.split('.').map(Number); const [hour, minute, second] = timePart.split(':').map(Number); // 原生Date月份从0开始计数,需要减1 return new Date(year, month - 1, day, hour, minute, second); }; // Date对象转指定格式字符串 const formatDate = (date) => { const pad = (num) => String(num).padStart(2, '0'); return `${pad(date.getDate())}.${pad(date.getMonth() + 1)}.${date.getFullYear()} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; }; const makeSequentialTimeInTracks = (dates) => { for (let i = 1; i < dates.length; i++) { const current = dates[i]; const prev = dates[i - 1]; // 计算原始时长毫秒差 const originalStart = parseDate(current.start); const originalEnd = parseDate(current.end); const durationMs = originalEnd.getTime() - originalStart.getTime(); // 计算新的起止时间 const newStart = parseDate(prev.end); current.start = formatDate(newStart); const newEnd = new Date(newStart.getTime() + durationMs); current.end = formatDate(newEnd); } return dates; }; // 测试用例 const dates = [ { start: '30.05.2022 02:30:00', end: '30.05.2022 03:30:00' }, { start: '30.05.2022 02:34:01', end: '3.06.2022 04:34:01' } ]; const sequentialTime = makeSequentialTimeInTracks(dates); console.log(sequentialTime);
内容的提问来源于stack exchange,提问作者Learner
相关产品推荐
相关产品推荐

