petrhejna / Php Math Interval
What it does?
Filters, calendars, opening hours, activity logs, ... all where since-till appears you can find intervals. Many times it looks like this:
public function foo(DateTimeImmutable $sice, DateTimeImmutable $till) { ... }
In such an implementation you have to repeat chceck $since > $till
all over again. Also you have to write your own tooling to work with intervals.
Library php-math-interval
brings Value Objects for representation and powerful tooling to easy manipulation.
public function foo(DateTimeImmutableInterval $interval) { ... }
It's awesome!
- Heavily tested,
- all object are immutable,
- code is clean and predictable,
- interval is modeled as mathematical entity.
composer require achse/php-math-interval
Create an interval
Via factories (most simple):
$interval = DateTimeImmutableIntervalFactory::create(
new \DateTimeImmutable('2015-10-07T12:00:00+02:00'),
Boundary::CLOSED,
new \DateTimeImmutable('2015-10-07T14:00:00+02:00'),
Boundary::OPENED
);
echo (string)$interval; // [2015-10-07T12:00:00+02:00, 2015-10-07T14:00:00+02:00)
Directly via constructors:
use Achse\Math\Interval\DateTimeImmutable\DateTimeImmutable; // We need object implementing IComparable
$left = new IntegerBoundary(new DateTimeImmutable('2015-10-07T12:00:00+02:00'), Boundary::CLOSED);
$right = new IntegerBoundary(new DateTimeImmutable('2015-10-07T14:00:00+02:00'), Boundary::OPENED);
$interval = new DateTimeImmutableInterval($left, $right);
Parsed from string (used for tests mostly):
$interval = DateTimeImmutableIntervalStringParser::parse('[2015-01-01T05:00:00+02:00, 2015-01-01T10:00:00+02:00)');
Methods
Interval object provides powerful tooling for operations with intervals:
use Achse\Math\Interval\Integer\IntegerIntervalStringParser as Parser;
- Test if interval contains element
$interval = Parser::parse('[1, 2]');
$interval->isContainingElement(new Integer(2)); // true
$interval->isContainingElement(new Integer(3)); // false
- Get intersection between two intervals
// (1, 3) ∩ (2, 4) ⟺ (2, 3)
Parser::parse('(1, 3)')->intersection(Parser::parse('(2, 4)')); // (2, 3)
- Get intersection between two not overlapping intervals => empty interval -- test via Interval::isEmpty()
// (1, 2) ∩ (3, 4) ⟺ empty
Parser::parse('(1, 2)')->intersection(Parser::parse('(1, 4)')); // (1, 1) -- empty interval
- Get union of two intervals
// Union of overlapping intervals
Parser::parse('[1, 3]')->union(Parser::parse('[2, 4]')); // ['[1, 4]']
// Union of not overlapping intervals
Parser::parse('[1, 2)')->union(Parser::parse('[3, 4]')); // ['[1, 4]']
// Union of two distant intervals is array of those two intervals
Parser::parse('[1, 2]')->union(Parser::parse('[3, 4]')); // ['[1, 2], [3, 4]']
- Diff two intervals
// [1, 4] \ [2, 3]
Parser::parse('[1, 4]')->difference(Parser::parse('[2, 3]')); // ['[1,2)', '(3, 4]']
- Test if one interval contains the other
// [1, 4] contains [2, 3]
Parser::parse('[1, 4]')->isContaining(Parser::parse('[2, 3]')); // true
// [2, 3] NOT contains [1, 4]
Parser::parse('[2, 3]')->isContaining(Parser::parse('[1, 4]')); // false
- Does one interval overlap other one from right
Parser::parse('[1, 2]')->isOverlappedFromRightBy(Parser::parse('[2, 3]')); // true
Parser::parse('[2, 3]')->isOverlappedFromRightBy(Parser::parse('[1, 2]')); // false
// (1, 2) ~ [2, 3]
Parser::parse('(1, 2)')->isOverlappedFromRightBy(Parser::parse('[2, 3]')); // false
- Test if two intervals collides (not empty intersection)
Parser::parse('[2, 3]')->isColliding(Parser::parse('[1, 2]')); // true
Parser::parse('[1, 2]')->isColliding(Parser::parse('(2, 3)')); // false
-
isFollowedByWithPrecision
andisFollowedByAtMidnight
is available for testing continuity ofDateTimeImmutable
/DateTime
intervals between days.
Available Types
Library contains intervals for those types:
-
Integer
- classic int, -
DateTimeImmutable
andDateTime
(I strongly advise you to use Immutable only), -
SingeDayTime
- represents "clock-time" of one day. From [00:00:00 one day to 00:00:00) another.
Creating your own type
Interval
(its Boundary
) can contains any type that implements IComparable
, but if you want
to have type-hinting you may want to write your own XyInterval
and XyBoundary
class
and probably also Factory
classes.