将时间范围切成部分

bep*_*ter 20 php time slice

第一个问题.要温柔.

我正在研究跟踪技术人员完成任务所花费时间的软件.需要增强软件以基于星期几和一天中的时间识别不同的可计费率乘数.(例如,"工作日下午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
            )
        ...
    )
Run Code Online (Sandbox Code Playgroud)

用简单的英语表示,从午夜到早上8点,从下午8点到下午5点,正常时间从5点到晚上11:59,时间为半年.这些中断发生的时间可以是第二次的任意时间,并且每天可以有任意数量的它们.(这种格式完全可以协商,但我的目标是让它尽可能易于阅读.)

例如:星期一从15:00:00(下午3点)到21:00:00(晚上9点)记录的时间条目包括2小时计费1x和4小时计费1.5x.单个时间条目也可以跨越多个中断.使用上面的示例rateTable,从上午6点到下午9点的时间条目将具有3个子范围,从6-8 AM @ 1.5x,8 AM-5PM @ 1x,以及5-9 PM @ 1.5x.相比之下,时间输入也可能只是从08:15:00到08:30:00,并且完全包含在单个乘数的范围内.

我真的可以使用一些帮助编写一些PHP(或至少设计一个算法),这可能需要一周中的一天,一个开始时间和一个停止时间,并解析到所需的子部分.将输出作为一个由(start,stop,multiplier)三元组的多个条目组成的数组是理想的.对于上面的示例,输出将是:

[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
            )
    )
Run Code Online (Sandbox Code Playgroud)

我只是简单地无法理解将单个(开始,停止)分成(可能)多个子部分的逻辑.

bep*_*ter 0

Eineki 破解了该算法。我的尝试中缺少的部分是每个乘数范围内都有可用的开始时间停止时间。我重视原始速率表中的数据密度,因此我使用了 Eineki 的 Convert() 例程的核心来获取存储在配置中的表并添加停止时间。我的代码已经自动创建(或填充)了最小速率表,保证其余代码不会阻塞或抛出警告/错误,所以我将其包括在内。我还将 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'));

?>
Run Code Online (Sandbox Code Playgroud)

谢谢大家,特别是Eineki。