Overview
La classe DateTime
possède une méthode pour ajouter facilement une période de temps (ici on ajoute 10 jours):
$date = new DateTime('now');
$date->add(new DateInterval('P10D'));
Mais il n’est pas possible d’ajouter des jours ouvrés, ce qui est parfois bien utile. Une solution simple est d’étendre la classe DateTime
avec deux méthodes :
addBusinessDays
qui prend en seul paramètre : le nombre de jours ouvrés à ajouter à la dateaddBusinessDay
, méthode sans paramètre qui va ajouter 1 jour ouvrés en vérifiant avant si la date courante est vendredi ou samedi
Il est importer d’utiliser un espace de nom si le nom de la classe est DateTime
:
namespace LDDW;
class DateTime extends \DateTime
{
private $periods;
public function __construct(string $time = 'now', \DateTimeZone $timezone = null)
{
parent::__construct($time, $timezone);
// Put periods in a var cache
$this->periods = [
1 => new \DateInterval('P1D'),
2 => new \DateInterval('P2D'),
3 => new \DateInterval('P3D'),
];
}
public function addBusinessDays(int $nb)
{
if($nb > 0) {
for($a = 0; $a < $nb; $a++) {
$this->addBusinessDay();
}
}
return $this;
}
private function addBusinessDay()
{
if($this->isFriday()) {
$this->add($this->periods[3]);
} elseif($this->isSaturday()) {
$this->add($this->periods[2]);
} else {
$this->add($this->periods[1]);
}
}
private function isFriday(): bool
{
return $this->format('N') == 5;
}
private function isSaturday(): bool
{
return $this->format('N') == 6;
}
}
Utilisation
La classe s’utilise comme DateTime
, à savoir :
use LDDW\DateTime;
$date = new DateTime('now');
$date->addBusinessDays(10);
Bien s’assurer d’utiliser le bon espace de nom avec use LDDW\DateTime;
Partons en vacances
Supposons que certains jours ouvrés sont en réalité des jours de vacances. Dans ce cas, si après avoir ajouté x jours ouvrés on tombe sur un jour de vacance, on doit renvoyer la prochaine date considérée comme ouvrée. Ajoutons donc quelques méthodes à notre classe :
- une propriété
$holidays
qui va contenir les jours considérés comme des vacances - les getters/setters de la propriété
$holidays
- une méthode
addHoliday()
qui va ajouter un jour de vacance - une méthode
isSunday()
qui va nous dire si la date est un dimanche. Cette méthode n’est pas fondamentalement obligatoire mais je vais m’en servir dans la suivante - une méthode
isWeekend()
qui va nous dire si la date est un des 2 jours de week-end - une méthode
isBusinnessDay()
qui va nous dire si je jour est ouvré : si on est pas en week-end ni en vacances, on a donc un jour ouvré..
class DateTime extends \DateTime
{
/** @var array */
private $holidays;
// ...
/**
* @return array
*/
public function getHolidays(): array
{
return $this->holidays;
}
/**
* @param array $holidays
* @return $this
*/
public function setHolidays(array $holidays)
{
$this->holidays = array_fill_keys($holidays, true);
return $this;
}
/**
* @param $day dd-mm-yyyy
* Ex: $this->addHoliday('06-12-2018');
* @return $this
*/
public function addHoliday($day)
{
if(preg_match('/^[0-9]{2}-[0-9]{2}-20[0-9]{2}$/', $day)) {
$this->holidays[$day] = true;
}
return $this;
}
/**
* @return bool
*/
private function isBusinnessDay(): bool
{
$key = $this->format('d-m-Y');
return !$this->isWeekend() && empty($this->holidays[$key]);
}
public function isSunday(): bool
{
return $this->format('N') == 7;
}
public function isWeekend(): bool
{
return ($this->isSunday() || $this->isSaturday());
}
}
Reste plus qu’à modifier la méthode addBusinessDays()
qui va maintenant vérifier que le résultat est bien un jour ouvré :
public function addBusinessDays(int $nb)
{
// ...
// Check for holidays
while(!$this->isBusinnessDay()) {
$this->addBusinessDay();
}
return $this;
}
Petit rappel : un jour est ouvré si on est ni en vacances ni en week-end.
Et voilà comment utiliser la classe :
use LDDW\DateTime;
$date = new DateTime('now');
$date->setHolidays(array(
'22-12-2018',
'23-12-2018'
));
$date->addBusinessDays(10);
Amélioration
Cette classe a un défaut majeur : elle ne prend en compte que les week-ends, passant sous silence les jours fériés et autres joyeusetés du calendrier. Une belle amélioration serait de les intégrer, au moins en Français et Anglais.
La classe complète
Voilà la classe au grand complet, merci de m’avoir lu jusqu’ici !
namespace LDDW;
class DateTime extends \DateTime
{
private $periods;
private $holidays;
/**
* @return array
*/
public function getPeriods(): array
{
return $this->periods;
}
/**
* @param array $periods
*/
public function setPeriods(array $periods): void
{
$this->periods = $periods;
}
public function __construct(string $time = 'now', \DateTimeZone $timezone = null)
{
parent::__construct($time, $timezone);
// Put periods in a var cache
$this->periods = [
1 => new \DateInterval('P1D'),
2 => new \DateInterval('P2D'),
3 => new \DateInterval('P3D'),
];
}
public function addBusinessDays(int $nb)
{
if($nb > 0) {
for($a = 0; $a < $nb; $a++) {
$this->addBusinessDay();
}
}
return $this;
}
private function addBusinessDay()
{
if($this->isFriday()) {
$this->add($this->periods[3]);
} elseif($this->isSaturday()) {
$this->add($this->periods[2]);
} else {
$this->add($this->periods[1]);
}
// Check for holidays
while(!$this->isBusinnessDay()) {
$this->addBusinessDay();
}
}
public function getHolidays(): array
{
return $this->holidays;
}
/**
* @param array $holidays
* @return $this
*/
public function setHolidays(array $holidays)
{
$this->holidays = array_fill_keys($holidays, true);
return $this;
}
/**
* @param $day dd-mm-yyyy
* Ex: $this->addHoliday('06-12-2018');
* @return $this
*/
public function addHoliday($day)
{
if(preg_match('/^[0-9]{2}-[0-9]{2}-20[0-9]{2}$/', $day)) {
$this->holidays[$day] = true;
}
return $this;
}
/**
* @return bool
*/
private function isBusinnessDay(): bool
{
$key = $this->format('d-m-Y');
return !$this->isWeekend() && empty($this->holidays[$key]);
}
private function isFriday(): bool
{
return $this->format('N') == 5;
}
private function isSaturday(): bool
{
return $this->format('N') == 6;
}
public function isSunday(): bool
{
return $this->format('N') == 7;
}
public function isWeekend(): bool
{
return ($this->isSunday() || $this->isSaturday());
}
}
Références externes
- la classe DateTime (fr)
Commentaires récents