第一个问题。请温柔些。
我正在开发跟踪技术人员在任务上花费的时间的软件。该软件需要加强,以识别不同的收费率乘数根据一周的一天和时间的一天。(例如,“工作日下午5点半之后的时间”)
使用该软件的技术人员只需要记录日期、开始时间和停止时间(以小时和分钟为单位)。该软件预计将打破时间进入部分边界时,速率乘数变化。单次输入不允许跨越多天。
下面是税率表的部分示例。很明显,第一级数组键是一周中的几天。二级数组键表示一天中新的乘法器启动时的时间,并一直运行到数组中的下一个顺序条目。数组值是该时间范围的乘数。
[rateTable] => Array
(
[Monday] => Array
(
[00:00:00] => 1.5
[08:00:00] => 1
[17:00:00] => 1.5
[23:59:59] => 1
)
[Tuesday] => Array
(
[00:00:00] => 1.5
[08:00:00] => 1
[17:00:00] => 1.5
[23:59:59] => 1
)
...
)
在通俗易懂的英语中,这是指从午夜到上午8点半的时间,从晚上8点到下午5点半,从下午5点到11点59分的时间半。这些中断发生的时间对第二天来说可能是任意的,而且每天都有任意数量的中断。(这种格式完全是可协商的,但我的目标是使它尽可能容易被人类阅读。)
例如:周一15:00(下午3点)至21:00 (9 PM)记录的时间条目将包括2小时的1倍计费和4小时1.5倍的计费。一个时间条目也有可能跨越多个中断。使用上面的示例rateTable,从上午6点到下午9点的时间条目将有3个子范围,分别是6-8 AM @1.5 x、8-5 PM @ 1x和5-9 PM @1.5 x。相反,时间条目可能只在08:15:00到08:30:00之间,并且完全包含在一个乘法器的范围内。
我真的需要一些帮助来编写一些PHP代码(或者至少设计一个算法),它可以花费一天的时间、一个开始时间和一个停止时间,然后解析成所需的子部分。理想的做法是,输出是一个数组,其中包含一个(开始、停止、乘法器)三重奏的多个条目。对于上面的例子,输出将是:
[output] => Array
(
[0] => Array
(
[start] => 15:00:00
[stop] => 17:00:00
[multiplier] => 1
)
[1] => Array
(
[start] => 17:00:00
[stop] => 21:00:00
[multiplier] => 1.5
)
)
我很清楚,我无法理解将单个(开始、停止)划分为(潜在)多个子部分的逻辑。
发布于 2010-05-11 20:04:59
伊内基破解了算法。在我的尝试中缺少的部分是让start、和在每个乘子范围内可用的停止时间。我对原始rateTable中的数据密度进行了评估,因此我使用Eineki的转化器()例程来接收存储在config中的表并在其中添加停止时间。我的代码已经自动创建(或填写)了一个最小速率表,确保其余代码不会阻塞或抛出警告/错误,因此我将其包括在内。我还将bill()和map_shift()合并在一起,因为在我看来,这两者没有任何有用的目的。
<?php
//-----------------------------------------------------------------------
function CompactSliceData($start, $stop, $multiplier)
// Used by the VerifyRateTable() to change the format of the multiplier table.
{
return compact('start', 'stop','multiplier');
}
//-----------------------------------------------------------------------
function VerifyAndConvertRateTable($configRateTable)
// The rate table must contain keyed elements for all 7 days of the week.
// Each subarray must contain at LEAST a single entry for '00:00:00' =>
// 1 and '23:59:59' => 1. If the first entry does not start at midnight,
// a new element will be added to the array to represent this. If given
// an empty array, this function will auto-vivicate a "default" rate
// table where all time is billed at 1.0x.
{
$weekDays = array('Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday',
'Sunday',); // Not very i18n friendly?
$newTable = array();
foreach($weekDays as $day)
{
if( !array_key_exists($day, $configRateTable)
|| !is_array($configRateTable[$day])
|| !array_key_exists('00:00:00', $configRateTable[$day]) )
{
$configRateTable[$day]['00:00:00'] = 1;
}
if( !array_key_exists($day, $configRateTable)
|| !is_array($configRateTable[$day])
|| !array_key_exists('23:59:59', $configRateTable[$day]) )
{
$configRateTable[$day]['23:59:59'] = 1;
}
// Convert the provided table format to something we can work with internally.
// Ref: http://stackoverflow.com/questions/2792048/slicing-a-time-range-into-parts
$newTable[$day] = array_slice(
array_map(
'CompactSliceData',
array_keys($configRateTable[$day]),
array_keys(array_slice($configRateTable[$day],1)),
$configRateTable[$day]),
0,-1);
}
return $newTable;
}
//-----------------------------------------------------------------------
function SliceTimeEntry($dayTable, $start, $stop)
// Iterate through a day's table of rate slices and split the $start/$stop
// into parts along the boundaries.
// Ref: http://stackoverflow.com/questions/2792048/slicing-a-time-range-into-parts
{
$report = array();
foreach($dayTable as $slice)
{
if ($start < $slice['stop'] && $stop > $slice['start'])
{
$report[] = array(
'start'=> max($start, $slice['start']),
'stop' => min($stop, $slice['stop']),
'multiplier' => $slice['multiplier']
);
}
}
return $report;
}
/* examples */
$rateTable = array(
'Monday' => array('00:00:00' => 1.5, '08:00:00' => 1, '17:00:00' => 1.5),
'Tuesday' => array('00:00:00' => 1.5, '08:00:00' => 1, '17:00:00' => 1.5),
'Wednesday' => array('00:00:00' => 1.5, '08:00:00' => 1, '17:00:00' => 1.5),
'Thursday' => array('00:00:00' => 1.5, '08:00:00' => 1, '17:00:00' => 1.5),
'Friday' => array('00:00:00' => 1.5, '08:00:00' => 1, '17:00:00' => 1.5),
'Saturday' => array('00:00:00' => 1.5, '15:00:00' => 2),
'Sunday' => array('00:00:00' => 1.5, '15:00:00' => 2),
);
$rateTable = VerifyAndConvertRateTable($rateTable);
print_r(SliceTimeEntry($rateTable['Monday'],'08:05:00','18:05:00'));
print_r(SliceTimeEntry($rateTable['Monday'],'08:05:00','12:00:00'));
print_r(SliceTimeEntry($rateTable['Tuesday'],'07:15:00','19:30:00'));
print_r(SliceTimeEntry($rateTable['Tuesday'],'07:15:00','17:00:00'));
?>
谢谢大家,尤其是伊内基。
发布于 2010-05-08 01:58:42
我将使用一种不同的方法,并将根据几个考虑因素更改rateTable表示。
definition);
最后但并非最不重要的一点是,我个人的经验是,如果你不能全神贯注于一种算法,那么你的同事可能会遇到同样的困难(即使你成功并解决了问题),而代码将是bug的主要来源。如果你找到一个简单有效的解决方案,那将是一次时间、金钱和头痛的收获。即使解决方案效率不高,它也可能是一种收获。
$rateTable = array(
'Monday' => array (
array('start'=>'00:00:00','stop'=>'07:59:59','multiplier'=>1.5),
array('start'=>'08:00:00','stop'=>'16:59:59','multiplier'=>1),
array('start'=>'17:00:00','stop'=>'23:59:59','multiplier'=>1.5)
),
'Tuesday'=> array (
array('start'=>'00:00:00','stop'=>'08:00:00','multiplier'=>1.5),
array('start'=>'08:00:00','stop'=>'17:00:00','multiplier'=>1),
array('start'=>'17:00:00','stop'=>'23:59:59','multiplier'=>1.5)
)
);
function map_shift($shift, $startTime, $stopTime)
{
if ($startTime >= $shift['stop'] or $stopTime <= $shift['start']) {
return;
}
return array(
'start'=> max($startTime, $shift['start']),
'stop' => min($stopTime, $shift['stop']),
'multiplier' => $shift['multiplier']
);
}
function bill($day, $start, $stop)
{
$report = array();
foreach($day as $slice) {
$result = map_shift($slice, $start, $stop);
if ($result) {
array_push($report,$result);
}
}
return $report;
}
/* examples */
var_dump(bill($rateTable['Monday'],'08:05:00','18:05:00'));
var_dump(bill($rateTable['Monday'],'08:05:00','12:00:00'));
var_dump(bill($rateTable['Tuesday'],'07:15:00','19:30:00'));
var_dump(bill($rateTable['Tuesday'],'07:15:00','17:00:00'));
至少,您需要一个函数来将原始格式转换为新格式。
$oldMonday = array (
'00:00:00'=>1.5,
'08:00:00'=>1,
'17:00:00'=>1.5,
'23:59:59'=>1
);
function convert($array)
{
return array_slice(
array_map(
function($start,$stop, $multiplier)
{
return compact('start', 'stop','multiplier');
},
array_keys($array),
array_keys(array_slice($array,1)),
$array),
0,
-1);
}
var_dump(convert($oldMonday));
是的,你可以通过
bill(convert($oldRateTable['Tuesday']),'07:15:00','17:00:00');
但如果你在乎一些表演..。
发布于 2010-05-08 01:31:59
这是我的方法
我把每件事都转换成了秒,这样就容易多了。
这是以秒为索引的比率表。星期一只有三个时隙
// 0-28800 (12am-8am) = 1.5
// 28800-61200 (8am-5pm) = 1
// 61200-86399 (5pm-11:50pm) = 1.5
$rate_table = array(
'monday' => array (
'28800' => 1.5,
'61200' => 1,
'86399' => 1.5
)
);
它使用此函数将hh:mm:ss转换为秒。
function time2seconds( $time ){
list($h,$m,$s) = explode(':', $time);
return ((int)$h*3600)+((int)$m*60)+(int)$s;
}
这是返回一个比率表的函数。
function get_rates( $start, $end, $rate_table ) {
$day = strtolower( date( 'l', strtotime( $start ) ) );
// these should probably be pulled out and the function
// should accept integers and not time strings
$start_time = time2seconds( end( explode( 'T', $start ) ) );
$end_time = time2seconds( end( explode( 'T', $end ) ) );
$current_time = $start_time;
foreach( $rate_table[$day] as $seconds => $multiplier ) {
// loop until we get to the first slot
if ( $start_time < $seconds ) {
//$rate[ $seconds ] = ( $seconds < $end_time ? $seconds : $end_time ) - $current_time;
$rate[] = array (
'start' => $current_time,
'stop' => $seconds < $end_time ? $seconds : $end_time,
'duration' => ( $seconds < $end_time ? $seconds : $end_time ) - $current_time,
'multiplier' => $multiplier
);
$current_time=$seconds;
// quit the loop if the next time block is after clock out time
if ( $current_time > $end_time ) break;
}
}
return $rate;
}
这是你怎么用的
$start = '2010-05-03T07:00:00';
$end = '2010-05-03T21:00:00';
print_r( get_rates( $start, $end, $rate_table ) );
返回
Array
(
[0] => Array
(
[start] => 25200
[stop] => 28800
[duration] => 3600
[multiplier] => 1.5
)
[1] => Array
(
[start] => 28800
[stop] => 61200
[duration] => 32400
[multiplier] => 1
)
[2] => Array
(
[start] => 61200
[stop] => 75600
[duration] => 14400
[multiplier] => 1.5
)
)
基本上,代码在速率表上循环,并从给定的时隙中查找出属于每个速率的秒数。
https://stackoverflow.com/questions/2792048
复制相似问题