How to find next date removing weekend and public holiday starting from today in PHP

Ok, so here is a snippets of finding out the next business or working day excluding public holiday that i used in a project. Its pretty neat, but holidays date changes every year and each country holiday is different from each other. Therefore, the holidays are being placed in an array instead. Without further ado, here's the snippets.

// get today date
$tmpDate = date('Y-m-d');
// all the holidays
$holidays = array('2015-01-01', '2015-01-02', '2015-02-04', '2015-02-18', '2015-02-19', '2015-02-20', '2015-02-21', '2015-02-22', '2015-02-23', '2015-02-27',
    '2015-02-28', '2015-03-05', '2015-03-08', '2015-03-12', '2015-03-20', '2015-03-21', '2015-03-29', '2015-04-03', '2015-04-04', '2015-04-05',
    '2015-04-06', '2015-04-07', '2015-05-01', '2015-05-03', '2015-05-04', '2015-05-10', '2015-05-11', '2015-05-25', '2015-06-03', '2015-06-19',
    '2015-06-20', '2015-06-21', '2015-06-28', '2015-08-08', '2015-08-20', '2015-08-28', '2015-09-03', '2015-09-23', '2015-09-26', '2015-09-27',
    '2015-09-28', '2015-10-09', '2015-10-10', '2015-10-21', '2015-10-25', '2015-11-12', '2015-11-26', '2015-12-22', '2015-12-25');
// 1 day
$i = 1;
// for the next business day from today
$nextBusinessDay = date('Y-m-d', strtotime($tmpDate.
    ' +'.$i.
    ' Weekday'));
// check whether the date of the business day is in public holiday
while (in_array($nextBusinessDay, $holidays)) {
    $i++;
    // move the business date forward since its a public holiday
    $nextBusinessDay = date('Y-m-d', strtotime($tmpDate.
        ' +'.$i.
        ' Weekday'));
}

Now, the code above will find the next working day. How about telling me how to find 10 days after the working day? Pretty much the same just repeat the same logic and place it below it,

    // get today date
    $tmpDate = date('Y-m-d');
    // all the holidays
    $holidays = array('2015-01-01', '2015-01-02', '2015-02-04', '2015-02-18', '2015-02-19', '2015-02-20', '2015-02-21', '2015-02-22', '2015-02-23',
        '2015-02-27', '2015-02-28', '2015-03-05', '2015-03-08', '2015-03-12', '2015-03-20', '2015-03-21', '2015-03-29', '2015-04-03', '2015-04-04',
        '2015-04-05', '2015-04-06', '2015-04-07', '2015-05-01', '2015-05-03', '2015-05-04', '2015-05-10', '2015-05-11', '2015-05-25', '2015-06-03',
        '2015-06-19', '2015-06-20', '2015-06-21', '2015-06-28', '2015-08-08', '2015-08-20', '2015-08-28', '2015-09-03', '2015-09-23', '2015-09-26',
        '2015-09-27', '2015-09-28', '2015-10-09', '2015-10-10', '2015-10-21', '2015-10-25', '2015-11-12', '2015-11-26', '2015-12-22', '2015-12-25');
    // 1 day
    $i = 1;
    // for the next business day from today
    $nextBusinessDay = date('Y-m-d', strtotime($tmpDate.
        ' +'.$i.
        ' Weekday'));
    // check whether the date of the business day is in public holiday
    while (in_array($nextBusinessDay, $holidays)) {
        $i++;
        // move the business date forward since its a public holiday
        $nextBusinessDay = date('Y-m-d', strtotime($tmpDate.
            ' +'.$i.
            ' Weekday'));
    }
    // working day + 10 working day excluding holidays
    $tmpDate = $nextBusinessDay;
    // 1 day
    $i = 1;
    // next business day excluding weekdays after 10 days
    $nextBusinessDay = date('Y-m-d', strtotime($tmpDate.
        ' +10 weekdays'));
    // this is the format we want it to look like
    $date = date("m月d日", strtotime($tmpDate.
        " +10 weekdays"));
    // is the next business day after 10 day a public holiday?
    while (in_array($nextBusinessDay, $holidays)) {
        // if it is, move the day by 1 
        $nextBusinessDay = date('Y-m-d', strtotime($nextBusinessDay.
            ' +'.$i.
            ' weekdays'));
        // get the format we want, actually this can be thrown outside of the loop
        $date = date('m月d日', strtotime($nextBusinessDay));
        $i++;
    }

And as you can see, i DID NOT OPTIMIZE the code. i'm sorry. don't hate me, but i do share it, so, forgive me. Anyway, the code above can be reduce by half if you dump it into a function and just reuse the logic. i kinda lazy without doing that. IM SO SORRY! but if you did optimize it and dump it into a neat function, do share it out in the comment and i'll dump it right here!

*** UPDATE ****

Ok basically the above code doesn't remove the issue when there are days when the holidays fall between 2 dates. Anyhow, the more appropriate code that takes care of ALL holidays are

// find latest working day
$tmpDate = date('Y-m-d');
$holidays = array('2016-02-06','2016-02-07','2016-02-08','2016-02-09','2016-02-10','2016-02-11','2016-02-12','2016-02-13','2016-02-14','2016-02-27','2016-02-28','2016-02-29','2016-04-02','2016-04-03','2016-04-04','2016-04-05','2016-06-09','2016-06-10','2016-06-11','2016-06-12','2016-09-15','2016-09-16','2016-09-17','2016-09-18','2016-10-09','2016-10-10','2016-10-11');
$i = 1;
$nextBusinessDay = date('Y-m-d', strtotime($tmpDate . ' +' . $i . ' Weekday'));

while (in_array($nextBusinessDay, $holidays)) {
  $i++;
  $nextBusinessDay = date('Y-m-d', strtotime($tmpDate . ' +' . $i . ' Weekday'));
}
// working day + 10 working day excluding holidays
$tmpDate = $nextBusinessDay;
$startDate = new DateTime( $tmpDate );    //intialize start date
$endDate = new DateTime( '2016-12-31' );    //initialize end date
$holiday = array('2016-02-06','2016-02-07','2016-02-08','2016-02-09','2016-02-10','2016-02-11','2016-02-12','2016-02-13','2016-02-14','2016-02-27','2016-02-28','2016-02-29','2016-04-02','2016-04-03','2016-04-04','2016-04-05','2016-06-09','2016-06-10','2016-06-11','2016-06-12','2016-09-15','2016-09-16','2016-09-17','2016-09-18','2016-10-09','2016-10-10','2016-10-11');
$interval = new DateInterval('P1D');    // set the interval as 1 day
$daterange = new DatePeriod($startDate, $interval ,$endDate);
// find all the dates that are working days
foreach($daterange as $date){
        if($date->format("N") <6 AND !in_array($date->format("Y-m-d"),$holiday))
                $result[] = $date->format("Y-m-d");
}
// now we want 10 working days starting from 0 (hence the 9 )
echo $result[9];

Basically, we first find the next business day as the 'starting' date and also initial the 'end' date which is the end of 2016 in this case, the 'results' will gives me a list of working dates. From the list of work date, i need 10 working days, and since its an array, something like 10th working day can be simply print out like the one show in the code

Share