【问题标题】:Staff schedule working alone minutes工作人员安排单独工作分钟
【发布时间】:2017-12-21 10:29:44
【问题描述】:

我有工作人员的时间清单。我需要了解是否有员工单独工作,以及他们一天单独工作了多少分钟

| staff| start | end   |
|:---  |:---   |:---   |
| 1    | 11:05 | 20:00 | 
| 2    | 11:00 | 17:00 |
| 3    | 19:00 | 03:00 |
| 4    | 13:00 | 20:00 |
| 5    | 19:00 | 03:00 |

对于Andreas' help,下面是获取第一个和最后一个单独工作的人的代码,但它并不完全正确。因为如果有 3 个不同时间的人单独工作,就会出现问题。 https://3v4l.org/6OmjO

$staff = array(1,2,3,4,5);
$start = array("11:05", "11:00", "19:00", "13:00", "19:00");
$end = array("20:00", "17:00", "03:00", "20:00", "03:05");

array_multisort($start, $end, $staff);

$aloneStart = (strtotime($start[1]) - strtotime($start[0])) / 60; // first and second items are the ones that may be working alone at start
$aloneEnd = (strtotime($end[count($end) - 1]) - strtotime($end[count($end) - 2])) / 60; // last and second to last are the ones that may be working alone at end

if ($aloneStart > 0)
{
    $staffAloneStart = $staff[0]; //must be the first who worked alone
    echo "minutes alone at start: " . $aloneStart . " and it was " . $staffAloneStart . "\n";
}

if ($aloneEnd > 0)
{
    $staffAloneEnd = $staff[count($end) - 1]; // must be the last to end that worked alone
    echo "minutes alone at end: " . $aloneEnd . " and it was " . $staffAloneEnd . "\n";
}

$aloneTime = intval($aloneStart) + intval($aloneEnd);
echo "total time alone " . $aloneTime;

使用以下数组,您会看到第一个用户的分钟数需要超过 5 分钟,因为他在晚上更多地独自工作。

$staff = array(1, 2, 3, 4, 5);
$start = array("11:05", "11:10", "19:00", "13:00", "19:00");
$end = array("20:00", "17:00", "03:00", "16:00", "03:00");

【问题讨论】:

  • @mickmackusa 添加日期不是问题,是我很困惑的计算。由于我们必须跟踪所有员工,这些员工可能在轮班期间单独工作,并计算他/她单独工作的分钟数。..
  • @mickmackusa 我理解他的两个问题的方式,输出应该是告诉他谁已经单独和多久了。你如何呈现它可能并不那么重要,因为他似乎无论如何都能将你的输出更改为他想要的。我可能错了,但这就是我对 Basit 的理解。他也有很多关于 PHP 的问题和答案,所以我认为他可以处理你给他的任何输出。
  • 热切期待……

标签: php datetime time schedule calculation


【解决方案1】:

我正在完全重写我的答案,以便它清晰并以正确的顺序流动。我对之前的方法做了一些小的改进,但没有什么大不了的。

首先是数据准备代码。我将 OP 的 hh:mm 时间进出值转换为简单的分钟值,同时将员工 ID 作为键。

// My test data in OP's format to start with:
$staff=[1,2,3];
$start=['11:00','13:00','17:00'];
$end=['21:00','15:00','19:00'];

// My data preparation method:
foreach($staff as $i=>$v){
    $on=explode(':',$start[$i]);  // separate hh from mm of start of shift
    $on_minutes=$on[0]*60+$on[1];  // calculate total minutes from start of day
    $off=explode(':',$end[$i]);   // separate hh from mm of end of shift
    $off_minutes=($off[0]+($on[0]>$off[0]?24:0))*60+$off[1];  // calculate minutes from start of day, factoring shift that run past midnight
    $shifts[$v]=[$on_minutes,$off_minutes];  // store prepared data for future processes
}
/*
  (new prepared array):
  $shifts=[
    1=>[660,1260],
    2=>[780,900],
    3=>[1020,1140]
  ];
*/

这是sn-p的数据处理。我已经建立了一条捷径——如果一名员工与另一名员工共享相同的班次,那么第一名员工将立即被视为零分钟单独(显然)。否则,将一个员工的班次与其他员工的班次一一进行比较,以确定他们单独的时间。

