Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get 3 next working dates, skip weekends and holidays

Tags:

date

php

I am trying to get the next 3 days using php but if the date is a weekend or a defined holiday I would like to skip that date.

For example if the date is Monday, December 23, 2013 My holiday dates are array('2013-12-24', '2013-12-25'); The script would return

Monday, December 23, 2013
Thursday, December 26, 2013
Friday, December 27, 2013
Monday, December 30, 2013

Her is my current code:

$arr_date = explode('-', '2013-12-23');
$counter = 0;
for ($iLoop = 0; $iLoop < 4; $iLoop++)
{
    $dayOfTheWeek = date("N", mktime(0, 0, 0, $arr_date[1], $arr_date[2]+$counter, $arr_date[0]));
    if ($dayOfTheWeek == 6) { $counter += 2; }
    $date = date("Y-m-d", mktime(0, 0, 0, $arr_date[1], $arr_date[2]+$counter, $arr_date[0]));
    echo $date;
    $counter++;
}

The issue I am having is I am not sure to how to even go about excluding holidays dates.

like image 309
Robert E. McIntosh Avatar asked Feb 15 '23 02:02

Robert E. McIntosh


2 Answers

Use a DateTime to recurse over the next days and have a string comparison check to see if the next generated day belongs in the holiday or weekend arrays using in_array

$holidays = array('12-24', '12-25');
$weekend = array('Sun','Sat');
$date = new DateTime('2013-12-23');
$nextDay = clone $date;
$i = 0; // We have 0 future dates to start with
$nextDates = array(); // Empty array to hold the next 3 dates
while ($i < 3)
{
    $nextDay->add(new DateInterval('P1D')); // Add 1 day
    if (in_array($nextDay->format('m-d'), $holidays)) continue; // Don't include year to ensure the check is year independent
    // Note that you may need to do more complicated things for special holidays that don't use specific dates like "the last Friday of this month"
    if (in_array($nextDay->format('D'), $weekend)) continue;
    // These next lines will only execute if continue isn't called for this iteration
    $nextDates[] = $nextDay->format('Y-m-d');
    $i++;
}

Using the suggestion of isset() for O(1) instead of O(n):

$holidays = array('12-24' => '', '12-25' => '');
$weekend = array('Sun' => '','Sat' => '');
$date = new DateTime('2013-12-23');
$dayInterval = new DateInterval('P1D');
$nextDay = clone $date;
$i = 0;
$nextDates = array();
while ($i < 3)
{
    $nextDay->add($dayInterval);
    if (isset($holidays[$nextDay->format('m-d')])) continue;
    if (isset($weekend[$nextDay->format('D')])) continue;
    $nextDates[] = $nextDay->format('Y-m-d');
    $i++;
}
like image 70
sjagr Avatar answered Feb 24 '23 08:02

sjagr


Here is a simple example of how to get number of working days between 2 dates; which you can simply modify to fit your needs:

function number_of_working_dates($from, $days) {
    $workingDays = [1, 2, 3, 4, 5]; # date format = N (1 = Monday, ...)
    $holidayDays = ['*-12-25', '*-01-01', '2013-12-24', '2013-12-25']; # variable and fixed holidays

    $from = new DateTime($from);
    $dates = [];
    $dates[] = $from->format('Y-m-d');
    while ($days) {
        $from->modify('+1 day');

        if (!in_array($from->format('N'), $workingDays)) continue;
        if (in_array($from->format('Y-m-d'), $holidayDays)) continue;
        if (in_array($from->format('*-m-d'), $holidayDays)) continue;

        $dates[] = $from->format('Y-m-d');
        $days--;
    }
    return $dates;
}

print_r( number_of_working_dates('2013-12-23', 3) );

demo

like image 22
Glavić Avatar answered Feb 24 '23 08:02

Glavić