Page MenuHomeDevCentral

D1641.id.diff
No OneTemporary

D1641.id.diff

diff --git a/omnitools/src/DateTime/Time.php b/omnitools/src/DateTime/Time.php
new file mode 100644
--- /dev/null
+++ b/omnitools/src/DateTime/Time.php
@@ -0,0 +1,123 @@
+<?php
+declare(strict_types=1);
+
+namespace Keruald\OmniTools\DateTime;
+
+use Keruald\OmniTools\Collections\Comparable;
+
+class Time implements Comparable {
+
+ private int $hours;
+ private int $minutes;
+
+ ///
+ /// Constants
+ ///
+
+ const MAX_HOURS = 24;
+
+ const MAX_MINUTES = 24 * 60;
+
+ ///
+ /// Constructors
+ ///
+
+ public function __construct (int $hours = 0, int $minutes = 0) {
+ $this->hours = $hours;
+ $this->minutes = $minutes;
+ }
+
+ public static function fromMinutes (int $minutes) : self {
+ if ($minutes < 0 || $minutes >= self::MAX_MINUTES) {
+ throw new \OutOfRangeException;
+ }
+
+ return (new Time)
+ ->setHours((int)($minutes / 60))
+ ->setMinutes($minutes % 60);
+ }
+
+ public static function parse (string $expression) : self {
+ $range = explode(":", $expression);
+
+ if (count($range) < 2 || count($range) > 3) {
+ throw new \InvalidArgumentException;
+ }
+
+ return new self((int)$range[0], (int)$range[1]);
+ }
+
+ ///
+ /// Getters and setters
+ ///
+
+ public function getHours () : int {
+ return $this->hours;
+ }
+
+ public function setHours (int $hours) : self {
+ $this->hours = $hours;
+
+ return $this;
+ }
+
+ public function getMinutes () : int {
+ return $this->minutes;
+ }
+
+ public function setMinutes (int $minutes) : self {
+ $this->minutes = $minutes;
+
+ return $this;
+ }
+
+ ///
+ /// Helper methods
+ ///
+
+ public function addMinutes (int $minutes) : self {
+ $totalMinutes = $this->toMinutes() + $minutes;
+
+ if ($totalMinutes < 0 || $totalMinutes >= self::MAX_MINUTES) {
+ throw new \OutOfRangeException;
+ }
+
+ $this->setHours((int)($totalMinutes / 60))
+ ->setMinutes($totalMinutes % 60);
+
+ return $this;
+ }
+
+ public function addHours (int $hours) : self {
+ $totalHours = $this->hours + $hours;
+
+ if ($totalHours >= self::MAX_HOURS) {
+ throw new \OutOfRangeException;
+ }
+
+ $this->setHours($totalHours);
+
+ return $this;
+ }
+
+ public function toMinutes () : int {
+ return $this->hours * 60 + $this->minutes;
+ }
+
+ public function __toString () : string {
+ return sprintf("%02d:%02d", $this->hours, $this->minutes);
+ }
+
+ ///
+ /// Comparable
+ ///
+
+ public function compareTo (object $other) : int {
+ if (!$other instanceof Time) {
+ throw new \InvalidArgumentException;
+ }
+
+ return $this->toMinutes() <=> $other->toMinutes();
+ }
+
+}
diff --git a/omnitools/src/DateTime/TimeRange.php b/omnitools/src/DateTime/TimeRange.php
new file mode 100644
--- /dev/null
+++ b/omnitools/src/DateTime/TimeRange.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Keruald\OmniTools\DateTime;
+
+class TimeRange {
+
+ private Time $start;
+ private Time $end;
+
+ ///
+ /// Constructors
+ ///
+
+ public function __construct (Time $start, Time $end) {
+ $this->start = $start;
+ $this->end = $end;
+
+ $this->normalize();
+ }
+
+ public static function fromDuration (Time $start,
+ int $hoursToAdd = 0,
+ int $minutesToAdd = 0) : self {
+ $end = clone $start;
+ $end
+ ->addHours($hoursToAdd)
+ ->addMinutes($minutesToAdd);
+
+ return new self($start, $end);
+ }
+
+ public static function parse (string $expression) : self {
+ $range = explode("-", $expression);
+
+ if (count($range) !== 2) {
+ throw new \InvalidArgumentException;
+ }
+
+ return new self(Time::Parse($range[0]), Time::Parse($range[1]));
+ }
+
+ ///
+ /// Getters and setters
+ ///
+
+ public function getStart () : Time {
+ return $this->start;
+ }
+
+ public function setStart (Time $start) : self {
+ $this->start = $start;
+
+ return $this->normalize();
+ }
+
+ public function getEnd () : Time {
+ return $this->end;
+ }
+
+ public function setEnd (Time $end) : self {
+ $this->end = $end;
+
+ return $this->normalize();
+ }
+
+ ///
+ /// Helper functions
+ ///
+
+ public function normalize () : self {
+ if ($this->start->compareTo($this->end) > 0) {
+ $swap = $this->end;
+ $this->end = $this->start;
+ $this->start = $swap;
+ }
+
+ return $this;
+ }
+
+ public function overlapsWith (TimeRange $other) : bool {
+ return $this->getEnd()->compareTo($other->getStart()) == 1
+ &&
+ $other->getEnd()->compareTo($this->getStart()) == 1;
+ }
+
+ public function countMinutes () : int {
+ return $this->end->toMinutes() - $this->start->toMinutes();
+ }
+
+}
diff --git a/omnitools/tests/DateTime/TimeRangeTest.php b/omnitools/tests/DateTime/TimeRangeTest.php
new file mode 100644
--- /dev/null
+++ b/omnitools/tests/DateTime/TimeRangeTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Keruald\OmniTools\Tests\DateTime;
+
+use Keruald\OmniTools\DateTime\Time;
+use Keruald\OmniTools\DateTime\TimeRange;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\TestCase;
+
+class TimeRangeTest extends TestCase {
+
+ private Time $start;
+
+ private Time $end;
+
+ private TimeRange $range;
+
+ protected function setUp () : void {
+ $this->start = new Time(8, 0);
+ $this->end = new Time(11, 36);
+
+ $this->range = new TimeRange($this->start, $this->end);
+ }
+
+ public function testGetStart () {
+ $this->assertEquals($this->start, $this->range->getStart());
+ }
+
+ public function testGetEnd () {
+ $this->assertEquals($this->end, $this->range->getEnd());
+ }
+
+ public function testNormalize () {
+ $range = new TimeRange($this->end, $this->start);
+ $this->assertEquals($this->start, $range->getStart());
+ }
+
+ public function testParse () {
+ $range = TimeRange::Parse("08:00-11:36");
+ $this->assertEquals($this->start, $range->getStart());
+ $this->assertEquals($this->end, $range->getEnd());
+ }
+
+ public function testFromDuration () {
+ $this->assertEquals(
+ $this->end,
+ TimeRange::fromDuration($this->start, 3, 36)->getEnd()
+ );
+ }
+
+ public function testDuration () {
+ $this->assertEquals(216, $this->range->countMinutes());
+ }
+
+ #[DataProvider('provideOverlappingRanges')]
+ public function testOverlap ($range1, $range2, $isOverlap) {
+ $this->assertEquals(
+ $isOverlap,
+ TimeRange::parse($range1)->overlapsWith(TimeRange::parse($range2))
+ );
+ }
+
+ ///
+ /// Data providers
+ ///
+
+ public static function provideOverlappingRanges() : iterable {
+
+ /**
+ * Two time ranges are overlapping if, THEIR LIMITS EXCLUDED,
+ * they don't have a common element.
+ *
+ * ]start1, end1[ ∩ ]start2, end2[ ≠ ø
+ */
+
+ yield ["09:00-11:00", "09:00-11:00", true];
+ yield ["09:00-11:00", "10:00-12:00", true];
+ yield ["10:00-12:00", "09:00-11:00", true];
+ yield ["09:00-12:00", "10:00-11:00", true];
+ yield ["10:00-11:00", "09:00-12:00", true];
+ yield ["09:00-10:00", "11:00-12:00", false];
+ yield ["11:00-12:00", "09:00-10:00", false];
+ yield ["09:00-10:00", "10:00-11:00", false];
+ yield ["10:00-11:00", "09:00-10:00", false];
+ yield ["10:00-10:00", "09:00-11:00", true];
+ yield ["09:00-11:00", "10:00-10:00", true];
+ yield ["09:00-09:00", "09:00-10:00", false];
+ yield ["10:00-10:00", "09:00-10:00", false];
+ yield ["09:00-10:00", "09:00-09:00", false];
+ yield ["09:00-10:00", "10:00-10:00", false];
+ yield ["09:00-09:00", "09:00-09:00", false];
+ }
+
+}
diff --git a/omnitools/tests/DateTime/TimeTest.php b/omnitools/tests/DateTime/TimeTest.php
new file mode 100644
--- /dev/null
+++ b/omnitools/tests/DateTime/TimeTest.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Keruald\OmniTools\Tests\DateTime;
+
+use Keruald\OmniTools\DateTime\Time;
+use PHPUnit\Framework\TestCase;
+
+class TimeTest extends TestCase {
+
+ private Time $time;
+
+ protected function setUp () : void {
+ $this->time = new Time(8, 6);
+ }
+
+ ///
+ /// Tests
+ ///
+
+ public function testGetHours () : void {
+ $this->assertSame(8, $this->time->getHours());
+ }
+
+ public function testGetMinutes () : void {
+ $this->assertSame(6, $this->time->getMinutes());
+ }
+
+ public function testSetHours () : void {
+ $this->time->setHours(9);
+ $this->assertSame(9, $this->time->getHours());
+ }
+
+ public function testSetMinutes () : void {
+ $this->time->setMinutes(7);
+ $this->assertSame(7, $this->time->getMinutes());
+ }
+
+ public function testAddMinutes () : void {
+ $this->time->addMinutes(7);
+ $this->assertSame(13, $this->time->getMinutes());
+ }
+
+ public function testAddMinutesWithHourOverlap () : void {
+ $this->time->addMinutes(55);
+ $this->assertSame(9, $this->time->getHours());
+ $this->assertSame(1, $this->time->getMinutes());
+ }
+
+ public function testAddMinutesWithDayOverlap () : void {
+ $this->expectException(\OutOfRangeException::class);
+ $this->time->addMinutes(2000);
+ }
+
+ public function testAddHours () : void {
+ $this->time->addHours(5);
+ $this->assertSame(13, $this->time->getHours());
+ }
+
+ public function testAddHoursWithDayOverlap () : void {
+ $this->expectException(\OutOfRangeException::class);
+ $this->time->addHours(16);
+ }
+
+ public function testToString () : void {
+ $this->assertSame("08:06", (string)$this->time);
+ }
+
+ public function testFromMinutes () : void {
+ $this->assertEquals($this->time, Time::fromMinutes(486));
+ }
+
+ public function testCompareTo () : void {
+ $this->assertEquals(0, $this->time->compareTo(new Time(8, 6)));
+ $this->assertEquals(-1, $this->time->compareTo(new Time(9, 0)));
+ $this->assertEquals(1, $this->time->compareTo(new Time(3, 0)));
+ }
+
+ public function testParse () {
+ $this->assertEquals($this->time, Time::Parse("08:06"));
+ $this->assertEquals($this->time, Time::Parse("08:06:00"));
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 18, 09:45 (19 h, 21 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2250120
Default Alt Text
D1641.id.diff (10 KB)

Event Timeline