Skip to content

Instantly share code, notes, and snippets.

@ranskills
Created December 11, 2018 21:05
Show Gist options
  • Save ranskills/50c3d5e0cfcda3c70b166f625afd706b to your computer and use it in GitHub Desktop.
Save ranskills/50c3d5e0cfcda3c70b166f625afd706b to your computer and use it in GitHub Desktop.
Calculates the next or prior business days(s) factoring in holidays
<?php
class BusinessCalendar
{
private $debugMessage;
private $debug = false;
public function __construct($debug = false)
{
$this->debug = $debug;
}
public function setDebug($debug = true)
{
$this->debug = $debug;
}
public function getNextBusinessDays($date, $workingDays, $holidays, $numDays)
{
$days = $this->getBusinessDays($date, $workingDays, $holidays, true, $numDays);
$this->debugMessage = sprintf(
'The next %d business after %s are %s',
$numDays,
$this->formatDate($date),
implode(', ', $this->formatDate($days, true))
);
$this->printDebugMessage();
return $days;
}
public function getPriorBusinessDays($date, $workingDays, $holidays, $numDays)
{
return $this->getBusinessDays($date, $workingDays, $holidays, false, $numDays);
}
public function getBusinessDaysForAPeriod($startDate, $endDate, $workingDays, $holidays)
{
$days = [];
$day = $startDate;
while ($day < $endDate) {
$day = $this->getTradingDate($day, $workingDays, $holidays, true);
$days[] = $day;
}
$this->debugMessage = sprintf(
'There are %d business day(s) between %s and %s which are %s',
count($days),
$this->formatDate($startDate),
$this->formatDate($endDate),
implode(', ', $this->formatDate($days, true))
);
$this->printDebugMessage();
return $days;
}
private function printDebugMessage()
{
if (!$this->debug) {
return;
}
echo $this->debugMessage.PHP_EOL.PHP_EOL;
}
public function getNextBusinessDay($date, $workingDays, $holidays)
{
$day = $this->getTradingDate($date, $workingDays, $holidays, true);
// \033[1;32m
$this->debugMessage = sprintf(
"The next business day after %s is %s",
$this->formatDate($date),
$this->formatDate($day, true)
);
$this->printDebugMessage();
return $day;
}
public function getPriorBusinessDay($date, $workingDays, $holidays)
{
$day = $this->getTradingDate($date, $workingDays, $holidays, false);
$this->debugMessage = sprintf(
'The business day before %s was %s',
$this->formatDate($date),
$this->formatDate($day, true)
);
$this->printDebugMessage();
return $day;
}
private function formatDate($dates, $highlight = false)
{
$ret = [];
if (is_array($dates)) {
array_walk(
$dates,
function ($date) use (&$ret, $highlight) {
$ret[] = $this->formatDate($date, $highlight);
}
);
} else {
// return sprintf("\033[40;1m%s\033[0m", (new \DateTime($dates))->format('D d M y'));
$format = $highlight ? "\033[30;47m%s\033[0m" : "%s";
return sprintf($format, (new \DateTime($dates))->format('D, j M y'));
}
return $ret;
}
private function getBusinessDays($date, $workingDays, $holidays, $next, $numDays)
{
$days = [];
$day = $date;
for ($i = 0; $i < $numDays; $i++) {
$day = $this->getTradingDate($day, $workingDays, $holidays, $next);
$days[] = $day;
}
return $days;
}
private function getTradingDate($date, $weekdays, $holidays, $next = true)
{
$date = new \DateTime($date);
$IsAWeekday = function (\DateTime $date) use ($weekdays) {
return in_array($date->format('N'), $weekdays);
};
$IsAHoliday = function (\DateTime $date) use ($holidays) {
return $holidays ?
in_array($date->format('Y-m-d'), $holidays) : false;
};
do {
if ($next) {
$date->add(new \DateInterval('P1D'));
} else {
$date->sub(new \DateInterval('P1D'));
}
} while (!($IsAWeekday($date) && ! $IsAHoliday($date)));
return $date->format('Y-m-d');
}
}
$calendar = new BusinessCalendar(true);
$weekdays = [1,2,3,4,5];
$day = '2018-12-11';
$nextDay = $calendar->getNextBusinessDay($day, $weekdays, []);
$nextDay = $calendar->getPriorBusinessDay($day, $weekdays, []);
$nextDay = $calendar->getNextBusinessDay($day, $weekdays, ['2018-08-06', '2018-08-07']);
$nextDays = $calendar->getNextBusinessDays($day, $weekdays, ['2018-08-06', '2018-08-07'], 2);
$nextDays = $calendar->getBusinessDaysForAPeriod(
$day,
'2018-12-18',
$weekdays,
['2018-12-12', '2018-12-15']
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment