-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
281 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
<?php | ||
|
||
namespace BusinessTime\Deadline; | ||
|
||
use BusinessTime\BusinessTime; | ||
use BusinessTime\Constraint\BusinessTimeConstraint; | ||
use BusinessTime\Constraint\Composite\All; | ||
use DateTimeInterface; | ||
|
||
/** | ||
* A recurring cut-off point in time. For example, orders might be shipped at | ||
* 11am on weekdays, and this class could be used to get the next shipping time. | ||
* | ||
* @see RecurringDeadlineTest | ||
*/ | ||
class RecurringDeadline | ||
{ | ||
/** @var All|BusinessTimeConstraint[] */ | ||
private $deadlineConstraints; | ||
|
||
/** | ||
* Be careful to ensure that the given constraints will reliably match a | ||
* deadline without too many iterations. | ||
* | ||
* @param BusinessTimeConstraint ...$deadlineConstraints | ||
*/ | ||
public function __construct(BusinessTimeConstraint ...$deadlineConstraints) | ||
{ | ||
$this->deadlineConstraints = new All(...$deadlineConstraints); | ||
} | ||
|
||
/** | ||
* Get the next time this deadline will occur after a given time. | ||
* | ||
* It's possible this will loop infinitely if the given constraints never | ||
* match a time with the given precision. | ||
* | ||
* @param BusinessTime $time | ||
* | ||
* @return BusinessTime | ||
*/ | ||
public function nextOccurrenceFrom(BusinessTime $time): BusinessTime | ||
{ | ||
$time = $time->copy(); | ||
|
||
// Advance until we're out of a current deadline (as we want the next). | ||
while ($this->isDeadline($time)) { | ||
$time = $time->add($time->precision()); | ||
} | ||
|
||
// Advance until we hit the next deadline. | ||
while (!$this->isDeadline($time)) { | ||
$time = $time->add($time->precision()); | ||
} | ||
|
||
return $time->floor(); | ||
} | ||
|
||
/** | ||
* Get the previous time this deadline occurred after a given time. | ||
* | ||
* It's possible this will loop infinitely if the given constraints never | ||
* match a time with the given precision. | ||
* | ||
* @param BusinessTime $time | ||
* | ||
* @return BusinessTime | ||
*/ | ||
public function previousOccurrenceFrom(BusinessTime $time): BusinessTime | ||
{ | ||
$time = $time->copy(); | ||
|
||
// Regress until we're out of a current deadline (as we want the | ||
// previous one). | ||
while ($this->isDeadline($time)) { | ||
$time = $time->sub($time->precision()); | ||
} | ||
|
||
// Regress until we hit the next deadline. | ||
while (!$this->isDeadline($time)) { | ||
$time = $time->sub($time->precision()); | ||
} | ||
|
||
return $time->floor(); | ||
} | ||
|
||
/** | ||
* @param DateTimeInterface $time | ||
* | ||
* @return bool | ||
*/ | ||
private function isDeadline(DateTimeInterface $time): bool | ||
{ | ||
return $this->deadlineConstraints->isBusinessTime($time); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
<?php | ||
|
||
namespace BusinessTime\Tests\Unit\Deadline; | ||
|
||
use BusinessTime\BusinessTime; | ||
use BusinessTime\Constraint\HoursOfDay; | ||
use BusinessTime\Constraint\WeekDays; | ||
use BusinessTime\Deadline\RecurringDeadline; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
/** | ||
* Unit test the RecurringDeadline class. | ||
*/ | ||
class RecurringDeadlineTest extends TestCase | ||
{ | ||
/** | ||
* Test finding the next occurrence of a deadline for weekdays at 11am. | ||
* | ||
* @dataProvider nextDeadlineWeekDays11amProvider | ||
* | ||
* @param string $time | ||
* @param string $expectedNextDeadline | ||
*/ | ||
public function testNextDeadlineWeekDays11am( | ||
string $time, | ||
string $expectedNextDeadline | ||
): void { | ||
// Given we have a recurring deadline for weekdays at 11am; | ||
$deadline = new RecurringDeadline(new WeekDays(), new HoursOfDay(11)); | ||
|
||
// And a specific time; | ||
$businessTime = new BusinessTime($time); | ||
|
||
// When we get the next occurrence of the deadline; | ||
$nextOccurrence = $deadline->nextOccurrenceFrom($businessTime); | ||
|
||
// Then it should be as expected. | ||
self::assertSame( | ||
$expectedNextDeadline, | ||
$nextOccurrence->format('l H:i') | ||
); | ||
} | ||
|
||
/** | ||
* Provides times and the expected next occurrence of a deadline for | ||
* weekdays at 11am. | ||
* | ||
* @return array[] | ||
*/ | ||
public function nextDeadlineWeekDays11amProvider(): array | ||
{ | ||
return [ | ||
// From Monday | ||
['Monday 00:00', 'Monday 11:00'], | ||
['Monday 09:00', 'Monday 11:00'], | ||
['Monday 09:30', 'Monday 11:00'], | ||
['Monday 10:59', 'Monday 11:00'], | ||
['Monday 11:00', 'Tuesday 11:00'], | ||
['Monday 11:01', 'Tuesday 11:00'], | ||
['Monday 17:00', 'Tuesday 11:00'], | ||
['Monday 23:59', 'Tuesday 11:00'], | ||
// From Friday | ||
['Friday 00:00', 'Friday 11:00'], | ||
['Friday 09:00', 'Friday 11:00'], | ||
['Friday 09:30', 'Friday 11:00'], | ||
['Friday 10:59', 'Friday 11:00'], | ||
['Friday 11:00', 'Monday 11:00'], | ||
['Friday 11:01', 'Monday 11:00'], | ||
['Friday 17:00', 'Monday 11:00'], | ||
['Friday 23:59', 'Monday 11:00'], | ||
// From Saturday | ||
['Saturday 00:00', 'Monday 11:00'], | ||
['Saturday 09:00', 'Monday 11:00'], | ||
['Saturday 09:30', 'Monday 11:00'], | ||
['Saturday 10:59', 'Monday 11:00'], | ||
['Saturday 11:00', 'Monday 11:00'], | ||
['Saturday 11:01', 'Monday 11:00'], | ||
['Saturday 17:00', 'Monday 11:00'], | ||
['Saturday 23:59', 'Monday 11:00'], | ||
// From Sunday | ||
['Sunday 00:00', 'Monday 11:00'], | ||
['Sunday 09:00', 'Monday 11:00'], | ||
['Sunday 09:30', 'Monday 11:00'], | ||
['Sunday 10:59', 'Monday 11:00'], | ||
['Sunday 11:00', 'Monday 11:00'], | ||
['Sunday 11:01', 'Monday 11:00'], | ||
['Sunday 17:00', 'Monday 11:00'], | ||
['Sunday 23:59', 'Monday 11:00'], | ||
]; | ||
} | ||
|
||
/** | ||
* Test finding the previous occurrence of a deadline for weekdays at 11am. | ||
* | ||
* @dataProvider previousDeadlineWeekDays11amProvider | ||
* | ||
* @param string $time | ||
* @param string $expectedNextDeadline | ||
*/ | ||
public function testPreviousDeadlineWeekDays11am( | ||
string $time, | ||
string $expectedNextDeadline | ||
): void { | ||
// Given we have a recurring deadline for weekdays at 11am; | ||
$deadline = new RecurringDeadline(new WeekDays(), new HoursOfDay(11)); | ||
|
||
// And a specific time; | ||
$businessTime = new BusinessTime($time); | ||
|
||
// When we get the previous occurrence of the deadline; | ||
$nextOccurrence = $deadline->previousOccurrenceFrom($businessTime); | ||
|
||
// Then it should be as expected. | ||
self::assertSame( | ||
$expectedNextDeadline, | ||
$nextOccurrence->format('l H:i') | ||
); | ||
} | ||
|
||
/** | ||
* Provides times and the expected previous occurrence of a deadline for | ||
* weekdays at 11am. | ||
* | ||
* @return array[] | ||
*/ | ||
public function previousDeadlineWeekDays11amProvider(): array | ||
{ | ||
return [ | ||
// From Friday | ||
['Friday 00:00', 'Thursday 11:00'], | ||
['Friday 09:00', 'Thursday 11:00'], | ||
['Friday 09:30', 'Thursday 11:00'], | ||
['Friday 10:59', 'Thursday 11:00'], | ||
['Friday 11:00', 'Thursday 11:00'], | ||
['Friday 11:01', 'Thursday 11:00'], | ||
['Friday 17:00', 'Friday 11:00'], | ||
['Friday 23:59', 'Friday 11:00'], | ||
// From Monday | ||
['Monday 00:00', 'Friday 11:00'], | ||
['Monday 09:00', 'Friday 11:00'], | ||
['Monday 09:30', 'Friday 11:00'], | ||
['Monday 10:59', 'Friday 11:00'], | ||
['Monday 11:00', 'Friday 11:00'], | ||
['Monday 11:01', 'Friday 11:00'], | ||
['Monday 17:00', 'Monday 11:00'], | ||
['Monday 23:59', 'Monday 11:00'], | ||
// From Saturday | ||
['Saturday 00:00', 'Friday 11:00'], | ||
['Saturday 09:00', 'Friday 11:00'], | ||
['Saturday 09:30', 'Friday 11:00'], | ||
['Saturday 10:59', 'Friday 11:00'], | ||
['Saturday 11:00', 'Friday 11:00'], | ||
['Saturday 11:01', 'Friday 11:00'], | ||
['Saturday 17:00', 'Friday 11:00'], | ||
['Saturday 23:59', 'Friday 11:00'], | ||
// From Sunday | ||
['Sunday 00:00', 'Friday 11:00'], | ||
['Sunday 09:00', 'Friday 11:00'], | ||
['Sunday 09:30', 'Friday 11:00'], | ||
['Sunday 10:59', 'Friday 11:00'], | ||
['Sunday 11:00', 'Friday 11:00'], | ||
['Sunday 11:01', 'Friday 11:00'], | ||
['Sunday 17:00', 'Friday 11:00'], | ||
['Sunday 23:59', 'Friday 11:00'], | ||
]; | ||
} | ||
} |