HiveBrain v1.2.0
Get Started
← Back to all entries
patternphpModerate

Calculate future date based on business days

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
businessdatefuturecalculatebaseddays

Problem

I've written a program for calculating the future date after certain number of business days.

function add_business_days($startdate,$businessdays,$holidays,$dateformat){  
   $i=1;  
   $dayx = strtotime($startdate);  

  while($i < $businessdays){  
     $day = date('N',$dayx);  
     $date = date('Y-m-d',$dayx);  
     if($day < 6 && !in_array($date,$holidays))$i++;  
     $dayx = strtotime($date.' +1 day');  
  }  
  return date($dateformat,$dayx);
}


But the problem with this function is that it doesn't check the last date of business day for Saturday and Sunday. Also, the last day could be the holiday. Can anyone provide me a better solution for this?

I've made a solution to this problem as

function add_business_days($startdate,$businessdays,$holidays,$dateformat){  
   $i=1;  
   $dayx = strtotime($startdate);  

  while($i <= $businessdays){  
     $day = date('N',$dayx);  
     $date = date('Y-m-d',$dayx);  
     if($day < 6 && !in_array($date,$holidays))$i++;  
     $dayx = strtotime($date.' +1 day');  
  }  
  return date($dateformat,strtotime(date('Y-m-d', $dayx) . ' -1 day'));
}


But it doesn't seems good.

Usage Example

I'm using this method for calculating the shipment date on which a product will be delivered to the customer.

$startdate = "2014-05-28";  //Order placing date  
$businessdays = 7; //number of days for delivery  
$holidays = array('2014-05-30','2014-06-03');  //array of dates having holidays. excluding business days.  
$dateformat = 'd-m-Y';  //output date format for displaying.
$delivery = add_business_days($startdate, $businessdays, $holidays, $dateformat);


The $delivery is supposed to have date 09-06-2014 but the first code returns 07-06-2014 which doesn't check for the last date in loop for weekends (Saturday or Sunday). I've made the solution in the second program by execution the loop one more time and then return the previous date. But this approach doesn't seem good. Any suggestions on not

Solution

I propose a class, BusinessDayCalculator, which would handle all this logic in one place. Here's an example:

date = $startDate;
        $this->holidays = $holidays;
        $this->nonBusinessDays = $nonBusinessDays;
    }

    public function addBusinessDays($howManyDays) {
        $i = 0;
        while ($i date->modify("+1 day");
            if ($this->isBusinessDay($this->date)) {
                $i++;
            }
        }
    }

    public function getDate() {
        return $this->date;
    }

    private function isBusinessDay(DateTime $date) {
        if (in_array((int)$date->format('N'), $this->nonBusinessDays)) {
            return false; //Date is a nonBusinessDay.
        }
        foreach ($this->holidays as $day) {
            if ($date->format('Y-m-d') == $day->format('Y-m-d')) {
                return false; //Date is a holiday.
            }
        }
        return true; //Date is a business day.
    }
}

$calculator = new BusinessDaysCalculator(
    new DateTime(), // Today
    [new DateTime("2014-06-01"), new DateTime("2014-06-02")],
    [BusinessDaysCalculator::SATURDAY, BusinessDaysCalculator::FRIDAY]
);

$calculator->addBusinessDays(3); // Add three business days 

var_dump($calculator->getDate());


In the above example the nonBusinessDays of the week are SATURDAY and FRIDAY, and 1st and 2nd of June are holidays. In that case, 3 business days from now is June 5th.

This can probably be improved further, let your imagination run wild :P

Code Snippets

<?php

class BusinessDaysCalculator {

    const MONDAY    = 1;
    const TUESDAY   = 2;
    const WEDNESDAY = 3;
    const THURSDAY  = 4;
    const FRIDAY    = 5;
    const SATURDAY  = 6;
    const SUNDAY    = 7;

    /**
     * @param DateTime   $startDate       Date to start calculations from
     * @param DateTime[] $holidays        Array of holidays, holidays are no conisdered business days.
     * @param int[]      $nonBusinessDays Array of days of the week which are not business days.
     */
    public function __construct(DateTime $startDate, array $holidays, array $nonBusinessDays) {
        $this->date = $startDate;
        $this->holidays = $holidays;
        $this->nonBusinessDays = $nonBusinessDays;
    }

    public function addBusinessDays($howManyDays) {
        $i = 0;
        while ($i < $howManyDays) {
            $this->date->modify("+1 day");
            if ($this->isBusinessDay($this->date)) {
                $i++;
            }
        }
    }

    public function getDate() {
        return $this->date;
    }

    private function isBusinessDay(DateTime $date) {
        if (in_array((int)$date->format('N'), $this->nonBusinessDays)) {
            return false; //Date is a nonBusinessDay.
        }
        foreach ($this->holidays as $day) {
            if ($date->format('Y-m-d') == $day->format('Y-m-d')) {
                return false; //Date is a holiday.
            }
        }
        return true; //Date is a business day.
    }
}

$calculator = new BusinessDaysCalculator(
    new DateTime(), // Today
    [new DateTime("2014-06-01"), new DateTime("2014-06-02")],
    [BusinessDaysCalculator::SATURDAY, BusinessDaysCalculator::FRIDAY]
);

$calculator->addBusinessDays(3); // Add three business days 

var_dump($calculator->getDate());

Context

StackExchange Code Review Q#51895, answer score: 10

Revisions (0)

No revisions yet.