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

如何在JavaScript中将工作日数据数组转换为带总工时的日期范围数组?

Solution: Merge Consecutive Workdays with Total Hours

Great question! The core issue with your current getDayRanges function is that it only compares day numbers in isolation—this fails when consecutive days cross a month boundary (like Oct 31 to Nov 1) or year boundary. Instead, we need to work with full date objects to accurately check if two days are sequential, then build our merged ranges accordingly.

Let's break this down into actionable steps:

Step 1: Parse Input Strings into Structured Objects

First, convert each input string into an object that holds the full date, hours worked, and human-readable date components (month abbreviation, year, day). This makes it easy to both check for consecutive days and format our final output.

const parseWorkday = (str) => {
  // Split the string into parts: ["Nov", "23", "2021", "8", "hrs"]
  const parts = str.split(' ');
  // Note: Months are 0-indexed in JavaScript Date
  const monthIndex = new Date(`${parts[0]} 1`).getMonth();
  const date = new Date(parts[2], monthIndex, parts[1]);
  return {
    date,
    hours: parseInt(parts[3], 10),
    monthStr: parts[0],
    year: parseInt(parts[2], 10),
    day: parseInt(parts[1], 10)
  };
};

Step 2: Merge Consecutive Days into Ranges

Now we can iterate over the parsed objects, grouping consecutive days (using their full date to check sequence) and accumulating total hours.

const mergeWorkdayRanges = (workdays) => {
  if (workdays.length === 0) return [];

  // Parse all input strings into structured objects
  const parsedDays = workdays.map(parseWorkday);
  // Ensure days are sorted by date (in case input is out of order)
  parsedDays.sort((a, b) => a.date.getTime() - b.date.getTime());

  const mergedRanges = [];
  // Start with the first day as the initial group
  let currentGroup = {
    start: parsedDays[0],
    end: parsedDays[0],
    totalHours: parsedDays[0].hours
  };

  for (let i = 1; i < parsedDays.length; i++) {
    const currentDay = parsedDays[i];
    const prevDayEnd = currentGroup.end.date;
    // Calculate the difference in milliseconds between current day and previous day's end
    const dayDiff = currentDay.date.getTime() - prevDayEnd.getTime();
    // 1 day in milliseconds = 24 * 60 * 60 * 1000 = 86400000
    const isConsecutive = dayDiff === 86400000;

    if (isConsecutive) {
      // Update the current group's end and total hours
      currentGroup.end = currentDay;
      currentGroup.totalHours += currentDay.hours;
    } else {
      // Convert current group to the output string format and add to results
      mergedRanges.push(formatRange(currentGroup));
      // Start a new group with the current day
      currentGroup = {
        start: currentDay,
        end: currentDay,
        totalHours: currentDay.hours
      };
    }
  }

  // Add the last group to the results
  mergedRanges.push(formatRange(currentGroup));
  return mergedRanges;
};

// Helper function to format a group into the desired string
const formatRange = (group) => {
  const { start, end, totalHours } = group;
  if (start.date.getTime() === end.date.getTime()) {
    // Single day case
    return `${start.monthStr} ${start.day} ${start.year} ${totalHours} hrs`;
  } else {
    // Date range case (check if same month/year to avoid redundant info)
    if (start.year === end.year && start.monthStr === end.monthStr) {
      return `${start.monthStr} ${start.day}-${end.day} ${start.year} ${totalHours} hrs`;
    } else {
      // Handle cross-month or cross-year ranges (e.g., "Oct 31 - Nov 1 2021 16 hrs")
      return `${start.monthStr} ${start.day} - ${end.monthStr} ${end.day} ${start.year} ${totalHours} hrs`;
    }
  }
};

Step 3: Test with Your Example Input

Let's run this with your sample input to verify it works:

const input = ["Nov 23 2021 8 hrs", "Nov 24 2021 8 hrs", "Nov 27 2021 8 hrs", "Dec 3 2021 8 hrs"];
console.log(mergeWorkdayRanges(input));
// Output: ["Nov 23-24 2021 16 hrs", "Nov 27 2021 8 hrs", "Dec 3 2021 8 hrs"]

Addressing Your Confusion

You don't need nested loops or separate month/year parameters for your function. By parsing each entry into a full Date object, we let JavaScript handle the complexity of month/year boundaries automatically. The key is checking if the time difference between two days is exactly one full day (86400000 milliseconds) to determine continuity.

This approach also scales to edge cases like cross-month days (e.g., "Oct 31 2021 8 hrs", "Nov 1 2021 8 hrs" becomes "Oct 31 - Nov 1 2021 16 hrs") or even cross-year days.

内容的提问来源于stack exchange,提问作者Logikev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.01 02:34:05