Statistics
66440
20
3
108d
Badges
Tags
utils

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.

  • 4.1.0 4.1.0

    • Code style, internal code polishing
    • No functional change
  • 4.0.0 4.0.0

    • Dropped deprecated DateTime support. Use DateTimeImmutable instead. (BC Break!)
    • Upgraded PHPStan, types objects needs to now implement ToStringConvertible interface. (BC Break!)
    • Integer class renames to IntegerValue because its confusing with interal integer type. (BC Break!)
    • Dropped support for PHP 7.0, minimum version is now 7.1. (BC Break!)
  • 3.0.3 3.0.3

    • Fixed SingleDay midnight method. Thx @K0nias
  • 3.0.2 3.0.2

    • Structure and namespaces changed (BC break).
    • API of some interval methods also changes (BC break).
      • Renames methos getIntersection(...) -> just: intersection(...).
      • For example, intersection return empty interval instead of NULL.
    • All objects are strictly immutable (BC break). Methods like setState and setElement from bundary were removed. New withXy methods that returns new object were introduced.
    • Most of classes are now final (BC break).
    • Support for timezones added.
    • DateTime is deprecated in favor of DateTimeInterval (which was added as new type of interval).
    • Dependencies o Nette Packages were dropped.
    • In SingleDate Intervals the "last second of the day" issue was solved. You can use (00:00:00 00:00:00) format - open from both side to create this 24 hours long interval.
  • 3.0.1-rc1 3.0.1-rc1

    Fixies for timezones.

  • 3.0.0-rc1 3.0.0-rc1

  • 2.0.0 2.0.0

  • 1.0.1 Nette 2.4 update dependecies

Is this addon outdated? Did you find an issue? Please report it.

Componette Componette admin@componette.com