From 3ef592aba5bc3f29c459ff82506a737be4e5d4e9 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Wed, 22 Oct 2025 15:40:38 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- src/php/time/Hour.php | 8 ++ src/php/time/Time.php | 155 ++++++++++++++++++++++-------------- tests/php/time/HourTest.php | 55 ++++++++++--- tests/php/time/TimeTest.php | 33 +++++++- 4 files changed, 178 insertions(+), 73 deletions(-) diff --git a/src/php/time/Hour.php b/src/php/time/Hour.php index 4f7f2d7..72be43e 100644 --- a/src/php/time/Hour.php +++ b/src/php/time/Hour.php @@ -5,4 +5,12 @@ class Hour extends Time { const UNIT = self::UNIT_MINUTES; const FORMAT = "%hh%M"; + + function format(?string $format=null): string { + $string = parent::format($format); + if (substr($string, -3) == "h00") { + $string = substr($string, 0, -2); + } + return $string; + } } diff --git a/src/php/time/Time.php b/src/php/time/Time.php index dae044f..a0d5591 100644 --- a/src/php/time/Time.php +++ b/src/php/time/Time.php @@ -3,6 +3,8 @@ namespace nulib\php\time; use DateTimeInterface; use nulib\exceptions; +use nulib\php\types\vbool; +use nulib\php\types\vint; /** * Class Time: une heure allant de 0h à 24h inclus. @@ -15,7 +17,10 @@ class Time { const UNIT_MINUTES = 60; const UNIT_SECONDS = 1; - const PATTERN = '/^(\d+):(\d+)(?::(\d+))?$/'; + const PATTERNS = [ + '/^(\d+)[:.h](\d+)(?:[:.](\d+))?$/', + '/^(\d+)h(?:(\d+)(?:[:.](\d+))?)?$/' + ]; /** @var string format par défaut pour l'affichage */ const FORMAT = "%H:%M:%S"; @@ -33,46 +38,59 @@ class Time { static function isa_time($time): bool { if ($time === null) return false; - if ($time instanceof Time) return true; + if ($time instanceof self) return true; if (is_int($time)) return true; - if (is_string($time)) return preg_match(self::PATTERN, $time); - #XXX supporter tous les formats + if (is_string($time)) { + foreach (self::PATTERNS as $pattern) { + if (preg_match($pattern, $time)) return true; + } + } + if ($time instanceof DateTimeInterface) return true; + if (is_array($time) && count($time) == 3) return true; return false; } - static function seconds_now(): int { + protected static function s_now(): int { $hms = date('His'); return intval(substr($hms, 0, 2)) * 3600 + intval(substr($hms, 2, 2)) * 60 + intval(substr($hms, 4, 2)); } - static function seconds_get($time, int $unit=self::UNIT_SECONDS): int { - if ($time instanceof self) { + protected static function s_get($time, int $unit=self::UNIT_SECONDS): int { + if ($time === null) { + return 0; + } elseif ($time instanceof self) { return $time->getSeconds(); } elseif (is_int($time)) { return $time * $unit; - } elseif (is_array($time)) { - [$h, $m, $s] = $time; - return $h * 3600 + $m * 60 + $s; - } elseif (is_string($time) && preg_match(self::PATTERN, $time, $ms)) { - $h = $ms[1]; - $m = $ms[2]; - $s = $ms[3] ?? 0; - return intval($h) * 3600 + intval($m) * 60 + intval($s); + } elseif (is_string($time)) { + $matched = false; + foreach (self::PATTERNS as $pattern) { + if (preg_match($pattern, $time, $ms)) { + $matched = true; + break; + } + } + if ($matched) { + $h = $ms[1]; + $m = $ms[2] ?? 0; + $s = $ms[3] ?? 0; + return intval($h) * 3600 + intval($m) * 60 + intval($s); + } } elseif ($time instanceof DateTimeInterface) { $hms = $time->format('His'); return intval(substr($hms, 0, 2)) * 3600 + intval(substr($hms, 2, 2)) * 60 + intval(substr($hms, 4, 2)); - } elseif ($time === null) { - return 0; - } else { - throw exceptions::invalid_value($time, "time"); + } elseif (is_array($time) && count($time) == 3) { + [$h, $m, $s] = $time; + return $h * 3600 + $m * 60 + $s; } + throw exceptions::invalid_value($time, "time"); } - static function seconds_adjust(int &$seconds, int $unit=self::UNIT_SECONDS, ?int $step=null): int { + protected static function s_adjust(int &$seconds, int $unit=self::UNIT_SECONDS, ?int $step=null): int { if ($step !== null) $unit *= $step; $adjust = $seconds % $unit; if ($seconds < 0) $adjust = -$adjust; @@ -80,46 +98,49 @@ class Time { return $seconds; } - static function seconds_wrap_start(int &$seconds): int { + protected static function s_wrap_start(int &$seconds): int { while ($seconds < 0) $seconds += 86400; if ($seconds >= 86400) $seconds %= 86400; return $seconds; } - static function seconds_wrap_end(int &$seconds): int { + protected static function s_wrap_end(int &$seconds): int { while ($seconds < 0) $seconds += 86400; if ($seconds > 86400) $seconds %= 86400; return $seconds; } /** - * @return int la valeur de l'unité en secondes, pour le constructeur et les - * méthodes {@link newu()}, {@link addu()} et {@link subu()} + * @var int la valeur de l'unité en secondes, pour le constructeur et les + * méthodes {@link setu()}, {@link addu()} et {@link subu()} */ - function UNIT(): int { - return static::UNIT; - } const UNIT = self::UNIT_SECONDS; + const UNIT = self::UNIT_SECONDS; /** - * @return int|null un nombre d'unité dont l'heure doit être multiple. par + * @var int|null un nombre d'unité dont l'heure doit être multiple. par * exemple, si l'unité est la minute, une valeur 5 permet d'avoir des heures * qui vont de 5 en 5 minutes (0h00, 0h05, 0h10, etc.) */ - protected function STEP(): ?int { - return static::STEP; - } const STEP = null; + const STEP = null; - /** @return bool s'il faut garder les heures dans la plage [0, 24h] */ - protected function WRAP(): bool { - return static::WRAP; - } const WRAP = true; + /** @var bool s'il faut garder les heures dans la plage [0, 24h] */ + const WRAP = true; - function __construct($time=null) { - if ($time === null) $seconds = self::seconds_now(); - else $seconds = self::seconds_get($time, $this->UNIT()); + function __construct($time=null, ?array $params=null) { + $this->unit = vint::with($params["unit"] ?? static::UNIT); + $this->step = vint::withn($params["step"] ?? self::STEP); + $this->wrap = vbool::with($params["wrap"] ?? self::WRAP); + if ($time === null) $seconds = self::s_now(); + else $seconds = self::s_get($time, $this->unit); $this->setSeconds($seconds); } + protected int $unit; + + protected ?int $step; + + protected bool $wrap; + protected int $seconds; function getSeconds(): int { @@ -135,13 +156,13 @@ class Time { * @return int le nombre de seconde effectif, après correction */ protected function setSeconds(?int $seconds): int { - $this->seconds = $seconds ?? self::seconds_now(); + $this->seconds = $seconds ?? self::s_now(); return $this->afterUpdate(); } protected function afterUpdate(): int { - self::seconds_adjust($this->seconds, $this->UNIT(), $this->STEP()); - if ($this->WRAP()) $this->wrapEnd(); + self::s_adjust($this->seconds, $this->unit, $this->step); + if ($this->wrap) $this->wrapEnd(); return $this->seconds; } @@ -150,7 +171,7 @@ class Time { * propice à l'utilisation comme borne inférieure d'une période */ function wrapStart(): self { - self::seconds_wrap_start($this->seconds); + self::s_wrap_start($this->seconds); return $this; } @@ -159,7 +180,7 @@ class Time { * propice à l'utilisation comme borne supérieure d'une période */ function wrapEnd(): self { - self::seconds_wrap_end($this->seconds); + self::s_wrap_end($this->seconds); return $this; } @@ -174,11 +195,13 @@ class Time { "%H", "%h", "%M", "%m", "%S", "%s", + "%%", ]; $replaces = [ sprintf("%02u", $h), strval($h), sprintf("%02u", $m), strval($m), sprintf("%02u", $s), strval($s), + "%", ]; return str_replace($searches, $replaces, $format); } @@ -189,39 +212,51 @@ class Time { /** @return int le nombre d'unités */ function getu(): int { - return intdiv($this->seconds, $this->UNIT()); + return intdiv($this->seconds, $this->unit); } /** créer une nouvelle heure avec le nombre d'unités spécifiées */ - function newu(int $count): self { - $this->setSeconds($count * $this->UNIT()); + function setu(int $count): self { + $this->setSeconds($count * $this->unit); return $this; } /** créer une nouvelle heure en ajoutant à cette heure le nombre d'unités spécifiées */ function addu(int $count=1): self { - $this->setSeconds($this->seconds + $count * $this->UNIT()); + $count *= $this->unit; + if ($this->step !== null) $count *= $this->step; + $this->setSeconds($this->seconds + $count); return $this; } /** créer une nouvelle heure en soustrayant à cette heure le nombre d'unités spécifiées */ function subu(int $count=1): self { - $this->setSeconds($this->seconds - $count * $this->UNIT()); + $count *= $this->unit; + if ($this->step !== null) $count *= $this->step; + $this->setSeconds($this->seconds - $count); return $this; } - /** créer une nouvelle heure en ajoutant à cette heure l'heure spécifiée */ - function add(?self $time): self { + /** forcer cette heure à l'heure spécifiée */ + function set($time): self { + if ($time === null) $seconds = self::s_now(); + else $seconds = self::with($time)->getSeconds(); + $this->setSeconds($seconds); + return $this; + } + + /** ajouter à cette heure l'heure spécifiée */ + function add($time): self { if ($time !== null) { - $this->setSeconds($this->seconds + $time->getSeconds()); + $this->setSeconds($this->seconds + self::with($time)->getSeconds()); } return $this; } - /** créer une nouvelle heure en soustrayant à cette heure l'heure spécifiée */ - function sub(?self $time): self { + /** soustraire à cette heure l'heure spécifiée */ + function sub($time): self { if ($time !== null) { - $this->setSeconds($this->seconds - $time->getSeconds()); + $this->setSeconds($this->seconds - self::with($time)->getSeconds()); } return $this; } @@ -230,24 +265,24 @@ class Time { * comparer avec l'heure spécifiée. retourner une valeur négative, égale à * zéro ou positive suivant le résultat de la comparaison */ - function compare(?self $time): int { + function compare($time): int { if ($time === null) return 1; - else return $this->seconds - $time->getSeconds(); + else return $this->seconds - self::with($time)->getSeconds(); } /** * tester si cette heure est avant ou égale à l'heure spécifiée */ - function before(?self $other): bool { + function before($other): bool { if ($other === null) return false; - else return $this->seconds <= $other->getSeconds(); + else return $this->seconds <= self::with($other)->getSeconds(); } /** * tester si cette heure est après ou égale à l'heure spécifiée */ - function after(?self $other): bool { + function after($other): bool { if ($other === null) return true; - else return $this->seconds >= $other->getSeconds(); + else return $this->seconds >= self::with($other)->getSeconds(); } } diff --git a/tests/php/time/HourTest.php b/tests/php/time/HourTest.php index 2117e68..1cb35b4 100644 --- a/tests/php/time/HourTest.php +++ b/tests/php/time/HourTest.php @@ -2,24 +2,59 @@ namespace nulib\php\time; use nulib\tests\TestCase; +use nulib\ValueException; class HourTest extends TestCase { + function testParse() { + self::assertSame("8h", strval(new Hour("08:00:00"))); + self::assertSame("8h", strval(new Hour("8:0"))); + self::assertSame("8h", strval(new Hour("8.0"))); + self::assertSame("8h", strval(new Hour("8h"))); + + self::assertSame("8h15", strval(new Hour("08:15:00"))); + self::assertSame("8h15", strval(new Hour("8:15"))); + self::assertSame("8h15", strval(new Hour("8.15"))); + self::assertSame("8h15", strval(new Hour("8h15"))); + + self::assertSame("8h15", strval(new Hour("08:15:23"))); + self::assertSame("8h15", strval(new Hour("8:15.23"))); + self::assertSame("8h15", strval(new Hour("8.15.23"))); + self::assertSame("8h15", strval(new Hour("8h15.23"))); + + self::assertSame("31h11", strval(new Hour("30:70:80", [ + "wrap" => false, + ]))); + self::assertSame("7h11", strval(new Hour("30:70:80"))); + + self::assertException(ValueException::class, function() { + return strval(new Hour("bad format")); + }); + } + function testFormat() { - self::assertSame("0h00", (new Hour(0))->format()); + self::assertSame("0h", (new Hour(0))->format()); self::assertSame("0h56", (new Hour(56))->format()); - self::assertSame("2h00", (new Hour(120))->format()); + self::assertSame("2h", (new Hour(120))->format()); self::assertSame("23h59", (new Hour(1439))->format()); - self::assertSame("24h00", (new Hour(1440))->format()); + self::assertSame("24h", (new Hour(1440))->format()); self::assertSame("0h01", (new Hour(1441))->format()); } function testStep() { - $h = new class extends Hour { - const STEP = 5; - }; - $h->newu(10); self::assertSame("0h10", strval($h)); - $h->newu(12); self::assertSame("0h10", strval($h)); - $h->newu(15); self::assertSame("0h15", strval($h)); - $h->newu(17); self::assertSame("0h15", strval($h)); + $h = new Hour(null, [ + "step" => 5, + ]); + $h->setu(10); self::assertSame("0h10", strval($h)); + $h->setu(12); self::assertSame("0h10", strval($h)); + $h->setu(15); self::assertSame("0h15", strval($h)); + $h->setu(17); self::assertSame("0h15", strval($h)); + + $h->set("8h"); + $h->addu(); self::assertSame("8h05", strval($h)); + $h->addu(); self::assertSame("8h10", strval($h)); + $h->addu(); self::assertSame("8h15", strval($h)); + $h->subu(); self::assertSame("8h10", strval($h)); + $h->subu(); self::assertSame("8h05", strval($h)); + $h->subu(); self::assertSame("8h", strval($h)); } } diff --git a/tests/php/time/TimeTest.php b/tests/php/time/TimeTest.php index a8800d8..998b0cb 100644 --- a/tests/php/time/TimeTest.php +++ b/tests/php/time/TimeTest.php @@ -2,9 +2,36 @@ namespace nulib\php\time; use nulib\tests\TestCase; +use nulib\ValueException; class TimeTest extends TestCase { - function testGetc() { + function testParse() { + self::assertSame("08:00:00", strval(new Time("08:00:00"))); + self::assertSame("08:00:00", strval(new Time("8:0"))); + self::assertSame("08:00:00", strval(new Time("8.0"))); + self::assertSame("08:00:00", strval(new Time("8h"))); + + self::assertSame("08:15:00", strval(new Time("08:15:00"))); + self::assertSame("08:15:00", strval(new Time("8:15"))); + self::assertSame("08:15:00", strval(new Time("8.15"))); + self::assertSame("08:15:00", strval(new Time("8h15"))); + + self::assertSame("08:15:23", strval(new Time("08:15:23"))); + self::assertSame("08:15:23", strval(new Time("8:15.23"))); + self::assertSame("08:15:23", strval(new Time("8.15.23"))); + self::assertSame("08:15:23", strval(new Time("8h15.23"))); + + self::assertSame("31:11:20", strval(new Time("30:70:80", [ + "wrap" => false, + ]))); + self::assertSame("07:11:20", strval(new Time("30:70:80"))); + + self::assertException(ValueException::class, function() { + return strval(new Time("bad format")); + }); + } + + function testGetu() { self::assertSame(0, (new Time(0))->getu()); self::assertSame(86399, (new Time(-1))->getu()); self::assertSame(1, (new Time(1))->getu()); @@ -13,7 +40,7 @@ class TimeTest extends TestCase { self::assertSame(0, (new Time(-86400))->getu()); } - function testAddc() { + function testAddu() { $t = new Time(0); $t->addu(0); self::assertSame(0, $t->getu()); $t->addu(10); self::assertSame(10, $t->getu()); @@ -24,7 +51,7 @@ class TimeTest extends TestCase { $t->addu(-86400); self::assertSame(0, $t->getu()); } - function testSubc() { + function testSubu() { $t = new Time(0); $t->subu(-86400); self::assertSame(86400, $t->getu()); $t->subu(86400); self::assertSame(0, $t->getu());