员工单独工作时间安排
Staff schedule working alone minutes
我有工作人员的时间清单。我需要查明是否有任何员工在单独工作,以及他们一天单独工作了多少分钟
| 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, following is the code that gets the first and last person who was working alone with alone minutes, but its not quite right. Because if there were 3 people with different times that worked alone, it will give a problem. 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");
知道了!
花了一些时间,但我找到了解决方案。
设法找到 mickmacks 测试用例的解决方案。
这是一个十人的案例,它似乎也支持。
<?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
我花了很长时间才弄清楚我需要一个假人。谁知道假人会有用?
我正在完全重写我的答案,以使其清晰并以正确的顺序流动。我对以前的方法做了一些小的改进,但没有太大的改进。
首先是数据准备代码。我将 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]
];
*/
这是数据处理片段。我建立了一条捷径——如果一名员工与另一名员工轮班相同,那么第一名员工将立即被视为单独工作时间为零(很明显)。否则,将一名员工的班次与其他员工的班次逐一比较,以确定他们独处的时间。
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]];
- 在与员工 #2 进行比较后,原来的
$pieces_of_shift
子阵列被两个新的子阵列所取代——轮班开始时的单独时间和轮班结束时的单独时间: 660
到 780
和 900
到 1260
。
$pieces_of_shift=[[660,780],[900,1260]];
- 然后将员工 #3 的班次与员工 #1 的两个剩余单独时间范围进行比较。 Staff #3 的班次不与第一个子阵列的任何部分重叠,但在第二个子阵列中重叠。这意味着第二个时间范围随后被替换以有效 "punch out" 轮班时间的重叠。
$pieces_of_shift=[[660,780],[900,1020],[1140,1260]];
- 这导致员工 #1 的班次有 3 个 "alone" 时间段:
660
到 780
、900
到 1020
和 1140
到 1260
。这 3 个单独时间范围(每个 2 小时)产生 6 小时的总单独工作或 360 分钟。
这里是a demo with additional comments.
如果在特定批次中存在高概率或大量重复移位,则可以通过在第一个 foreach()
循环之前写入 $colleague_shifts=array_map('unserialize', array_unique(array_map('serialize', $shifts)))
来减少 whittle()
内的总迭代次数.
就此而言,相同的多功能方法 可以 用于在调用 foreach($shifts...)
之前缩短几个重复的班次,但我选择不实施这种方法,因为它可能不值得卷积。
我有工作人员的时间清单。我需要查明是否有任何员工在单独工作,以及他们一天单独工作了多少分钟
| 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, following is the code that gets the first and last person who was working alone with alone minutes, but its not quite right. Because if there were 3 people with different times that worked alone, it will give a problem. 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");
知道了!
花了一些时间,但我找到了解决方案。
设法找到 mickmacks 测试用例的解决方案。
这是一个十人的案例,它似乎也支持。
<?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
我花了很长时间才弄清楚我需要一个假人。谁知道假人会有用?
我正在完全重写我的答案,以使其清晰并以正确的顺序流动。我对以前的方法做了一些小的改进,但没有太大的改进。
首先是数据准备代码。我将 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]
];
*/
这是数据处理片段。我建立了一条捷径——如果一名员工与另一名员工轮班相同,那么第一名员工将立即被视为单独工作时间为零(很明显)。否则,将一名员工的班次与其他员工的班次逐一比较,以确定他们独处的时间。
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]];
- 在与员工 #2 进行比较后,原来的
$pieces_of_shift
子阵列被两个新的子阵列所取代——轮班开始时的单独时间和轮班结束时的单独时间:660
到780
和900
到1260
。$pieces_of_shift=[[660,780],[900,1260]];
- 然后将员工 #3 的班次与员工 #1 的两个剩余单独时间范围进行比较。 Staff #3 的班次不与第一个子阵列的任何部分重叠,但在第二个子阵列中重叠。这意味着第二个时间范围随后被替换以有效 "punch out" 轮班时间的重叠。
$pieces_of_shift=[[660,780],[900,1020],[1140,1260]];
- 这导致员工 #1 的班次有 3 个 "alone" 时间段:
660
到780
、900
到1020
和1140
到1260
。这 3 个单独时间范围(每个 2 小时)产生 6 小时的总单独工作或 360 分钟。
这里是a demo with additional comments.
如果在特定批次中存在高概率或大量重复移位,则可以通过在第一个 foreach()
循环之前写入 $colleague_shifts=array_map('unserialize', array_unique(array_map('serialize', $shifts)))
来减少 whittle()
内的总迭代次数.
就此而言,相同的多功能方法 可以 用于在调用 foreach($shifts...)
之前缩短几个重复的班次,但我选择不实施这种方法,因为它可能不值得卷积。