achse/php-math-interval

Implementation of mathematical Intervals in PHP. Creation of this was motivated by working with opening hours and other time intervals.

4.1.0 2019-07-07 12:32 UTC

README

Latest Stable Version Build Status Code Climate Coverage Status

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 and isFollowedByAtMidnight is available for testing continuity of DateTimeImmutable / DateTime intervals between days.

Available Types

Library contains intervals for those types:

  • Integer - classic int,
  • DateTimeImmutable and DateTime (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.