简体   繁体   English

使用 PHP 从具有 start_time 和 end_time 的多维数组中查找总小时数?

[英]Find total hours from multi-dimensional array with start_time and end_time using PHP?

I am trying to calculate the correct total hours using start_time and end_time我正在尝试使用start_timeend_time计算正确的总小时数

Here the algorithm I am using which has been provided by @Pilan:这是@Pilan提供的我正在使用的算法:

  1. For each set of Time-Bookings do the following对于每组时间预订,请执行以下操作
  2. Find the smallest start_time找到最小的start_time
  3. Add the duration between start_time and end_time to a sumstart_timeend_time之间的duration加到sum
  4. Find the next smallest Time-Booking by start_timestart_time查找下一个最小的 Time-Booking
  5. IF start_time < previous_end_time subtract difference from sum END IF IF start_time < previous_end_timesum中减去差 END IF
  6. Add duration between start_time and end_timestart_timeend_time之间添加duration
  7. Jump to 4 until there is no matching element left.跳到 4 直到没有匹配的元素。

Using the above I have managed to create below code:使用上面我已经设法创建以下代码:

<?php
class Helper 
{
    
     public static function minToHour($minutes)
    {
        if (empty($minutes)) {
            return 0;
        }
        $hours = floor($minutes / 60);
        $min = $minutes - ($hours * 60);

        return $hours . ":" . $min;
    }
public static function getMinsBetweenTwoTimes($start, $end)
    {
        $datetime1 = strtotime($start);
        $datetime2 = strtotime($end);
        $interval = abs($datetime2 - $datetime1);
        $minutes = round($interval / 60);
        return $minutes;
    }
   
}
$array =array (
  '2020-07-14' => 
  array (
  
    array (
      'start_time' => '09:00:00',
      'end_time' => '13:00:00',
      'hours' => '4 hours 0 mins',
    ),
    1 => 
    array (
      'start_time' => '13:30:00',
      'end_time' => '16:30:00',
      'hours' => '3 hours 0 mins',
    ),
    2 => 
    array (
      'start_time' => '09:00:00',
      'end_time' => '14:00:00',
      'hours' => '5 hours 0 mins',
    ),
  ),
  '2020-07-15' => 
  array (

    array (
      'start_time' => '13:30:00',
      'end_time' => '17:00:00',
      'hours' => '3 hours 30 mins',
    ),
    1 => 
    array (
      'start_time' => '09:00:00',
      'end_time' => '14:00:00',
      'hours' => '5 hours 0 mins',
    ),
  ),
);

  $newArray = [];
            foreach ($array as $key => $dateArr) {
                array_multisort(
                    array_map('strtotime', array_column($dateArr, 'start_time')),
                    array_column($dateArr, 'start_time'),
                    $dateArr
                );
                $newArray[$key] [] = $dateArr;
            }
            $sumArr = [];
            foreach ($newArray as $date => $items) {
                $sum = 0;
                foreach ($items[0] as $key => $item) {
                    $sum += Helper::getMinsBetweenTwoTimes($item['start_time'], $item['end_time']);
                    if ($key > 0) {
                        $previous_end_time = $items[0][$key - 1]['end_time'] ?? null;
                        if (!empty($previous_end_time)) {
                            if (($item['start_time']) < strtotime($previous_end_time)) {
                                $sum -= Helper::getMinsBetweenTwoTimes($item['start_time'], $item['end_time']);
                            }
                        }
                    }
                }
                $newArray[$date] ['total_attended_hours'] = Helper::minToHour($sum);
            }
            echo "<pre>";
            print_r($newArray);
            exit;
?>

Playground sample code link Playground 示例代码链接

I changed 2 lines, 81 & 82 (shown below).我更改了 2 行,81 和 82(如下所示)。 You don't need to strtotime($previous_end_time) to compare it because times are in 24 hour clock and also when you subtract you want to be subtracting $previous_end_time not $item['end_time'].你不需要 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);
}

This is my solution to your problem.这是我对您的问题的解决方案。

I don't know if I have understand it correctly but basically this is the algorithm:我不知道我是否理解正确,但基本上这是算法:

1- Convert all times strings to integers and sort each date list of attended periods. 1- 将所有时间字符串转换为整数并对每个参加时间段的日期列表进行排序。

2- Combine overlaping periods for each date, for exampe if one period goes from 9 to 12 and other from 11h to 13h, it is combined in a single period from 9h to 13h. 2- 合并每个日期的重叠时段,例如,如果一个时段从 9 点到 12 点,另一个从 11 点到 13 点,则将其合并为从 9 点到 13 点的单个时段。

3- Sum all the attehnded hours for each date. 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;
}

https://paiza.io/projects/lq_rgfR9DptBC7Ors-Xorw https://paiza.io/projects/lq_rgfR9DptBC7Ors-Xorw

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 获取每个日期 PHP 的 start_time 和 end_time 的总小时数? - Get total hours from start_time and end_time for each date PHP? 如何使用多维数组以及重叠检查从开始时间和结束时间获取总小时数? - How to get total hours from start time and end time with multi dimensional array along with over lapping check? sql使用start_time和end_time进行复杂数据排序 - sql Complex data sorting with start_time and end_time PayPal + PHP - 获取收款(开始时间/结束时间) - PayPal + PHP - Fetch incoming payments (start_time / end_time) 7个SQL行中的第一个开始时间和最后一个结束时间,仅显示1个SQL结果 - First start_time and last end_time from 7 SQL rows to show only 1 SQL result 如果在范围内找到,如何找到 start_time 和 end_time 范围,然后检查 start_date 和 end_date - How to find the start_time and end_time range if found in the range then check start_date and end_date 我想要`start_time` 和`end_time` 计数如何得到这个? - I want to `start_time` and `end_time` count how can get this? 如何在yii2中使用start_time和end_time获取MySql中已有的记录? - How to fetch already existing records in MySql with start_time and end_time in yii2? Laravel 5.1获取start_time和end_time之间的行(两个不同的字段) - Laravel 5.1 Fetch Rows between start_time and end_time (two different fields) 我想显示当前时间不在(column)start_time和end_time(column)之间的所有人员记录 - I want to display all personnel records that the current time is not between to the (column)start_time and end_time(column)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM