2 回答

TA貢獻1802條經驗 獲得超4個贊
這是我對你的問題的解決方案。
我不知道我是否理解正確,但基本上這是算法:
1-將所有時間字符串轉換為整數并對參加時間段的每個日期列表進行排序。
2- 合并每個日期的重疊時段,例如,如果一個時段從 9 點到 12 點,另一個時段從 11 點到 13 點,則將其合并為從 9 點到 13 點的單個時段。
3- 將每個日期的所有參加時間相加。
<?php
$array = [
? ? '2020-07-14' =>[
? ? ? ? [
? ? ? ? ? ? 'start_time' => '09:00:00',
? ? ? ? ? ? 'end_time' => '13:00:00',
? ? ? ? ? ? 'hours' => '4 hours 0 mins',
? ? ? ? ],
? ? ? ? [
? ? ? ? ? ? 'start_time' => '13:30:00',
? ? ? ? ? ? 'end_time' => '16:30:00',
? ? ? ? ? ? 'hours' => '3 hours 0 mins',
? ? ? ? ],
? ? ? ? [
? ? ? ? ? ? 'start_time' => '09:00:00',
? ? ? ? ? ? 'end_time' => '14:00:00',
? ? ? ? ? ? 'hours' => '5 hours 0 mins',
? ? ? ? ],
? ? ? ? [
? ? ? ? ? ? 'start_time' => '17:00:00',
? ? ? ? ? ? 'end_time' => '18:00:00',
? ? ? ? ? ? 'hours' => '1 hours 0 mins',
? ? ? ? ]
? ? ],
? ? '2020-07-15' => [
? ? ? ? [
? ? ? ? ? ? 'start_time' => '09:00:00',
? ? ? ? ? ? 'end_time' => '14:00:00',
? ? ? ? ? ? 'hours' => '5 hours 0 mins',
? ? ? ? ],
? ? ? ? [
? ? ? ? ? ? 'start_time' => '13:30:00',
? ? ? ? ? ? 'end_time' => '17:00:00',
? ? ? ? ? ? 'hours' => '4 hours 30 mins',
? ? ? ? ]
? ? ],
];
// Convert all times strings into integers and sort each day list
// by the start time
$r = parseTimesAndSort($array);
// Combine overlaping periods in a single period
$r = flatternOverlaps($r);
// Sum all the periods in each date
$r = sumPeriods($r);
// Applly the result to the original array
foreach($r as $date => $item){
? ? $array[$date]['total_attended_hours'] = $item;
}
print_r($array);
/**
?* Given a time string returns the number of seconds from 00:00:00 as integer.
?* example: 09:30:10 => 34210 (9*3600 + 30*60 + 10)
?* @param $time
?* @return int
?*/
function timeToSeconds($time){
? ? $list = explode(":", $time);
? ? return $list[0] * 3600 + $list[1] * 60 + $list[2];
}
/**
?* Given an integer as seconds returns the time string in 00:00:00 format.
?* example: 34210 => 09:30:10
?* @param $value
?* @return string
?*/
function secondsToTime($value){
? ? $hours = floor($value/3600);
? ? $min = floor(($value%3600) / 60);
? ? $secods = floor($value % 60);
? ? return str_pad($hours, 2, "0", STR_PAD_LEFT)
? ? ? ? .":".str_pad($min, 2, "0", STR_PAD_LEFT)
? ? ? ? .":".str_pad($secods, 2, "0", STR_PAD_LEFT);
}
/**
?* Function to compare two periods
?* @param $a
?* @param $b
?* @return int
?*/
function sortByStartTime($a, $b){
? ? if ($a['start_time'] == $b['start_time']){
? ? ? ? return 0;
? ? }
? ? return $a['start_time'] < $b['start_time'] ? -1 : 1;
}
/**
?* Parses the periods string times to integers and sorts them
?* @param $array
?* @return array
?*/
function parseTimesAndSort($array){
? ? $r = [];
? ? foreach($array as $date => $list){
? ? ? ? $current = [];
? ? ? ? foreach($list as $item){
? ? ? ? ? ? $current[] = [
? ? ? ? ? ? ? ? 'start_time' => timeToSeconds($item['start_time']),
? ? ? ? ? ? ? ? 'end_time' => timeToSeconds($item['end_time']),
? ? ? ? ? ? ];
? ? ? ? }
? ? ? ? usort($current, 'sortByStartTime');
? ? ? ? $r[$date] = $current;
? ? }
? ? return $r;
}
/**
?* Finds overlapping periods and combines them
?* @param $array
?* @return array
?*/
function flatternOverlaps($array){
? ? $r = [];
? ? foreach($array as $date => $list){
? ? ? ? $currentList = [];
? ? ? ? $prev = null;
? ? ? ? foreach($list as $item){
? ? ? ? ? ? if ($prev && $item['start_time'] < $prev['end_time']){
? ? ? ? ? ? ? ? if ($item['end_time'] > $prev['end_time']) {
? ? ? ? ? ? ? ? ? ? $prev['end_time'] = $item['end_time'];
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? else{
? ? ? ? ? ? ? ? $currentList[] = $item;
? ? ? ? ? ? }
? ? ? ? ? ? // Point prev to the last item in the current list
? ? ? ? ? ? $prev = &$currentList[count($currentList)-1];
? ? ? ? }
? ? ? ? unset($prev);
? ? ? ? $r[$date] = $currentList;
? ? }
? ? return $r;
}
/**
?* Sums the periods of each date
?* @param $array
?* @return array
?*/
function sumPeriods($array){
? ? $r = [];
? ? foreach($array as $date => $list){
? ? ? ? $seconds = array_reduce($list, function($carry, $item){ return $carry + $item['end_time'] - $item['start_time']; }, 0);
? ? ? ? $r[$date] = secondsToTime($seconds);
? ? }
? ? return $r;
}

TA貢獻1963條經驗 獲得超6個贊
我更改了 2 行,81 和 82(如下所示)。您不需要 strtotime($previous_end_time) 來比較它,因為時間是 24 小時制,而且當您減去時,您想要減去 $previous_end_time 而不是 $item['end_time']。
if (($item['start_time']) < $previous_end_time) { $sum -= Helper::getMinsBetweenTwoTimes($item['start_time'], $previous_end_time); }
- 2 回答
- 0 關注
- 150 瀏覽
添加回答
舉報