计算两个有暂停的日期之间的业务持续时间

问题描述 投票:0回答:1

我必须在工作时间内(周一至周五,上午 09:00 至下午 5:00)确定两个日期之间的持续时间,公共假期除外。 此外,我需要考虑业务间隔内的暂停。 我已经使用两个库成功处理了程序的初始部分:https://github.com/briannesbitt/Carbonhttps://github.com/spatie/opening-hours现在我需要考虑考虑暂停时间。

下面是我的代码:

<?php
require 'opening-hours-master/vendor/autoload.php';
require 'carbon-master/vendor/autoload.php';

use Spatie\OpeningHours\OpeningHours;
use Carbon\CarbonInterval;

//Initiate the working hours and exlude public hollidays
$openingHours = OpeningHours::create([
    'monday'     => ['09:00-17:00'],
    'tuesday'    => ['09:00-17:00'],
    'wednesday'  => ['09:00-17:00'],
    'thursday'   => ['09:00-17:00'],
    'friday'     => ['09:00-17:00'],
    'saturday'   => [],
    'sunday'     => [],
    'exceptions' => [
        '2023-04-10' => [],
        '2023-05-08' => [],
        '2023-05-18' => [],
        '2023-05-29' => [],
        '2023-08-15' => [],
        '2023-11-01' => [],
        '2023-11-11' => [],
        '07-14'      => [],                // Recurring on each 14 of July
        '05-01'      => [],                // Recurring on each 1st of May
        '01-01'      => [],                // Recurring on each 1st of January
        '12-25'      => []                 // Recurring on each 25th of December
    ],
    'timezone' => [
        'input' => 'Europe/Paris',
        'output' => 'Europe/Paris',
    ]
]);

//Date to calculate interval
$data = '[
    {"entity_id":1,"start_phase":"2023-12-04T10:00:00.973+01:00","end_phase":"2023-12-05T12:01:01.973+01:00"},
    {"entity_id":2,"start_phase":"2023-11-30T08:00:00.973+01:00","end_phase":"2023-12-07T16:31:01.973+01:00"},
    {"entity_id":2,"start_phase":"2023-12-12T14:35:51.973+01:00","end_phase":"2023-12-12T16:31:01.973+01:00"}
]';

//Pause per entity_id
$data_pause = '[
    {"entity_id":1,"start_pause":"2023-12-04T09:00:00.973+01:00","end_pause":"2023-12-04T12:00:00.973+01:00"},
    {"entity_id":1,"start_pause":"2023-12-05T12:00:00.973+01:00","end_pause":"2023-12-05T14:00:00.973+01:00"}
]';

$report_data = json_decode($data, true);
$data_pause = json_decode($data_pause, true);
$result = array();

//Calculate the durations in business time between two dates
foreach($report_data as $row){
    foreach($data_pause as $pause){
        if($row['entity_id'] == $pause['entity_id']){
            //TO DO
            //Remove pause duration inside the specified start and end phases, ensuring it falls within the designated business time interval.
            var_dump($pause);
        }
    }
    $diffInSeconds = round($openingHours->diffInOpenSeconds(new DateTime($row['start_phase']), new DateTime($row['end_phase'])));   
    $human_readable = CarbonInterval::seconds($diffInSeconds)->cascade()->forHumans();
    $data_diff[] = array(
        'entity_id' => $row['entity_id'], 
        'start_phase' => $row['start_phase'],
        'end_phase' => $row['end_phase'],
        'difference_in_seconds' => $diffInSeconds,
        'human_readable' => $human_readable
    );
}
var_dump($data_diff);

输出:

array(3) {
  [0]=>
  array(5) {
    ["entity_id"]=>
    int(1)
    ["start_phase"]=>
    string(29) "2023-12-04T10:00:00.973+01:00"
    ["end_phase"]=>
    string(29) "2023-12-05T12:01:01.973+01:00"
    ["difference_in_seconds"]=>
    float(36061)
    ["human_readable"]=>
    string(26) "10 hours 1 minute 1 second"
  }
  [1]=>
  array(5) {
    ["entity_id"]=>
    int(2)
    ["start_phase"]=>
    string(29) "2023-11-30T08:00:00.973+01:00"
    ["end_phase"]=>
    string(29) "2023-12-07T16:31:01.973+01:00"
    ["difference_in_seconds"]=>
    float(171062)
    ["human_readable"]=>
    string(35) "1 day 23 hours 31 minutes 2 seconds"
  }
  [2]=>
  array(5) {
    ["entity_id"]=>
    int(2)
    ["start_phase"]=>
    string(29) "2023-12-12T14:35:51.973+01:00"
    ["end_phase"]=>
    string(29) "2023-12-12T16:31:01.973+01:00"
    ["difference_in_seconds"]=>
    float(6910)
    ["human_readable"]=>
    string(28) "1 hour 55 minutes 10 seconds"
  }
}

