début date et datetime
This commit is contained in:
parent
eb988c35cc
commit
c3ef659bfe
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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" où 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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue