diff --git a/.gitignore b/.gitignore index c7eea7f..0c4e8bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ ### MacOs ### .DS_Store +### PhpStorm ### +/.idea + ### Composer ### composer.lock composer.phar diff --git a/composer.json b/composer.json index 2a3bd38..b399ba6 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,8 @@ "files": [ "src/classnames/function.php" ], "psr-4": { "ClassNames\\": "src/classnames", - "MergeCoverage\\": "src/merge-coverage" + "MergeCoverage\\": "src/merge-coverage", + "TimeOfDay\\": "src/time-of-day" } }, "require-dev": { diff --git a/phpunit.xml b/phpunit.xml index 8e37f4a..eccb588 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -13,6 +13,9 @@ src/classnames/Tests + + src/time-of-day/Tests + diff --git a/src/time-of-day/AbstractTime.php b/src/time-of-day/AbstractTime.php new file mode 100644 index 0000000..ed4ca54 --- /dev/null +++ b/src/time-of-day/AbstractTime.php @@ -0,0 +1,56 @@ + $hours || $hours > 23) => throw new \ValueError('Hour is not valid'), + (0 > $minutes || $minutes > 59) => throw new \ValueError('Minute is not valid'), + (0 > $seconds || $seconds > 59) => throw new \ValueError('Second is not valid'), + (0 > $milliseconds || $milliseconds > 999) => throw new \ValueError('Millisecond is not valid'), + default => 'Everything is ok' + }; + + return new static( + $hours * self::MILLISECONDS_IN_A_HOUR + + $minutes * self::MILLISECONDS_IN_A_MINUTE + + $seconds * 1000 + + $milliseconds + ); + } + + protected function hours(): int + { + return intdiv($this->counter, self::MILLISECONDS_IN_A_HOUR); + } + + protected function minutes(): int + { + $rest = $this->counter % self::MILLISECONDS_IN_A_HOUR; + return intdiv($rest, self::MILLISECONDS_IN_A_MINUTE); + } + + protected function seconds(): int + { + $rest = $this->counter % self::MILLISECONDS_IN_A_MINUTE; + return intdiv($rest, 1000); + } + + protected function milliseconds(): int + { + return $this->counter % 1000; + } +} \ No newline at end of file diff --git a/src/time-of-day/Duration.php b/src/time-of-day/Duration.php new file mode 100644 index 0000000..840267e --- /dev/null +++ b/src/time-of-day/Duration.php @@ -0,0 +1,29 @@ + $counter) { + throw new \ValueError('Milliseconds should be a positive integer'); + } + $this->counter = $counter; + } + + public function equals(Duration $other): bool + { + return $this->counter === $other->counter; + } + + public function isGreater(Duration $other): bool + { + return $this->counter > $other->counter; + } + + public function isLess(Duration $other): bool + { + return $this->counter < $other->counter; + } +} \ No newline at end of file diff --git a/src/time-of-day/Tests/DurationTest.php b/src/time-of-day/Tests/DurationTest.php new file mode 100644 index 0000000..bad994a --- /dev/null +++ b/src/time-of-day/Tests/DurationTest.php @@ -0,0 +1,93 @@ +isGreater($bar)); + self::assertTrue($bar->isLess($foo)); + } + + #[Test] + public function equalsComparison(): void + { + $foo = Duration::fromParts(12, 34 , 0); + $bar = Duration::fromParts(12, 34); + + self::assertTrue($foo->equals($bar)); + } + + #[Test] + public function equalsComparisonBetweenChildrenObjects(): void + { + $foo = Duration::fromParts(12, 34 , 0); + $bar = TimeOfDay::fromParts(12, 34); + + self::expectException(\TypeError::class); + self::assertTrue($foo->equals($bar)); + } +} \ No newline at end of file diff --git a/src/time-of-day/Tests/TimeOfDayTest.php b/src/time-of-day/Tests/TimeOfDayTest.php new file mode 100644 index 0000000..d252933 --- /dev/null +++ b/src/time-of-day/Tests/TimeOfDayTest.php @@ -0,0 +1,140 @@ +format('H:i:s'), $result->format('H:i:s')); + } + + #[Test] + public function plop(): void + { + $time = new TimeOfDay(45296000); + $result = $time->onDay(new \DateTimeImmutable('2000-01-01')); + + $expected = new \DateTimeImmutable('2000-01-01 12:34:56'); + + self::assertEquals($expected, $result); + } + + #[Test] + public function negativeMilliseconds(): void + { + $result = new TimeOfDay(-45296000); + self::assertEquals(new TimeOfDay(41104000), $result); + + $result = new TimeOfDay(-131696000); + self::assertEquals(new TimeOfDay(41104000), $result); + } + + #[Test] + public function comparison(): void + { + $foo = TimeOfDay::fromParts(12, 34 , 56); + $bar = TimeOfDay::fromParts(12); + + self::assertTrue($foo->isAfter($bar)); + self::assertTrue($bar->isBefore($foo)); + } + + #[Test] + public function equalsComparison(): void + { + $foo = TimeOfDay::fromParts(12, 34 , 0); + $bar = TimeOfDay::fromParts(12, 34); + + self::assertTrue($foo->equals($bar)); + } + + #[Test] + public function fromParts(): void + { + $result = TimeOfDay::fromParts(12); + $expected = new TimeOfDay(12 * 60 * 60 * 1000); + self::assertEquals($expected, $result); + + $result = TimeOfDay::fromParts(12,34); + $expected = new TimeOfDay(12 * 60 * 60 * 1000 + 34 * 60 * 1000); + self::assertEquals($expected, $result); + + $result = TimeOfDay::fromParts(12, 34 , 56 ); + $expected = new TimeOfDay(12 * 60 * 60 * 1000 + 34 * 60 * 1000 + 56 * 1000); + self::assertEquals($expected, $result); + + $result = TimeOfDay::fromParts(0); + $expected = new TimeOfDay(0); + self::assertEquals($expected, $result); + } + + #[Test] + public function hourNotValidTooBig(): void + { + self::expectException(\ValueError::class); + self::expectExceptionMessage('Hour is not valid'); + TimeOfDay::fromParts(24); + } + + #[Test] + public function hourNotValidTooSmall(): void + { + self::expectException(\ValueError::class); + self::expectExceptionMessage('Hour is not valid'); + TimeOfDay::fromParts(-3); + } + + #[Test] + public function minuteNotValid(): void + { + self::expectException(\ValueError::class); + self::expectExceptionMessage('Minute is not valid'); + TimeOfDay::fromParts(12, 60); + } + + #[Test] + public function secondNotValid(): void + { + self::expectException(\ValueError::class); + self::expectExceptionMessage('Second is not valid'); + TimeOfDay::fromParts(12, 34 , 60); + } + + #[Test] + public function passedSince(): void + { + $time = new TimeOfDay(45296000); + $duration = $time->passedSince(new TimeOfDay(44296000)); + + self::assertEquals(new Duration(1000000), $duration); + } + + #[Test] + public function was(): void + { + $time = new TimeOfDay(45296000); + $was = $time->was(new Duration(1000000)); + + self::assertEquals(new TimeOfDay(44296000), $was); + } + + #[Test] + public function willBe(): void + { + $time = new TimeOfDay(45296000); + $was = $time->willBe(new Duration(1000000)); + + self::assertEquals(new TimeOfDay(46296000), $was); + } +} diff --git a/src/time-of-day/TimeOfDay.php b/src/time-of-day/TimeOfDay.php new file mode 100644 index 0000000..3000365 --- /dev/null +++ b/src/time-of-day/TimeOfDay.php @@ -0,0 +1,57 @@ +counter = $rest + (0 > $rest ? self::MILLISECONDS_IN_A_DAY : 0); + } + + public function passedSince(TimeOfDay $other) + { + return new Duration($this->counter - $other->counter); + } + + public function was(Duration $duration): TimeOfDay + { + return new self($this->counter - $duration->counter); + } + + public function willBe(Duration $duration): TimeOfDay + { + return new self($this->counter + $duration->counter); + } + + public function equals(TimeOfDay $other): bool + { + return $this->counter === $other->counter; + } + + public function isBefore(TimeOfDay $other): bool + { + return $this->counter < $other->counter; + } + + public function isAfter(TimeOfDay $other): bool + { + return $this->counter > $other->counter; + } + + public function format(string $format): string + { + return $this->onDay(new \DateTimeImmutable())->format($format); + } + + public function onDay(\DateTimeImmutable $day): \DateTimeImmutable + { + return ($day)->setTime( + $this->hours(), + $this->minutes(), + $this->seconds(), + $this->milliseconds(), + ); + } +} \ No newline at end of file