Skip to content

Commit

Permalink
Add intersects method for date range (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
fre5h authored Apr 27, 2021
1 parent c3f2618 commit 8977917
Show file tree
Hide file tree
Showing 6 changed files with 590 additions and 3 deletions.
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,12 @@ $dateTimeZone2 = $dateTimeHelper->createDateTimeZone('Europe/Kiev'); // Or with
$dateTimeZone3 = $dateTimeHelper->createDateTimeZoneUtc(); // Another method to get UTC timezone
```

### Immutable ValueObject `DateRange`
### Immutable `DateRange` ValueObject

You often need to manipulate with since/till dates, so-called date ranges.
You often needed to manipulate with since/till dates, so-called date ranges.
By its nature, date range is a `ValueObject`, it can be reused many times for different purposes.
This library provides a `DateRange` immutable class, which is not able to be changed after its creation.
This library provides a `DateRange` immutable class, which is not able to be changed after its creation.
`DateRange` operates only with dates and ignore time.

```php
use Fresh\DateTime\DateRange;
Expand All @@ -90,6 +91,32 @@ $dateRange2 = new DateRange(new \DateTime('yesterday'), new \DateTime('tomorrow'
$dateRange1->isEqual($dateRange2); // Returns FALSE, because date ranges have different timezones
```

### Immutable `DateTimeRange` ValueObject

This library provides also immutable class `DateTimeRange`, instead of `DateRange` it checks date and time.

```php
use Fresh\DateTime\DateTimeRange;

$dateTimeRange1 = new DateTimeRange(new \DateTime('2000-01-01 19:00:00'), new \DateTime('2000-01-01 21:00:00'));
$dateTimeRange2 = new DateTimeRange(new \DateTime('2000-01-01 19:00:00'), new \DateTime('2000-01-01 21:00:00', new \DateTimeZone('Europe/Kiev')));
$dateTimeRange3 = new DateTimeRange(new \DateTime('2000-01-01 20:00:00'), new \DateTime('2000-01-01 22:00:00'));

// There is also the `isEqual` method to compare two DateTimeRange objects.
$dateTimeRange1->isEqual($dateTimeRange2); // Returns FALSE, because datetime ranges have different timezones

// There is also the `intersects` method to check if datetime range intersected each other.
$dateTimeRange1->intersects($dateTimeRange3); // Returns TRUE, because datetime ranges are intersected
```

#### Examples of date ranges with intersection

![Example of intersection](docs/images/intersect.png "Example of intersection")

#### Examples of date ranges without intersection

![Example of no intersection](docs/images/does_not_intersect.png "Example of no intersection")

### Getting array of objects/strings of all dates in date range

```php
Expand Down
Binary file added docs/images/does_not_intersect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/intersect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
124 changes: 124 additions & 0 deletions src/DateTimeRange.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php
/*
* This file is part of the DateTime library.
*
* (c) Artem Henvald <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Fresh\DateTime;

use Fresh\DateTime\Exception\LogicException;

/**
* DateTimeRange.
*
* @author Artem Henvald <[email protected]>
*/
final class DateTimeRange implements DateTimeRangeInterface
{
/** @var \DateTimeImmutable */
private $since;

/** @var \DateTimeImmutable */
private $till;

/**
* @param \DateTimeInterface $since
* @param \DateTimeInterface $till
*/
public function __construct(\DateTimeInterface $since, \DateTimeInterface $till)
{
$this->assertSameTimezones($since, $till);

$this->since = DateTimeCloner::cloneIntoDateTimeImmutable($since);
$this->till = DateTimeCloner::cloneIntoDateTimeImmutable($till);
}

/**
* {@inheritdoc}
*/
public function getTimezone(): \DateTimeZone
{
return $this->since->getTimezone(); // Since and till timezones are equal
}

/**
* {@inheritdoc}
*/
public function getTimezoneName(): string
{
return $this->since->getTimezone()->getName(); // Since and till timezones are equal
}

/**
* {@inheritdoc}
*/
public function getSince(): \DateTimeImmutable
{
return $this->since;
}

/**
* {@inheritdoc}
*/
public function getTill(): \DateTimeImmutable
{
return $this->till;
}

/**
* {@inheritdoc}
*/
public function isEqual(DateTimeRangeInterface $dateTimeRange): bool
{
$dateRangeSince = $dateTimeRange->getSince();
$dateRangeTill = $dateTimeRange->getTill();

return $this->since->getTimestamp() === $dateRangeSince->getTimestamp()
&& $this->till->getTimestamp() === $dateRangeTill->getTimestamp()
&& $this->since->getTimezone()->getName() === $dateRangeSince->getTimezone()->getName()
&& $this->till->getTimezone()->getName() === $dateRangeTill->getTimezone()->getName();
}

/**
* {@inheritdoc}
*/
public function intersects(DateTimeRangeInterface $dateTimeRange): bool
{
if ($this->getTimezoneName() !== $dateTimeRange->getTimezoneName()) {
throw new LogicException('Timezones of datetime ranges are different');
}

$givenDateRangeSince = $dateTimeRange->getSince();
$givenDateRangeTill = $dateTimeRange->getTill();

switch (true) {
case $this->since === $givenDateRangeSince && $this->till === $givenDateRangeTill: // Current date range is equal to the given date range
case $givenDateRangeSince < $this->since && $this->till < $givenDateRangeTill: // Current date range is fully inside the given date range
case $this->since < $givenDateRangeSince && $givenDateRangeTill < $this->till: // Given date range is fully inside the current date range
case $givenDateRangeSince <= $this->since && $this->since < $givenDateRangeTill: // Current date range beginning is inside the given date range
case $givenDateRangeSince < $this->till && $this->till <= $givenDateRangeTill: // Current date range ending is inside the given date range
return true;
default:
return false;
}
}

/**
* @param \DateTimeInterface $since
* @param \DateTimeInterface $till
*
* @throws LogicException
*/
private function assertSameTimezones(\DateTimeInterface $since, \DateTimeInterface $till): void
{
if ($since->getTimezone()->getName() !== $till->getTimezone()->getName()) {
throw new LogicException('Datetimes have different timezones');
}
}
}
55 changes: 55 additions & 0 deletions src/DateTimeRangeInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the DateTime library.
*
* (c) Artem Henvald <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Fresh\DateTime;

/**
* DateTimeRangeInterface.
*
* @author Artem Henvald <[email protected]>
*/
interface DateTimeRangeInterface
{
/**
* @return \DateTimeZone
*/
public function getTimezone(): \DateTimeZone;

/**
* @return string
*/
public function getTimezoneName(): string;

/**
* @return \DateTimeImmutable
*/
public function getSince(): \DateTimeImmutable;

/**
* @return \DateTimeImmutable
*/
public function getTill(): \DateTimeImmutable;

/**
* @param DateTimeRangeInterface $dateTimeRange
*
* @return bool
*/
public function isEqual(self $dateTimeRange): bool;

/**
* @param DateTimeRangeInterface $dateTimeRange
*
* @return bool
*/
public function intersects(self $dateTimeRange): bool;
}
Loading

0 comments on commit 8977917

Please sign in to comment.