function whittle($colleague_shifts,$pieces_of_shift){  // initially, PoS is only one element
    foreach($colleague_shifts as $k=>$cs){
        foreach($pieces_of_shift as $i=>$ps){
            if($cs[0]<=$ps[0] && $cs[1]>=$ps[1]){
                unset($pieces_of_shift[$i]);
                continue;  // fully covered by coworker
            }
            $temp=[];
            if($ps[0]<$cs[0] && $cs[0]<$ps[1]){
                $temp[]=[$ps[0],$cs[0]];    // push new unmatched start into temp PoS array
            }
            if($ps[1]>$cs[1] && $cs[1]>$ps[0]){
                $temp[]=[$cs[1],$ps[1]];    // push new unmatched end into temp PoS array
            }
            if($temp){
                array_splice($pieces_of_shift,$i,1,$temp);  // replace the current PoS with 1 or 2 new PoS subarrays
            }
        }
        if(!$pieces_of_shift){
            return 0;  // no minutes alone
        }
    }
    // subtract all end alone minutes from all start alone minutes
    return array_sum(array_column($pieces_of_shift,1))-array_sum(array_column($pieces_of_shift,0));
}

foreach($shifts as $id=>$s){
    $colleague_shifts=array_diff_key($shifts,[$id=>'']);  // generate array excluding target worker's shift
    if(in_array($s,$colleague_shifts)){  // check for same start and end times elsewhere
        $alone[$id]=0;  // exact duplicate allows shortcut as "never alone"
    }else{
        $alone[$id]=whittle($colleague_shifts,[$s]);  // whittle down times where target employee is alone
    }
}
var_export($alone);

输出:

array (
  1 => 360,  // alone from 11am-1pm, 3pm-5pm, and 7pm-9pm
  2 => 0,   // never alone
  3 => 0,   // never alone
)

帮助您了解whittle() 内部发生的事情

  • 1 号员工从660 开始全面转变为1260。 ($pieces_of_shift 是一个只有一个子数组的数组,其中包含两个元素 - 开始分钟和结束分钟)
    $pieces_of_shift=[[660,1260]];
  • 在与 Staff #2 进行比较后,原来的 $pieces_of_shift 子数组被两个新的子数组替换——轮班开始时的独处时间和轮班结束时的独处时间:6607809001260.
    $pieces_of_shift=[[660,780],[900,1260]];
  • 然后将员工#3 的轮班与员工#1 的两个剩余的单独时间范围进行比较。员工 #3 的班次不与第一个子数组的任何部分重叠,但在第二个子数组中重叠。这意味着随后会替换第二个时间范围,以有效地“消除”班次时间的重叠。
    $pieces_of_shift=[[660,780],[900,1020],[1140,1260]];
  • 这导致员工 #1 的轮班有 3 个“单独”时间段:660780900102011401260。这 3 个单独的时间范围(每个 2 小时)产生 6 小时的独奏时间或 360 分钟。

这里是a demo with additional comments


如果在特定批次中存在高概率或大量重复移位,则可以通过在第一个 foreach() 循环之前写入 $colleague_shifts=array_map('unserialize', array_unique(array_map('serialize', $shifts))) 来减少 whittle() 内的总迭代次数。

就此而言,同样的多功能方法可以用于在调用foreach($shifts...) 之前缩短几个重复的班次,但我选择不实施这种方法,因为它可能不是值得卷积。

【讨论】:

  • 我不确定我是否理解您的“时代”。如果我要将它们转换回“正常”,这是否正确? 3v4l.org/f0s9u 只是为了确保我的第一行代码是正确的。 27 = 第二天 03:00。
  • 它已经从 OP 变得非常安静。我也只是想知道我们的答案是否有问题。他们两个似乎都有效。而且我认为性能差异不大。据我所知,这只是一个输出问题
  • @Andreas 抱歉,伙计们,过去 2 天都在旅行。今天要测试两者并实现一个可以轻松分解为小功能或其他东西的功能。也必须为此进行 phpunit 测试.但无论如何......我真的很感激帮助。
  • @mickmackusa 我收到错误 Warning: Illegal offset type in test.php on line 24 并带有以下数组初始值 gist.github.com/iBasit/b18457f6bed6145827292fd02eb57512
  • 很难接受两位伟大贡献者的两个答案。我要感谢你们俩。 @mickmackusa,感谢您一步一步的解释。我将完成所有这些并尝试不同的值并解决任何问题。
