début date et datetime

This commit is contained in:
Jephté Clain 2024-04-06 05:36:48 +04:00
parent eb988c35cc
commit c3ef659bfe
7 changed files with 422 additions and 0 deletions

20
src/php/time/Date.php Normal file
View File

@ -0,0 +1,20 @@
<?php
namespace nur\sery\php\time;
use DateTimeZone;
/**
* Class Date: une date
*/
class Date extends DateTime {
const DEFAULT_FORMAT = "d/m/Y";
function __construct($datetime="now", DateTimeZone $timezone=null) {
parent::__construct($datetime, $timezone);
$this->setTime(0, 0, 0, 0);
}
function format($format=self::DEFAULT_FORMAT): string {
return \DateTime::format($format);
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace nur\sery\php\time;
class DateInterval extends \DateInterval {
protected static function to_string(DateInterval $interval) {
$string = "P";
$y = $interval->y;
$m = $interval->m;
$d = $interval->d;
if ($y > 0) $string .= "${y}Y";
if ($m > 0) $string .= "${m}M";
if ($d > 0) $string .= "${d}D";
$string .= "T";
$h = $interval->h;
$i = $interval->i;
$s = $interval->s;
if ($h > 0) $string .= "${h}H";
if ($i > 0) $string .= "${i}M";
if ($s > 0 || $string == "PT") $string .= "${s}S";
if ($interval->invert == 1) $string = "-$string";
return $string;
}
function __construct($duration) {
if ($duration instanceof DateInterval) {
$this->y = $duration->y;
$this->m = $duration->m;
$this->d = $duration->d;
$this->h = $duration->h;
$this->i = $duration->i;
$this->s = $duration->s;
$this->invert = $duration->invert;
$this->days = $duration->days;
} else {
if (substr($duration, 0, 1) == "-") {
$duration = substr($duration, 1);
$invert = true;
} else {
$invert = false;
}
parent::__construct($duration);
if ($invert) $this->invert = 1;
}
}
function __toString(): string {
return self::to_string($this);
}
}

65
src/php/time/DateTime.php Normal file
View File

@ -0,0 +1,65 @@
<?php
namespace nur\sery\php\time;
use DateTimeInterface;
use DateTimeZone;
use InvalidArgumentException;
/**
* Class DateTime: une date et une heure
*
* @property-read string $year
* @property-read string $month
* @property-read string $day
* @property-read string $hour
* @property-read string $minute
* @property-read string $second
* @property-read string $timezone
*/
class DateTime extends \DateTime {
const DEFAULT_FORMAT = "d/m/Y H:i:s";
static function clone(DateTimeInterface $dateTime): self {
if ($dateTime instanceof static) return clone $dateTime;
$clone = new static();
$clone->setTimestamp($dateTime->getTimestamp());
$clone->setTimezone($dateTime->getTimezone());
return $clone;
}
function __construct($datetime="now", DateTimeZone $timezone=null) {
if ($datetime instanceof \DateTimeInterface) {
if ($timezone === null) $timezone = $datetime->getTimezone();
parent::__construct();
$this->setTimestamp($datetime->getTimestamp());
$this->setTimezone($timezone);
} else {
parent::__construct($datetime, $timezone);
}
}
function diff($target, $absolute=false): DateInterval {
return new DateInterval(parent::diff($target, $absolute));
}
function format($format=self::DEFAULT_FORMAT): string {
return \DateTime::format($format);
}
function __toString(): string {
return $this->format();
}
function __get($name) {
switch ($name) {
case "year": return $this->format("Y");
case "month": return $this->format("m");
case "day": return $this->format("d");
case "hour": return $this->format("H");
case "minute": return $this->format("i");
case "second": return $this->format("s");
case "timezone": return $this->format("P");
default: throw new InvalidArgumentException("Unknown property $name");
}
}
}

139
src/php/time/Delay.php Normal file
View File

@ -0,0 +1,139 @@
<?php
namespace nur\sery\php\time;
use DateTimeInterface;
use InvalidArgumentException;
/**
* Class Delay: une durée jusqu'à un moment destination. le moment destination
* est calculé à la création de l'objet en fonction du moment courant.
*
* La durée peut-être:
* - un nombre, qui est le nombre de secondes jusqu'au moment destination
* - une chaine de la forme "x[WDHMS]y" x et y sont des nombres et la lettre
* est l'unité de temps: W représente une semaine, D une journée, H une heure,
* M une minute et S une seconde.
*
* Dans cette dernière forme, le timestamp destination est calculé en ajoutant x
* unités. puis l'unité inférieure est ramenée à (0 + y)
* par exemple,
* - "1D5" signifie le lendemain à 5h (+1 jour, puis ramené à 5h)
* - "2H0" signifie au plus deux heures plus tard
* - "0W5" signifie à 5 le dimanche de la semaine en cours
*
* NB: la valeur y pour l'unité S est ignorée
*/
class Delay {
/** valeurs par défaut de x et y pour les unités supportées */
const DEFAULTS = [
"w" => [0, 5],
"d" => [1, 5],
"h" => [1, 0],
"m" => [1, 0],
"s" => [1, 0],
];
static function compute_dest(int $x, string $u, ?int $y, DateTime $from): array {
$dest = DateTime::clone($from);
switch ($u) {
case "w":
if ($x > 0) {
$x *= 7;
$dest->add(new \DateInterval("P${x}D"));
}
$w = 7 - intval($dest->format("w"));
$dest->add(new \DateInterval("P${w}D"));
$u = "h";
break;
case "d":
$dest->add(new \DateInterval("P${x}D"));
$u = "h";
break;
case "h":
$dest->add(new \DateInterval("PT${x}H"));
$u = "m";
break;
case "m":
$dest->add(new \DateInterval("PT${x}M"));
$u = "s";
break;
case "s":
$dest->add(new \DateInterval("PT${x}S"));
$u = null;
break;
}
if ($y !== null && $u !== null) {
$h = intval($dest->format("H"));
$m = intval($dest->format("i"));
switch ($u) {
case "h":
$dest->setTime($y, 0, 0, 0);
break;
case "m":
$dest->setTime($h, $y, 0, 0);
break;
case "s":
$dest->setTime($h, $m, $y, 0);
break;
}
}
$repr = $y !== null? "$x$y$y": "$x";
return [$dest, $repr];
}
function __construct($delay, ?DateTimeInterface $from=null) {
if ($from === null) $from = new DateTime();
if (is_int($delay)) {
[$dest, $repr] = self::compute_dest($delay, "s", null, $from);
} elseif (is_string($delay) && preg_match('/^\d+$/', $delay)) {
$x = intval($delay);
[$dest, $repr] = self::compute_dest($x, "s", null, $from);
} elseif (is_string($delay) && preg_match('/^(\d*)([wdhms])(\d*)$/i', $delay, $ms)) {
[$x, $u, $y] = [$ms[1], $ms[2], $ms[3]];
$u = strtolower($u);
$default = self::DEFAULTS[$u];
if ($x === "") $x = $default[0];
else $x = intval($x);
if ($y === "") $y = $default[1];
else $y = intval($y);
[$dest, $repr] = self::compute_dest($x, $u, $y, $from);
} else {
throw new InvalidArgumentException("invalid delay");
}
$this->dest = $dest;
$this->repr = $repr;
}
/** @var DateTime */
protected $dest;
function getDest(): DateTime {
return $this->dest;
}
/** @var string */
protected $repr;
function __toString(): string {
return $this->repr;
}
protected function _getDiff(?DateTimeInterface $now=null): \DateInterval {
if ($now === null) $now = new DateTime();
return $this->dest->diff($now);
}
/** retourner true si le délai imparti est écoulé */
function isElapsed(?DateTimeInterface $now=null): bool {
return $this->_getDiff($now)->invert == 0;
}
/**
* retourner l'intervalle entre le moment courant et la destination.
*
* l'intervalle est négatif si le délai n'est pas écoulé, positif sinon
*/
function getDiff(?DateTimeInterface $now=null): DateInterval {
return new DateInterval($this->_getDiff($now));
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace nur\sery\php\time;
use DateTimeZone;
use PHPUnit\Framework\TestCase;
class DateTest extends TestCase {
protected static function dt(string $datetime): Date {
return new Date($datetime, new DateTimeZone("Indian/Reunion"));
}
function testDate() {
$date = self::dt("2024-04-05 09:15:23");
self::assertEquals("05/04/2024", $date->format());
self::assertEquals("05/04/2024", strval($date));
self::assertEquals("2024", $date->year);
self::assertEquals("04", $date->month);
self::assertEquals("05", $date->day);
self::assertEquals("00", $date->hour);
self::assertEquals("00", $date->minute);
self::assertEquals("00", $date->second);
self::assertEquals("+04:00", $date->timezone);
}
function testClone() {
$date = self::dt("now");
$clone = Date::clone($date);
self::assertInstanceOf(DateTime::class, $clone);
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace nur\sery\php\time;
use DateTimeZone;
use nur\sery\php\time\Date;
use nur\sery\php\time\DateTime;
use PHPUnit\Framework\TestCase;
class DateTimeTest extends TestCase {
protected static function dt(string $datetime): DateTime {
return new DateTime($datetime, new DateTimeZone("Indian/Reunion"));
}
function testDateTime() {
$date = self::dt("2024-04-05 09:15:23");
self::assertEquals("05/04/2024 09:15:23", $date->format());
self::assertEquals("05/04/2024 09:15:23", strval($date));
self::assertEquals("2024", $date->year);
self::assertEquals("04", $date->month);
self::assertEquals("05", $date->day);
self::assertEquals("09", $date->hour);
self::assertEquals("15", $date->minute);
self::assertEquals("23", $date->second);
self::assertEquals("+04:00", $date->timezone);
}
function testClone() {
$date = self::dt("now");
$clone = DateTime::clone($date);
self::assertInstanceOf(DateTime::class, $clone);
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace nur\sery\php\time;
use DateTimeZone;
use nur\sery\php\time\DateTime;
use nur\sery\php\time\Delay;
use nur\t\TestCase;
class DelayTest extends TestCase {
protected static function dt(string $datetime): DateTime {
return new DateTime($datetime, new DateTimeZone("Indian/Reunion"));
}
function testDelay() {
$from = self::dt("2024-04-05 09:15:23");
$delay = new Delay(10, $from);
self::assertEquals(self::dt("2024-04-05 09:15:33"), $delay->getDest());
$delay = new Delay("10", $from);
self::assertEquals(self::dt("2024-04-05 09:15:33"), $delay->getDest());
$delay = new Delay("10s", $from);
self::assertEquals(self::dt("2024-04-05 09:15:33"), $delay->getDest());
$delay = new Delay("s", $from);
self::assertEquals(self::dt("2024-04-05 09:15:24"), $delay->getDest());
$delay = new Delay("5m", $from);
self::assertEquals(self::dt("2024-04-05 09:20:00"), $delay->getDest());
$delay = new Delay("5m0", $from);
self::assertEquals(self::dt("2024-04-05 09:20:00"), $delay->getDest());
$delay = new Delay("5m2", $from);
self::assertEquals(self::dt("2024-04-05 09:20:02"), $delay->getDest());
$delay = new Delay("m", $from);
self::assertEquals(self::dt("2024-04-05 09:16:00"), $delay->getDest());
$delay = new Delay("5h", $from);
self::assertEquals(self::dt("2024-04-05 14:00:00"), $delay->getDest());
$delay = new Delay("5h0", $from);
self::assertEquals(self::dt("2024-04-05 14:00:00"), $delay->getDest());
$delay = new Delay("5h2", $from);
self::assertEquals(self::dt("2024-04-05 14:02:00"), $delay->getDest());
$delay = new Delay("h", $from);
self::assertEquals(self::dt("2024-04-05 10:00:00"), $delay->getDest());
$delay = new Delay("5d", $from);
self::assertEquals(self::dt("2024-04-10 05:00:00"), $delay->getDest());
$delay = new Delay("5d2", $from);
self::assertEquals(self::dt("2024-04-10 02:00:00"), $delay->getDest());
$delay = new Delay("5d0", $from);
self::assertEquals(self::dt("2024-04-10 00:00:00"), $delay->getDest());
$delay = new Delay("d", $from);
self::assertEquals(self::dt("2024-04-06 05:00:00"), $delay->getDest());
$delay = new Delay("2w", $from);
self::assertEquals(self::dt("2024-04-21 05:00:00"), $delay->getDest());
$delay = new Delay("2w2", $from);
self::assertEquals(self::dt("2024-04-21 02:00:00"), $delay->getDest());
$delay = new Delay("2w0", $from);
self::assertEquals(self::dt("2024-04-21 00:00:00"), $delay->getDest());
$delay = new Delay("w", $from);
self::assertEquals(self::dt("2024-04-07 05:00:00"), $delay->getDest());
}
function testElapsed() {
$delay = new Delay(5);
sleep(2);
self::assertFalse($delay->isElapsed());
sleep(5);
self::assertTrue($delay->isElapsed());
}
}