我有一张带约会的桌子:
$barber | $open | $close | $appointment_id | $start | $finish |
1 |10:00:00 |17:00:00 | 1 | 11:30:00 | 12:30:00 |
1 |10:00:00 |17:00:00 | 2 | 14:30:00 | 15:30:00 |
2 |8:00:00 |16:00:00 | 3 | 12:00:00 | 13:00:00 |
2 |8:00:00 |16:00:00 | 4 | 15:00:00 | 16:00:00 |
打开和关闭的位置-工作时间,开始和结束时间-预约时间。假设在理发店有2名工人。我想将时间表中的重叠定义为“已预订”,否则定义为“空”。
我想拥有这种数组:
'open' => '08:00:00' //defined by min in $open column
'close' => '17:00:00' //defined by max in $close column
'schedule' => Array (
[08.00] => 'empty'
[09.00] => 'empty'
[10.00] => 'empty'
[11.00] => 'empty'
[12.00] => 'booked' //as appointments 1 and 3 overlap
[13.00] => 'empty'
[14.00] => 'empty'
[15.00] => 'booked' //as appointments 2 and 4 overlap
[16.00] => 'empty'
)
我的问题-php循环,该循环从给定的数据表构建所需的数组。
我正在尝试做的简化版本(只是为了显示算法):
for ($t = date('H',$open); $t < date('H',$close); $t++) {
if (date('H',$start) < $t + 1 AND date('H',$finish) > $t AND $schedule_arr['schedule'][number_format($t,2)] != 'empty') {
$schedule_arr['schedule'][number_format($t,2)] = 'booked';}
else {$schedule_arr['schedule'][number_format($t,2)] = 'empty';}
}
大多数时候,最后我都有带有“空”插槽的阵列。
答案 0 :(得分:1)
这就是我可能的方法。首先要注意的是,您可能应该使用strtotime()而不是date()。 考虑以下内容,对我来说,我得到的第二条消息显示第一个日期不少于第二个。
if(date("9:00:00") < date("10:00:00")) {
echo "9:00:00 < 10:00:00 <br/>";
} else {
echo "9:00:00 >= 10:00:00 ? <br/>";
}
您说您正在使用MySQL。本示例使用SQLite,因为它是一个完全正常的示例。当然,您将必须更改连接数据库的方式以及运行的查询。
最重要的是,请考虑如何检查重叠。作为人类,我们认为从12:00到13:00的约会与从13:00到14:00的约会不重叠。但是时间“ 13:00”出现在两个范围中,因此您可以考虑从约会结束时间中减去一秒,或者调整用于确定重叠的功能。
无论如何,以下内容是否可以帮助您提出任何想法。这似乎对我有用。
<?php
class MyDB extends SQLite3 {
function __construct() {
$this->open('test.db');
}
}
$db = new MyDB();
$results = $db->query(
'SELECT barber, open, close, appointment_id, start, finish ' .
'FROM appointments' );
//
// Create structure with some defaults populated.
//
$output = array(
'open' => strtotime('23:59:59'),
'close' => strtotime('0:00:00'),
'schedule' => array(
strtotime('8:00:00') => 'empty',
strtotime('9:00:00') => 'empty',
strtotime('10:00:00') => 'empty',
strtotime('11:00:00') => 'empty',
strtotime('12:00:00') => 'empty',
strtotime('13:00:00') => 'empty',
strtotime('14:00:00') => 'empty',
strtotime('15:00:00') => 'empty',
strtotime('16:00:00') => 'empty'
) );
$barbers = array();
echo 'INPUT' . '<br/>';
while ($row = $results->fetchArray()) {
//
// Display input
//
echo $row[0] . ' ' .
$row[1] . ' ' .
$row[2] . ' ' .
$row[3] . ' ' .
$row[4] . ' ' .
$row[5] . ' ' .
' <br/>';
$barber = $row[0];
$open = strtotime($row[1]);
$close = strtotime($row[2]);
$start = strtotime($row[4]);
$finish = strtotime($row[5]);
//
// Handle setting open and close times.
//
if($open < $output['open']) {
$output['open'] = $open;
}
if($close > $output['close']) {
$output['close'] = $close;
}
//
// Want to also ensure close > open???
//
if(!array_key_exists($barber, $barbers)) {
$barbers[$barber] = array();
}
$range = array( 'start' => $start,
'finish' => $finish );
//
// For this range, check all existing ranges we have added so far.
//
checkOverlap($barbers, $barber, $range, $output);
array_push($barbers[$barber], $range);
}
echo '<br/><br/><br/>';
//
// Display results
//
$open = date("H:i:s", $output['open']);
$close = date("H:i:s", $output['close']);
echo 'OUTPUT' . '<br/>';
echo 'open: ' . $open . '<br/>';
echo 'close: ' . $close . '<br/>';
echo 'schedule: ' . '<br/>';
foreach ($output['schedule'] as $key => $val) {
$time = date("H:i:s", $key);
echo ' ';
echo $time . ' => ' . $val . '<br/>';
}
//
// Helper function
//
function checkOverlap($barbers, $current_barber, $range, & $output) {
$start = $range['start'];
$finish = $range['finish'];
foreach($barbers as $barber => $appointments) {
if($barber == $current_barber) {
continue; // Do not consider conflicts with self???
}
for($i = 0; $i < count($appointments); $i++) {
$appointment = $appointments[$i];
$aStart = $appointment['start'];
$aFinish = $appointment['finish'];
if($aStart < $finish && $aFinish > $start) {
//
// Which one starts later?
//
$conflict_time = $start;
if($aStart > $start) {
$conflict_time = $aStart;
}
//
// Now find which hour to mark as conflict
//
foreach($output['schedule'] as $hour => $status) {
if( $conflict_time >= $hour &&
$conflict_time < ($hour + ((60 * 60) - 1)) ) {
$output['schedule'][$hour] = 'booked';
}
}
}
}
}
}
?>
我使用以下命令填充sqlite3数据库:
DROP TABLE IF EXISTS appointments;
CREATE TABLE appointments (
barber integer,
open text,
close text,
appointment_id integer,
start text,
finish text
);
INSERT INTO appointments VALUES (
'1','10:00:00','17:00:00', '1','11:30:00','12:30:00' );
INSERT INTO appointments VALUES (
'1','10:00:00','17:00:00', '2','14:30:00','15:30:00' );
INSERT INTO appointments VALUES (
'2','8:00:00', '16:00:00', '3','12:00:00','13:00:00' );
INSERT INTO appointments VALUES (
'2','8:00:00', '16:00:00', '4','15:00:00','16:00:00' );
这是通过linux命令行运行的:
sqlite3 test.db <appointments.sql
编辑checkOverlap()的进一步说明
首先,这将创建一个称为$ barbers的结构。 这将把一个理发师ID用作键,并且对于每个理发师ID,它将包含一个范围数组。当我们遍历查询中的表行时,将添加这些范围。 所以最终看起来像这样:
$barbers => {
1 => [
{ start: 1:00:00, finish: 1:30:00 },
{ start: 13:00:00, finish: 14:00:00 },
{ start: 15:00:00, finish: 16:45:00 } ],
2 => [
{ start: 3:00:00, finish: 4:30:00 } ],
3 => [ ... ],
4 => [ ... ]
...
}
但是在将范围插入此结构之前,先调用checkOverlap()传递结构本身和要添加的范围。 checkOverlap()遍历该结构,对于每个理发师ID,它都会获取该理发师的现有约会列表,即范围。 然后,它检查现有约会范围 $ aStart 和 $ aFinish 是否与我们要添加 $ start 和的新约会范围重叠。 $ finish 。
如果存在重叠,我们将选择较晚的开始时间(因为这将是实际冲突开始的时间),然后在 $ output ['schedule'] 中的键中进行搜索该时间属于一天中的哪个小时,并将该键的值设置为“已预订”值。请注意, $ output 对象是使用&通过引用传递的,因此对该对象的更改将反映在函数外部。即呼叫者的对象将被更新。
还要注意,在上述结构的说明中,我使用的是人类容易理解的时间,例如1:00:00,但是实际结构将具有UTC时间,因为这是使用strtotime()。