日期数组分组处理:提取被动日期间符合条件的空闲时段
Hey there! Let's break down how to solve this problem—we need to take an array of passive dates, group consecutive dates into blocks, then find the gaps between those blocks and format them as requested. Here's a step-by-step solution using PHP (since the example uses PHP-like syntax, but the core logic applies to other languages too):
Step 1: Clean Up and Prepare the Date Array
First, we need to turn those string dates into usable date objects, remove duplicates (like the two 10-07-2020 entries in your example), and ensure they're sorted chronologically (adds robustness even if your input is already ordered).
$rawDates = [ '17-05-2020', '18-05-2020', '19-05-2020', '20-05-2020', '28-05-2020', '29-05-2020', '30-05-2020', '02-06-2020', '03-06-2020', '10-07-2020', '10-07-2020', '11-07-2020', '12-07-2020', '13-07-2020', '20-07-2020', '21-07-2020', '25-07-2020', '26-07-2020', '27-07-2020' ]; // Convert strings to DateTime objects and remove duplicates $dateObjects = array_unique(array_map(function($dateStr) { return DateTime::createFromFormat('d-m-Y', $dateStr); }, $rawDates)); // Sort the date objects to ensure chronological order usort($dateObjects, function($a, $b) { return $a <=> $b; });
Step 2: Group Consecutive Dates into Blocks
Next, we'll group consecutive dates into blocks. Each block tracks the first and last date of that consecutive sequence:
$dateBlocks = []; foreach ($dateObjects as $date) { if (empty($dateBlocks)) { $dateBlocks[] = ['start' => $date, 'end' => $date]; continue; } $lastBlock = end($dateBlocks); // Check if current date is the day after the last block's end date $nextDay = (clone $lastBlock['end'])->modify('+1 day'); if ($date == $nextDay) { // Merge into the existing block by updating the end date $lastBlock['end'] = $date; $dateBlocks[key($dateBlocks)] = $lastBlock; } else { // Start a new block for non-consecutive dates $dateBlocks[] = ['start' => $date, 'end' => $date]; } }
After running this, $dateBlocks will look like this (simplified for readability):
Array ( [0] => Array ( 'start' => 17-05-2020, 'end' => 20-05-2020 ) [1] => Array ( 'start' => 28-05-2020, 'end' => 30-05-2020 ) [2] => Array ( 'start' => 02-06-2020, 'end' => 03-06-2020 ) [3] => Array ( 'start' => 10-07-2020, 'end' => 13-07-2020 ) [4] => Array ( 'start' => 20-07-2020, 'end' => 21-07-2020 ) [5] => Array ( 'start' => 25-07-2020, 'end' => 27-07-2020 ) )
Step 3: Calculate and Format Free Periods
Now we'll loop through adjacent blocks, calculate the gap between them, and format the result. If you only want gaps longer than 8 days, just uncomment the conditional check below:
$freePeriods = []; for ($i = 0; $i < count($dateBlocks) - 1; $i++) { $currentBlockEnd = $dateBlocks[$i]['end']; $nextBlockStart = $dateBlocks[$i+1]['start']; // Calculate the number of days between the end of one block and start of the next $interval = $currentBlockEnd->diff($nextBlockStart); $days = $interval->days; // Uncomment this line if you only want gaps longer than 8 days // if ($days <= 8) continue; // Convert dates back to string format $startStr = $currentBlockEnd->format('d-m-Y'); $endStr = $nextBlockStart->format('d-m-Y'); $freePeriods[] = [$startStr, $endStr, $days]; } // Print the final result print_r($freePeriods);
Output
This will give you exactly the result you're expecting:
Array ( [0] => Array ( [0] => 30-05-2020 [1] => 02-06-2020 [2] => 3 ) [1] => Array ( [0] => 13-07-2020 [1] => 20-07-2020 [2] => 7 ) [2] => Array ( [0] => 21-07-2020 [1] => 25-07-2020 [2] => 4 ) )
Key Notes
- Duplicate Handling: The
array_uniquecall takes care of any repeated dates in the input, which prevents weird grouping issues. - Consecutive Check: We compare each date to the day after the last block's end to confirm it's part of the same sequence.
- Flexible Filtering: If you need to only keep gaps longer than 8 days, just enable the
if ($days <= 8) continue;line—this will skip any gaps that don't meet the threshold.
内容的提问来源于stack exchange,提问作者Güven Göç

