skip to Main Content

I’m trying to determine the duration of a time interval in PHP, taking into account business hours (from 9 am to 5 pm) on Monday to Friday and excluding specific public holidays. Let’s consider the public holidays as 15th August 2023, 1st November 2023, 11th November 2023, and 25th December 2023.

For example, suppose I have an interval from 28th October 07:00:00 to 3rd November 10:00:00. How can I calculate the duration (in seconds) only during business hours, excluding weekends and the mentioned holidays?

I’m looking for guidance on an effective approach to achieve this duration calculation.

2

Answers


  1. Chosen as BEST ANSWER

    By using the spatie/opening-hours and briannesbitt/Carbon library, I successfully implemented the desired functionality. The script, utilizing a sample JSON file (sample.json) as input, calculates time differences while excluding public holidays and weekends. The focus is on determining the duration only during working hours.

    sample.json

    [
    {"entity_id":2478013,"end_phase":"2023-10-16T14:39:49.839+02:00","start_phase":"2023-01-02T10:40:01.982+01:00"}
    ]
    

    Below is the complete code:

    <?php
    require 'opening-hours-master/vendor/autoload.php';
    require 'Carbon-master/vendor/autoload.php';
    
    use SpatieOpeningHoursOpeningHours;
    use CarbonCarbonInterval;
    
    $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',
        ]
    ]);
    
    $result_csv = array(array('entity_id', 'start_phase', 'end_phase', 'difference_in_seconds', 'difference_in_human_readable', 'isBreached'));
    
    $data = file_get_contents('sample.json');
    $data = json_decode($data, true);
    $isBreached = '';
    foreach($data as $row){
        $diffInSeconds = $openingHours->diffInOpenSeconds(new DateTime($row['start_phase']), new DateTime($row['end_phase']));  
        $human_readable = CarbonInterval::seconds($diffInSeconds)->cascade()->forHumans();
    
        if($diffInSeconds > 144000){
            $isBreached = 'true';
        }else{
            $isBreached = 'false';
        }
    
        $result_csv[] = array($row['entity_id'], $row['start_phase'], $row['end_phase'], $diffInSeconds, $human_readable, $isBreached);
    }
    
    $fp = fopen('file.csv', 'wb');
    foreach ($result_csv as $fields) {
        fputcsv($fp, $fields);
    }
    fclose($fp);
    

    • Keep list of holidays in an array in Y-m-d format or similar.
    • Iterate on each day one by one.
    • Check if it’s a weekend or falls on one of these holidays. If yes, skip them.
    • For a current valid date, just add 9 hours. If the date is either the start date or the end date, then get the maximum of start time and minimum of end time and add the valid hours to your answer.

    Pseudocode:

    holidays = ['2023-08-15', '2023-11-01', '2023-11-11', '2023-12-25']
    startDay = '2023-10-28 07:00:00'
    endDay = '2023-11-03 10:00:00'
    
    ans = 0
    
    for day in range(start, end):
       if day in holidays OR date->weekDay() in [6, 0]:
           continue
       startTime = '09:00:00 AM'
       endTime = '17:00:00 PM' 
       if day == startDay:
          startTime = max('09:00:00 AM', day->startTime)
       if day == endDay:
          endTime = min('17:00:00 PM', day->endTime)
       if endTime > startTime and startTime >= '09:00 AM' and endTime <= '17:00:00 PM': 
          ans += endTime - startTime
    
    print ans + ' working hours'
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search