【解决方案2】:

知道了!

花了一些时间,但我找到了解决方案。
设法找到了 mickmacks 测试用例的解决方案。
这是一个 10 人的案例,它似乎也适用。

<?php
$staff = array(1,2,3,4,5,6,7,8,9,10);
$start = array("11:00", "13:00", "17:00", "17:00", "11:00", "13:30", "16:50", "18:30","17:00", "11:00");
$end = array("21:00", "15:00", "19:00", "19:30", "11:30", "15:10", "18:45", "19:45", "19:00", "11:30");

// Add staff number to end of time ex 11:00 => 11:00#2
For($i=0; $i<count($start);$i++){
    $start[$i] .= "#" . $staff[$i];
    $end[$i] .= "#" . $staff[$i];

}
$t = array_merge($start,$end); // create one long array with all in and out times
sort($t);
//var_dump($t);
// Multisport is needed to get all arrays in time order as reference
array_multisort($start, $end, $staff);

// Find first start time (11:00) and slice array thwre, build string
$test = implode(PHP_EOL,array_slice($t, array_search($start[0], $t)));

// Find the times before first start (night end times) and add them last in string
$test .= PHP_EOL . implode(PHP_EOL,array_slice($t, 0,array_search($start[0], $t)));
$times = explode(PHP_EOL, $test); // explode to make it array again
 // Var_dump($times);

$WhoIsInDaHouse = array("dummy"); // add a dummy variable since 0=false in later if
$j=0;
for($i=0; $i<count($times);$i++){
    //echo $times[$i] ." " . $i ."\n";
    if($times[$i]){
        $TimePerson = explode("#", $times[$i]);
        $Time = $TimePerson[0];
        $person = $TimePerson[1];


        $inout = array_search($person, $WhoIsInDaHouse); //is person in house and about to leave?
        If($inout != false){ //if person enter work false, if true: key of person leaving in $WhoIsInDaHouse
            //Here $person is leaving work
            Unset($WhoIsInDaHouse[$inout]);

            If(count($WhoIsInDaHouse) == 2){ // someone will now be alone since we have a dummy
                $Alone[$j]["start"] = $Time;
                $Alone[$j]["who"] = array_slice($WhoIsInDaHouse, -1)[0];
            }elseif(count($WhoIsInDaHouse) == 1 && $prevcount == 2){
                // Only dummy left
                $Alone[$j]["end"] = $Time;
                $Alone[$j]["duration"] = strtotime($Alone[$j]["end"])-strtotime($Alone[$j]["start"]);
                $j++;
            }
        }Else{
            // Here person enters work
            $WhoIsInDaHouse[] = $person;

            If(count($WhoIsInDaHouse) == 2){ // someone is entering alone
                $Alone[$j]["start"] = $Time;
                $Alone[$j]["who"] = $person;
            }elseif(count($WhoIsInDaHouse)>2 && $prevcount == 2){ // not alone anymore
                $Alone[$j]["end"] = $Time;
                $Alone[$j]["duration"] = strtotime($Alone[$j]["end"])-strtotime($Alone[$j]["start"]);
                $j++;
            }
        }
        $prevcount = count($WhoIsInDaHouse);
    }
}
foreach($Alone as $key => &$loner){
    if($loner["duration"]==0) unset($Alone[$key]);
}
Var_dump($Alone);

看美女跑https://3v4l.org/bT2bZ

我花了很长时间才弄清楚我需要一个假人。谁知道假人会有用?

【讨论】:

  • 而数组中有一个额外项的原因是因为我有两个人同时开始。所以前一个是单独的,但由于下一个同时开始,持续时间为零。
  • 不知何故,这个简单的事情变得很难做,哈哈,甚至我自己仍然坚持寻找一个简单的解决方案。
  • @mickmackusa 哇...这很奇怪。似乎 for 循环多次运行。如果我回显 $times[$i] 它会回显一个空的。如果我提前停止循环一次,它适用于您的示例,但不适用于五个人。所以奇怪的事情发生了。我刚刚添加了一个 if($times[$i]) ,这似乎解决了这个问题。感谢您指出! :-)
猜你喜欢
  • 1970-01-01
  • 2017-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-05
  • 1970-01-01
  • 2018-02-09
  • 1970-01-01
相关资源
最近更新 更多