You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 08:06:19