Skip to content

Instantly share code, notes, and snippets.

@hranicka
Created February 22, 2017 17:15
Show Gist options
  • Save hranicka/badd576d08cad21a7e96eb6441fb9bda to your computer and use it in GitHub Desktop.
Save hranicka/badd576d08cad21a7e96eb6441fb9bda to your computer and use it in GitHub Desktop.
AvailabilityResolver - Can you find a bug w/o tests?
<?php
class AvailabilityResolver
{
/** @var \DateTimeInterface */
private $dateTime;
public function __construct(\DateTimeInterface $dateTime = NULL)
{
$this->dateTime = $dateTime ?: new \DateTimeImmutable();
}
/**
* Returns a date increased by a given amount of working days.
* @param int $workingDays
* @return \DateTimeInterface
*/
public function date($workingDays)
{
$dateTime = new \DateTime();
$dateTime->setTimestamp($this->dateTime->getTimestamp());
$dateTime->setTimezone($this->dateTime->getTimezone());
while ($workingDays-- > 0) {
$dateTime->modify('+1 day');
$this->fixWorkingDays($dateTime);
};
$this->fixWorkingDays($dateTime);
return $dateTime;
}
private function fixWorkingDays(\DateTime $dateTime)
{
do {
$beforeEncore = clone $dateTime;
// Check for weekends.
$weekDay = (int)$dateTime->format('w');
if ($weekDay === 6) { // Saturday
$dateTime->modify('+2 days');
} elseif ($weekDay === 0) { // Sunday
$dateTime->modify('+1 day');
}
// Check for holidays.
$holidays = $this->holidays();
$monthDay = $dateTime->format('m-d');
if (array_search($monthDay, $holidays) !== FALSE) {
$dateTime->modify('+1 day');
}
} while ($beforeEncore != $dateTime); // Intentional weak type comparison.
}
private function holidays()
{
return [
'01-01',
'05-01',
'05-08',
'07-05',
'07-06',
'09-28',
'10-28',
'11-17',
'12-24',
'12-25',
'12-26',
];
}
}
@hranicka
Copy link
Author

hranicka commented Feb 23, 2017

I'm convinced it's impossible find the bug (it really exists there) without tests. It's a scenario where you should always write them!

If you wanna see tests, here they're:

<?php

namespace Tests;

use AvailabilityResolver;
use PHPUnit\Framework\TestCase;

class AvailabilityResolverTest extends TestCase
{

    public function testDateBasic1()
    {
        $date = new \DateTimeImmutable('2017-01-03 0:00:00'); // Tuesday
        $resolver = new AvailabilityResolver($date);

        $this->assertEquals($date->modify('+0 days'),  $resolver->date(0)); // Tuesday + 0 working days = today = Tuesday (+0).
        $this->assertEquals($date->modify('+1 day'),  $resolver->date(1)); // Tuesday + 1 working day = Wednesday (+1).
        $this->assertEquals($date->modify('+2 days'),  $resolver->date(2)); // Tuesday + 2 working days = Thursday (+3).
        $this->assertEquals($date->modify('+3 days'),  $resolver->date(3)); // Tuesday + 3 working days = Friday (+4).
    }

    public function testDateBasic2()
    {
        $date = new \DateTimeImmutable('2017-01-01 0:00:00'); // Sunday
        $resolver = new AvailabilityResolver($date);

        $this->assertEquals($date->modify('+1 day'),  $resolver->date(0)); // Sunday + 0 working days = we need Monday (+1).
        $this->assertEquals($date->modify('+2 days'),  $resolver->date(1)); // Sunday + 1 working day = Tuesday (+2).
        $this->assertEquals($date->modify('+8 days'),  $resolver->date(5)); // Sunday + 5 working days = next Monday (+8).
        $this->assertEquals($date->modify('+9 days'),  $resolver->date(6)); // Sunday + 6 working days => next Tuesday (+9).
        $this->assertEquals($date->modify('+10 days'), $resolver->date(7)); // Sunday + 7 working days = next Wednesday (+10)
    }

    public function testDateWeekend()
    {
        $date = new \DateTimeImmutable('2016-12-31 0:00:00'); // Saturday
        $resolver = new AvailabilityResolver($date);

        $this->assertEquals($date->modify('+2 days'),  $resolver->date(0)); // Saturday + 0 working days = we need Monday (+2).
        $this->assertEquals($date->modify('+3 days'),  $resolver->date(1)); // Saturday + 1 working day = Tuesday (+3).
        $this->assertEquals($date->modify('+4 days'),  $resolver->date(2)); // Saturday + 2 working days = Wednesday (+4).
    }

    public function testDateHoliday()
    {
        $date = new \DateTimeImmutable('2017-04-30 0:00:00'); // Sunday
        $resolver = new AvailabilityResolver($date);

        $this->assertEquals($date->modify('+2 days'),  $resolver->date(0)); // Sunday + 0 working days = wee need Tuesday (Monday is holiday) (+2).
        $this->assertEquals($date->modify('+3 days'),  $resolver->date(1)); // Sunday + 1 working day = Wednesday (+3).
        $this->assertEquals($date->modify('+4 days'),  $resolver->date(2)); // Sunday + 2 working days = Tuesday (+4).
    }

    public function testDateConsecutiveHolidays()
    {
        $date = new \DateTimeImmutable('2017-12-23 0:00:00'); // Saturday
        $resolver = new AvailabilityResolver($date);

        $this->assertEquals($date->modify('+4 days'),  $resolver->date(0)); // Saturday + 0 working days = wee need Wednesday (Monday and Tuesday are holidays) (+4).
        $this->assertEquals($date->modify('+5 days'),  $resolver->date(1)); // Saturday + 1 working day = Thursday (+5).
        $this->assertEquals($date->modify('+6 days'),  $resolver->date(2)); // Saturday + 2 working days = Friday (+6).
        $this->assertEquals($date->modify('+10 days'),  $resolver->date(3)); // Saturday + 3 working days = next Tuesday (Monday is holiday) (+10).
        $this->assertEquals($date->modify('+11 days'),  $resolver->date(4)); // Saturday + 4 working days = next Wednesday (+11).
        $this->assertEquals($date->modify('+12 days'),  $resolver->date(5)); // Saturday + 5 working days = next Thursday (+12).
    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment