如何在JavaScript中将工作日数据数组转换为带总工时的日期范围数组?
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