如何排除阶段间隔内和营业时间内的暂停时长? 我可以使用

$openingHours->isOpenAt($date);
来确定日期是否在工作时间内,但如何继续处理剩余的逻辑?

例如,对于entity_id 1,我有以下间隔:

  • 开始阶段:2023-12-04 10:00:00
  • 结束阶段:2023-12-05 12:01:01
  • 持续时间 => 10 小时 1 分 1 秒

暂停是:

第一次暂停:

  • 开始暂停 : 2023-12-04 09:00:00
  • 结束暂停 : 2023-12-04 12:00:00

第二次暂停:

  • 开始暂停 : 2023-12-05 12:00:00
  • 结束暂停 : 2023-12-05 14:00:00

在这种情况下,初始暂停需要排除10:00到12:00的时间,总共2小时。至于第二次暂停,我们应该扣除从12:00到12:01:01的持续时间,相当于1分1秒。因此,累计结果应为 8 小时。

或者简化一下:我必须计算开始阶段和结束阶段之间的持续时间(由绿线表示)。持续时间必须在指定的工作时间内(红线所示),并且暂停时间(蓝线所示)应排除在总体计算之外。

编辑:

用户可以在一天中进行多次暂停,并且暂停的启动由用户完成。暂停可以随时发生,并且可以在一天中多次进行。暂停时间可能随时出现,包括周末和公共假期。

php php-carbon
1个回答
0
投票

使用

cmixin/business-time
中的\BusinessTime\Schedule,您可以减去暂停时间的差异与营业时间的差异(不考虑毫秒,这是第二精确的):

$tz = 'Europe/Paris';
$schedule = \BusinessTime\Schedule::create([
    'monday'     => ['09:00-17:00'],
    'tuesday'    => ['09:00-17:00'],
    'wednesday'  => ['09:00-17:00'],
    'thursday'   => ['09:00-17:00'],
    'friday'     => ['09:00-17:00'],
    'saturday'   => [],
    'sunday'     => [],
    'exceptions' => [
        '2023-04-10' => [],
        '2023-05-08' => [],
        '2023-05-18' => [],
        '2023-05-29' => [],
        '2023-08-15' => [],
        '2023-11-01' => [],
        '2023-11-11' => [],
        '07-14'      => [],                // Recurring on each 14 of July
        '05-01'      => [],                // Recurring on each 1st of May
        '01-01'      => [],                // Recurring on each 1st of January
        '12-25'      => []                 // Recurring on each 25th of December
    ],
    'timezone' => [
        'input' => $tz,
        'output' => $tz,
    ]
]);

$pauses = json_decode('[
    {"entity_id":1,"start_pause":"2023-12-04T09:00:00.973+01:00","end_pause":"2023-12-04T12:00:00.973+01:00"},
    {"entity_id":1,"start_pause":"2023-12-05T12:00:00.973+01:00","end_pause":"2023-12-05T14:00:00.973+01:00"}
]', true);

$exceptions = [];

foreach ($pauses as $pause) {
    $start = \Carbon\CarbonImmutable::parse($pause['start_pause'])->tz($tz);
    $end = \Carbon\CarbonImmutable::parse($pause['end_pause'])->tz($tz);
    $days = $start->startOfDay()->daysUntil($end->startOfDay())->toArray();
    $lastIndex = count($days) - 1;

    foreach ($start->startOfDay()->daysUntil($end->startOfDay()) as $index => $date) {
        $isFirstDay = ($index === 0);
        $isLastDay = ($index === $lastIndex);
        $day = $date->format('Y-m-d');
        $exceptions[$day] ??= [];
        $exceptions[$day][] = ($isFirstDay ? $start->format('H:i') : '00:00') . '-' .
            ($isLastDay ? $end->format('H:i') : '24:00');
    }
}

$pauses = \BusinessTime\Schedule::create([
    'exceptions' => $exceptions,
]);

$start = \Carbon\CarbonImmutable::parse('2023-12-04 10:00:00', $tz);
$end = \Carbon\CarbonImmutable::parse('2023-12-05 12:01:01', $tz);

$seconds = $schedule->diffInBusinessSeconds($start, $end) - $pauses->diffInBusinessSeconds($start, $end);

$diff = \Carbon\CarbonInterval::seconds($seconds)->cascade();

echo $diff->forHumans();
© www.soinside.com 2019 - 2024. All rights reserved.