Date et DateTime sont immutable
This commit is contained in:
parent
810ead58d6
commit
c748fed388
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace nulib\php\time;
|
||||
|
||||
use DateTimeZone;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
* Class Date: une date
|
||||
@ -9,9 +9,14 @@ use DateTimeZone;
|
||||
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);
|
||||
protected function fix(DateTimeInterface $datetime): \DateTimeInterface {
|
||||
return $datetime->setTime(0, 0);
|
||||
}
|
||||
|
||||
/** @return MutableDate|self */
|
||||
function clone(bool $mutable=false): DateTimeInterface {
|
||||
if ($mutable) return new MutableDate($this);
|
||||
else return clone $this;
|
||||
}
|
||||
|
||||
function format($format=self::DEFAULT_FORMAT): string {
|
||||
|
@ -1,10 +1,10 @@
|
||||
<?php
|
||||
namespace nulib\php\time;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use InvalidArgumentException;
|
||||
use nulib\str;
|
||||
|
||||
/**
|
||||
* Class DateTime: une date et une heure
|
||||
@ -24,252 +24,78 @@ use nulib\str;
|
||||
* @property-read string $YmdHMS
|
||||
* @property-read string $YmdHMSZ
|
||||
*/
|
||||
class DateTime extends \DateTime {
|
||||
static function with($datetime): self {
|
||||
if ($datetime instanceof static) return $datetime;
|
||||
else return new static($datetime);
|
||||
}
|
||||
|
||||
static function withn($datetime): ?self {
|
||||
if ($datetime === null) return null;
|
||||
elseif ($datetime instanceof static) return $datetime;
|
||||
else return new static($datetime);
|
||||
}
|
||||
|
||||
static function ensure(&$datetime): void {
|
||||
$datetime = static::withn($datetime);
|
||||
}
|
||||
|
||||
const DMY_PATTERN = '/^(\d+)\/(\d+)(?:\/(\d+))?$/';
|
||||
const YMD_PATTERN = '/^((?:\d{2})?\d{2})(\d{2})(\d{2})$/';
|
||||
const DMYHIS_PATTERN = '/^(\d+)\/(\d+)(?:\/(\d+))? +(\d+)[h:.](\d+)(?:[:.](\d+))?$/';
|
||||
const YMDHISZ_PATTERN = '/^((?:\d{2})?\d{2})-?(\d{2})-?(\d{2})[tT](\d{2}):?(\d{2}):?(\d{2})?([zZ]|\+\d{2}:?\d{2})?$/';
|
||||
|
||||
|
||||
protected static function get_value(array $datetime, ?string $key, ?string $k, ?int $index) {
|
||||
$value = null;
|
||||
if ($value === null && $key !== null) $value = $datetime[$key] ?? null;
|
||||
if ($value === null && $k !== null) $value = $datetime[$k] ?? null;
|
||||
if ($value === null && $index !== null) $value = $datetime[$index] ?? null;
|
||||
return $value;
|
||||
}
|
||||
|
||||
private static function parse_int(array $datetime, ?string $key, ?string $k, ?int $index, ?int &$part, bool $required=true, ?int $default=null): bool {
|
||||
$part = null;
|
||||
$value = self::get_value($datetime, $key, $k, $index);
|
||||
if ($value === null) {
|
||||
if ($required && $default === null) return false;
|
||||
$part = $default;
|
||||
return true;
|
||||
}
|
||||
if (is_numeric($value)) {
|
||||
$part = intval($value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function parse_str(array $datetime, ?string $key, ?string $k, ?int $index, ?string &$part, bool $required = true, ?string $default=null): bool {
|
||||
$part = null;
|
||||
$value = self::get_value($datetime, $key, $k, $index);
|
||||
if ($value === null) {
|
||||
if ($required && $default === null) return false;
|
||||
$part = $default;
|
||||
return true;
|
||||
}
|
||||
if (is_string($value)) {
|
||||
$part = $value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function parse_array(array $datetime): ?array {
|
||||
if (!self::parse_int($datetime, "year", "Y", 0, $year)) return null;
|
||||
if (!self::parse_int($datetime, "month", "m", 1, $month)) return null;
|
||||
if (!self::parse_int($datetime, "day", "d", 2, $day)) return null;
|
||||
self::parse_int($datetime, "hour", "H", 3, $hour, false);
|
||||
self::parse_int($datetime, "minute", "M", 4, $minute, false);
|
||||
self::parse_int($datetime, "second", "S", 5, $second, false);
|
||||
self::parse_str($datetime, "tz", null, 6, $tz, false);
|
||||
return [$year, $month, $day, $hour, $minute, $second, $tz];
|
||||
}
|
||||
|
||||
static function isa($datetime): bool {
|
||||
if ($datetime === null) return false;
|
||||
if ($datetime instanceof DateTimeInterface) return true;
|
||||
if (is_int($datetime)) return true;
|
||||
if (is_string($datetime)) {
|
||||
return preg_match(self::DMY_PATTERN, $datetime) ||
|
||||
preg_match(self::YMD_PATTERN, $datetime) ||
|
||||
preg_match(self::DMYHIS_PATTERN, $datetime) ||
|
||||
preg_match(self::YMDHISZ_PATTERN, $datetime);
|
||||
}
|
||||
if (is_array($datetime)) {
|
||||
return self::parse_array($datetime) !== null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static function isa_datetime($datetime, bool $frOnly=false): bool {
|
||||
if ($datetime === null) return false;
|
||||
if ($datetime instanceof DateTimeInterface) return true;
|
||||
if (is_int($datetime)) return true;
|
||||
if (is_string($datetime)) {
|
||||
return preg_match(self::DMYHIS_PATTERN, $datetime) ||
|
||||
(!$frOnly && preg_match(self::YMDHISZ_PATTERN, $datetime));
|
||||
}
|
||||
if (is_array($datetime)) {
|
||||
return self::parse_array($datetime) !== null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static function isa_date($date, bool $frOnly=false): bool {
|
||||
if ($date === null) return false;
|
||||
if ($date instanceof DateTimeInterface) return true;
|
||||
if (is_int($date)) return true;
|
||||
if (is_string($date)) {
|
||||
return preg_match(self::DMY_PATTERN, $date) ||
|
||||
(!$frOnly && preg_match(self::YMD_PATTERN, $date));
|
||||
}
|
||||
if (is_array($date)) {
|
||||
return self::parse_array($date) !== null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** retourner le nombre de secondes depuis minuit */
|
||||
static function _nbsecs_format(\DateTime $datetime): string {
|
||||
[$h, $m, $s] = explode(",", $datetime->format("H,i,s"));
|
||||
return $h * 3600 + $m * 60 + $s;
|
||||
}
|
||||
|
||||
static function _YmdHMSZ_format(\DateTime $datetime): string {
|
||||
$YmdHMS = $datetime->format("Ymd\\THis");
|
||||
$Z = $datetime->format("P");
|
||||
if ($Z === "+00:00") $Z = "Z";
|
||||
return "$YmdHMS$Z";
|
||||
}
|
||||
class DateTime extends \DateTimeImmutable {
|
||||
use _TDateTime;
|
||||
|
||||
const DEFAULT_FORMAT = "d/m/Y H:i:s";
|
||||
const INT_FORMATS = [
|
||||
"year" => "Y",
|
||||
"month" => "m",
|
||||
"day" => "d",
|
||||
"hour" => "H",
|
||||
"minute" => "i",
|
||||
"second" => "s",
|
||||
"wday" => "N",
|
||||
"wnum" => "W",
|
||||
"nbsecs" => [self::class, "_nbsecs_format"],
|
||||
];
|
||||
const STRING_FORMATS = [
|
||||
"timezone" => "P",
|
||||
"datetime" => "d/m/Y H:i:s",
|
||||
"date" => "d/m/Y",
|
||||
"Ymd" => "Ymd",
|
||||
"YmdHMS" => "Ymd\\THis",
|
||||
"YmdHMSZ" => [self::class, "_YmdHMSZ_format"],
|
||||
];
|
||||
|
||||
/**
|
||||
* corriger une année à deux chiffres qui est située dans le passé et
|
||||
* retourner l'année à 4 chiffres.
|
||||
* $datetime est une spécification de date, avec ou sans fuseau horaire
|
||||
*
|
||||
* par exemple, si l'année courante est 2019, alors:
|
||||
* - fix_past_year('18') === '2018'
|
||||
* - fix_past_year('19') === '1919'
|
||||
* - fix_past_year('20') === '1920'
|
||||
*/
|
||||
static function fix_past_year(int $year): int {
|
||||
if ($year < 100) {
|
||||
$y = getdate(); $y = $y["year"];
|
||||
$r = $y % 100;
|
||||
$c = $y - $r;
|
||||
if ($year >= $r) $year += $c - 100;
|
||||
else $year += $c;
|
||||
}
|
||||
return $year;
|
||||
}
|
||||
|
||||
/**
|
||||
* corriger une année à deux chiffres et retourner l'année à 4 chiffres.
|
||||
* l'année charnière entre année passée et année future est 70
|
||||
* si $datetime ne contient pas de fuseau horaire, elle est réputée être dans
|
||||
* le fuseau $timezone, qui est le fuseau local par défaut
|
||||
*
|
||||
* par exemple, si l'année courante est 2019, alors:
|
||||
* - fix_past_year('18') === '2018'
|
||||
* - fix_past_year('19') === '2019'
|
||||
* - fix_past_year('20') === '2020'
|
||||
* - fix_past_year('69') === '2069'
|
||||
* - fix_past_year('70') === '1970'
|
||||
* - fix_past_year('71') === '1971'
|
||||
* si $datetime contient un fuseau horaire et si $forceTimezone est vrai,
|
||||
* alors $datetime est réexprimée dans le fuseau $timezone.
|
||||
* si $timezone est null alors $forceTimezone vaut vrai par défaut.
|
||||
*
|
||||
* datetime | timezone | forceTimezone | résultat
|
||||
* -----------------|----------|---------------|---------
|
||||
* datetime | any | any | datetime+localtz
|
||||
* datetime+origtz | null | null | datetime+localtz
|
||||
* datetime+origtz | null | true | datetime+localtz
|
||||
* datetime+origtz | null | false | datetime+origtz
|
||||
* datetime+origtz | newtz | null | datetime+origtz
|
||||
* datetime+origtz | newtz | false | datetime+origtz
|
||||
* datetime+origtz | newtz | true | datetime+newtz
|
||||
*/
|
||||
static function fix_any_year(int $year): int {
|
||||
if ($year < 100) {
|
||||
$y = intval(date("Y"));
|
||||
$r = $y % 100;
|
||||
$c = $y - $r;
|
||||
if ($year >= 70) $year += $c - 100;
|
||||
else $year += $c;
|
||||
}
|
||||
return $year;
|
||||
}
|
||||
|
||||
static function fix_z(?string $Z): ?string {
|
||||
$Z = strtoupper($Z);
|
||||
str::del_prefix($Z, "+");
|
||||
if (preg_match('/^\d{4}$/', $Z)) {
|
||||
$Z = substr($Z, 0, 2).":".substr($Z, 2);
|
||||
}
|
||||
if ($Z === "Z" || $Z === "UTC" || $Z === "00:00") return "UTC";
|
||||
return "GMT+$Z";
|
||||
}
|
||||
|
||||
function __construct($datetime="now", DateTimeZone $timezone=null, ?bool $forceLocalTimezone=null) {
|
||||
$forceLocalTimezone ??= $timezone === null;
|
||||
if ($forceLocalTimezone) {
|
||||
$setTimezone = $timezone;
|
||||
function __construct($datetime=null, DateTimeZone $timezone=null, ?bool $forceTimezone=null) {
|
||||
$resetTimezone = null;
|
||||
$forceTimezone ??= $timezone === null;
|
||||
if ($forceTimezone) {
|
||||
$resetTimezone = $timezone ?? new DateTimeZone(date_default_timezone_get());
|
||||
$timezone = null;
|
||||
}
|
||||
|
||||
$datetime ??= "now";
|
||||
if ($datetime instanceof \DateTimeInterface) {
|
||||
$timezone ??= $datetime->getTimezone();
|
||||
parent::__construct();
|
||||
$this->setTimestamp($datetime->getTimestamp());
|
||||
$this->setTimezone($timezone);
|
||||
if ($datetime instanceof DateTimeImmutable) {
|
||||
$datetime = \DateTime::createFromImmutable($datetime);
|
||||
} elseif ($datetime instanceof \DateTime) {
|
||||
$datetime = clone $datetime;
|
||||
#XXX sous PHP 8, remplacer les deux commandes ci-dessus par
|
||||
# DateTime::createFromInterface
|
||||
|
||||
} elseif (is_int($datetime)) {
|
||||
parent::__construct("now", $timezone);
|
||||
$this->setTimestamp($datetime);
|
||||
$timestamp = $datetime;
|
||||
$datetime = new \DateTime("now", $timezone);
|
||||
$datetime->setTimestamp($timestamp);
|
||||
|
||||
} elseif (is_string($datetime)) {
|
||||
$Y = $H = $Z = null;
|
||||
if (preg_match(self::DMY_PATTERN, $datetime, $ms)) {
|
||||
if (preg_match(_utils::DMY_PATTERN, $datetime, $ms)) {
|
||||
$Y = $ms[3] ?? null;
|
||||
if ($Y !== null) $Y = self::fix_any_year(intval($Y));
|
||||
if ($Y !== null) $Y = _utils::fix_any_year(intval($Y));
|
||||
else $Y = intval(date("Y"));
|
||||
$m = intval($ms[2]);
|
||||
$d = intval($ms[1]);
|
||||
} elseif (preg_match(self::YMD_PATTERN, $datetime, $ms)) {
|
||||
} elseif (preg_match(_utils::YMD_PATTERN, $datetime, $ms)) {
|
||||
$Y = $ms[1];
|
||||
if (strlen($Y) == 2) $Y = self::fix_any_year(intval($ms[1]));
|
||||
if (strlen($Y) == 2) $Y = _utils::fix_any_year(intval($ms[1]));
|
||||
else $Y = intval($Y);
|
||||
$m = intval($ms[2]);
|
||||
$d = intval($ms[3]);
|
||||
} elseif (preg_match(self::DMYHIS_PATTERN, $datetime, $ms)) {
|
||||
} elseif (preg_match(_utils::DMYHIS_PATTERN, $datetime, $ms)) {
|
||||
$Y = $ms[3];
|
||||
if ($Y !== null) $Y = self::fix_any_year(intval($Y));
|
||||
if ($Y !== null) $Y = _utils::fix_any_year(intval($Y));
|
||||
else $Y = intval(date("Y"));
|
||||
$m = intval($ms[2]);
|
||||
$d = intval($ms[1]);
|
||||
$H = intval($ms[4]);
|
||||
$M = intval($ms[5]);
|
||||
$S = intval($ms[6] ?? 0);
|
||||
} elseif (preg_match(self::YMDHISZ_PATTERN, $datetime, $ms)) {
|
||||
} elseif (preg_match(_utils::YMDHISZ_PATTERN, $datetime, $ms)) {
|
||||
$Y = $ms[1];
|
||||
if (strlen($Y) == 2) $Y = self::fix_any_year(intval($ms[1]));
|
||||
if (strlen($Y) == 2) $Y = _utils::fix_any_year(intval($ms[1]));
|
||||
else $Y = intval($Y);
|
||||
$m = intval($ms[2]);
|
||||
$d = intval($ms[3]);
|
||||
@ -281,73 +107,61 @@ class DateTime extends \DateTime {
|
||||
if ($Y !== null) {
|
||||
if ($H === null) $datetime = sprintf("%04d-%02d-%02d", $Y, $m, $d);
|
||||
else $datetime = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $Y, $m, $d, $H, $M, $S);
|
||||
if ($Z !== null) $timezone ??= new DateTimeZone(self::fix_z($Z));
|
||||
if ($Z !== null) $timezone = new DateTimeZone(_utils::fix_z($Z));
|
||||
}
|
||||
parent::__construct($datetime, $timezone);
|
||||
$datetime = new \DateTime($datetime, $timezone);
|
||||
|
||||
} elseif (is_array($datetime) && ($datetime = self::parse_array($datetime)) !== null) {
|
||||
$setTimezone = $timezone;
|
||||
$timezone = null;
|
||||
} elseif (is_array($datetime) && ($datetime = _utils::parse_array($datetime)) !== null) {
|
||||
[$Y, $m, $d, $H, $M, $S, $Z] = $datetime;
|
||||
if ($H === null && $M === null && $S === null) {
|
||||
$datetime = sprintf("%04d-%02d-%02d", $Y, $m, $d);
|
||||
} else {
|
||||
$datetime = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $Y, $m, $d, $H ?? 0, $M ?? 0, $S ?? 0);
|
||||
}
|
||||
if ($Z !== null) $timezone ??= new DateTimeZone(self::fix_z($Z));
|
||||
parent::__construct($datetime, $timezone);
|
||||
if ($Z !== null) $timezone = new DateTimeZone(_utils::fix_z($Z));
|
||||
$datetime = new \DateTime($datetime, $timezone);
|
||||
}
|
||||
|
||||
} else {
|
||||
if ($datetime instanceof DateTimeInterface) {
|
||||
if ($resetTimezone !== null) $datetime->setTimezone($resetTimezone);
|
||||
$datetime = $this->fix($datetime);
|
||||
parent::__construct($datetime->format("Y-m-d\\TH:i:s.uP"));
|
||||
} else {
|
||||
throw new InvalidArgumentException("datetime must be a string or an instance of DateTimeInterface");
|
||||
}
|
||||
|
||||
if ($forceLocalTimezone) {
|
||||
$setTimezone ??= new DateTimeZone(date_default_timezone_get());
|
||||
$this->setTimezone($setTimezone);
|
||||
}
|
||||
}
|
||||
|
||||
function clone(): self {
|
||||
return clone $this;
|
||||
protected function fix(DateTimeInterface $datetime): DateTimeInterface {
|
||||
return $datetime;
|
||||
}
|
||||
|
||||
function diff($target, $absolute=false): DateInterval {
|
||||
return new DateInterval(parent::diff($target, $absolute));
|
||||
}
|
||||
|
||||
function format($format=self::DEFAULT_FORMAT): string {
|
||||
if (array_key_exists($format, self::INT_FORMATS)) {
|
||||
$format = self::INT_FORMATS[$format];
|
||||
} elseif (array_key_exists($format, self::STRING_FORMATS)) {
|
||||
$format = self::STRING_FORMATS[$format];
|
||||
}
|
||||
if (is_callable($format)) return $format($this);
|
||||
else return \DateTime::format($format);
|
||||
/** @return MutableDateTime|self */
|
||||
function clone(bool $mutable=false): DateTimeInterface {
|
||||
if ($mutable) return new MutableDateTime($this);
|
||||
else return clone $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* modifier cet objet pour que l'heure soit à 00:00:00 ce qui le rend propice
|
||||
* à l'utilisation comme borne inférieure d'une période
|
||||
*/
|
||||
function wrapStartOfDay(): self {
|
||||
$this->setTime(0, 0);
|
||||
return $this;
|
||||
function getStartOfDay(): self {
|
||||
return new static($this->setTime(0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* modifier cet objet pour que l'heure soit à 23:59:59.999999 ce qui le rend
|
||||
* propice à l'utilisation comme borne supérieure d'une période
|
||||
*/
|
||||
function wrapEndOfDay(): self {
|
||||
$this->setTime(23, 59, 59, 999999);
|
||||
return $this;
|
||||
function getEndOfDay(): self {
|
||||
return new static($this->setTime(23, 59, 59, 999999));
|
||||
}
|
||||
|
||||
function getPrevDay(int $nbDays=1, bool $skipWeekend=false): self {
|
||||
if ($nbDays == 1 && $skipWeekend && $this->wday == 1) {
|
||||
$nbdays = 3;
|
||||
$nbDays = 3;
|
||||
}
|
||||
return static::with($this->sub(new \DateInterval("P${nbDays}D")));
|
||||
return new static($this->sub(new \DateInterval("P${nbDays}D")));
|
||||
}
|
||||
|
||||
function getNextDay(int $nbDays=1, bool $skipWeekend=false): self {
|
||||
@ -355,35 +169,6 @@ class DateTime extends \DateTime {
|
||||
$wday = $this->wday;
|
||||
if ($wday > 5) $nbDays = 8 - $this->wday;
|
||||
}
|
||||
return static::with($this->add(new \DateInterval("P${nbDays}D")));
|
||||
}
|
||||
|
||||
function getElapsedAt(?DateTime $now=null, ?int $resolution=null): string {
|
||||
return Elapsed::format_at($this, $now, $resolution);
|
||||
}
|
||||
|
||||
function getElapsedSince(?DateTime $now=null, ?int $resolution=null): string {
|
||||
return Elapsed::format_since($this, $now, $resolution);
|
||||
}
|
||||
|
||||
function getElapsedDelay(?DateTime $now=null, ?int $resolution=null): string {
|
||||
return Elapsed::format_delay($this, $now, $resolution);
|
||||
}
|
||||
|
||||
function __toString(): string {
|
||||
return $this->format();
|
||||
}
|
||||
|
||||
function __get($name) {
|
||||
if (array_key_exists($name, self::INT_FORMATS)) {
|
||||
$format = self::INT_FORMATS[$name];
|
||||
if (is_callable($format)) return intval($format($this));
|
||||
else return intval($this->format($format));
|
||||
} elseif (array_key_exists($name, self::STRING_FORMATS)) {
|
||||
$format = self::STRING_FORMATS[$name];
|
||||
if (is_callable($format)) return $format($this);
|
||||
else return $this->format($format);
|
||||
}
|
||||
throw new InvalidArgumentException("Unknown property $name");
|
||||
return new static($this->add(new \DateInterval("P${nbDays}D")));
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,7 @@ class Delay {
|
||||
"s" => [1, 0],
|
||||
];
|
||||
|
||||
static function compute_dest(int $x, string $u, ?int $y, ?DateTimeInterface $from): array {
|
||||
$dest = DateTime::with($from)->clone();
|
||||
protected static function compute_dest(int $x, string $u, ?int $y, MutableDateTime $dest): array {
|
||||
$yu = null;
|
||||
switch ($u) {
|
||||
case "w":
|
||||
@ -89,9 +88,9 @@ class Delay {
|
||||
}
|
||||
|
||||
function __construct($delay, ?DateTimeInterface $from=null) {
|
||||
if ($from === null) $from = new DateTime();
|
||||
$from = MutableDateTime::with($from)->clone(true);
|
||||
if ($delay === "INF") {
|
||||
$dest = DateTime::with($from)->clone();
|
||||
$dest = $from;
|
||||
$dest->add(new DateInterval("P9999Y"));
|
||||
$repr = "INF";
|
||||
} elseif (is_int($delay)) {
|
||||
@ -126,11 +125,11 @@ class Delay {
|
||||
[$this->dest, $this->repr] = $data;
|
||||
}
|
||||
|
||||
/** @var DateTime */
|
||||
/** @var MutableDateTime */
|
||||
protected $dest;
|
||||
|
||||
function getDest(): DateTime {
|
||||
return $this->dest;
|
||||
return $this->dest->clone();
|
||||
}
|
||||
|
||||
function addDuration($duration) {
|
||||
@ -157,7 +156,7 @@ class Delay {
|
||||
}
|
||||
|
||||
protected function _getDiff(?DateTimeInterface $now=null): \DateInterval {
|
||||
if ($now === null) $now = new DateTime();
|
||||
$now ??= new \DateTime();
|
||||
return $this->dest->diff($now);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace nulib\php\time;
|
||||
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
* Class Elapsed: durée entre deux événements
|
||||
*/
|
||||
@ -124,19 +126,19 @@ class Elapsed {
|
||||
return self::format_generic($prefix, $d, 0, 0);
|
||||
}
|
||||
|
||||
static function format_at(DateTime $start, ?DateTime $now=null, ?int $resolution=null): string {
|
||||
static function format_at(DateTimeInterface $start, ?DateTimeInterface $now=null, ?int $resolution=null): string {
|
||||
$now ??= new DateTime();
|
||||
$seconds = $now->getTimestamp() - $start->getTimestamp();
|
||||
return (new self($seconds, $resolution))->formatAt();
|
||||
}
|
||||
|
||||
static function format_since(DateTime $start, ?DateTime $now=null, ?int $resolution=null): string {
|
||||
static function format_since(DateTimeInterface $start, ?DateTimeInterface $now=null, ?int $resolution=null): string {
|
||||
$now ??= new DateTime();
|
||||
$seconds = $now->getTimestamp() - $start->getTimestamp();
|
||||
return (new self($seconds, $resolution))->formatSince();
|
||||
}
|
||||
|
||||
static function format_delay(DateTime $start, ?DateTime $now=null, ?int $resolution=null): string {
|
||||
static function format_delay(DateTimeInterface $start, ?DateTimeInterface $now=null, ?int $resolution=null): string {
|
||||
$now ??= new DateTime();
|
||||
$seconds = $now->getTimestamp() - $start->getTimestamp();
|
||||
return (new self($seconds, $resolution))->formatDelay();
|
||||
|
24
php/src/php/time/MutableDate.php
Normal file
24
php/src/php/time/MutableDate.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace nulib\php\time;
|
||||
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
|
||||
class MutableDate extends MutableDateTime {
|
||||
const DEFAULT_FORMAT = "d/m/Y";
|
||||
|
||||
function __construct($datetime="now", DateTimeZone $timezone=null) {
|
||||
parent::__construct($datetime, $timezone);
|
||||
$this->setTime(0, 0);
|
||||
}
|
||||
|
||||
/** @return Date|self */
|
||||
function clone(bool $mutable=false): DateTimeInterface {
|
||||
if ($mutable) return clone $this;
|
||||
else return new Date($this);
|
||||
}
|
||||
|
||||
function format($format=self::DEFAULT_FORMAT): string {
|
||||
return parent::format($format);
|
||||
}
|
||||
}
|
168
php/src/php/time/MutableDateTime.php
Normal file
168
php/src/php/time/MutableDateTime.php
Normal file
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
namespace nulib\php\time;
|
||||
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class DateTime: une date et une heure mutables
|
||||
*
|
||||
* @property-read int $year
|
||||
* @property-read int $month
|
||||
* @property-read int $day
|
||||
* @property-read int $hour
|
||||
* @property-read int $minute
|
||||
* @property-read int $second
|
||||
* @property-read int $wday
|
||||
* @property-read int $wnum
|
||||
* @property-read string $timezone
|
||||
* @property-read string $datetime
|
||||
* @property-read string $date
|
||||
* @property-read string $Ymd
|
||||
* @property-read string $YmdHMS
|
||||
* @property-read string $YmdHMSZ
|
||||
*/
|
||||
class MutableDateTime extends \DateTime {
|
||||
use _TDateTime;
|
||||
|
||||
const DEFAULT_FORMAT = "d/m/Y H:i:s";
|
||||
|
||||
/**
|
||||
* $datetime est une spécification de date, avec ou sans fuseau horaire
|
||||
*
|
||||
* si $datetime ne contient pas de fuseau horaire, elle est réputée être dans
|
||||
* le fuseau $timezone, qui est le fuseau local par défaut
|
||||
*
|
||||
* si $datetime contient un fuseau horaire et si $forceTimezone est vrai,
|
||||
* alors $datetime est réexprimée dans le fuseau $timezone.
|
||||
* si $timezone est null alors $forceTimezone vaut vrai par défaut.
|
||||
*
|
||||
* datetime | timezone | forceTimezone | résultat
|
||||
* -----------------|----------|---------------|---------
|
||||
* datetime | any | any | datetime+localtz
|
||||
* datetime+origtz | null | null | datetime+localtz
|
||||
* datetime+origtz | null | true | datetime+localtz
|
||||
* datetime+origtz | null | false | datetime+origtz
|
||||
* datetime+origtz | newtz | null | datetime+origtz
|
||||
* datetime+origtz | newtz | false | datetime+origtz
|
||||
* datetime+origtz | newtz | true | datetime+newtz
|
||||
*/
|
||||
function __construct($datetime=null, DateTimeZone $timezone=null, ?bool $forceTimezone=null) {
|
||||
$resetTimezone = null;
|
||||
$forceTimezone ??= $timezone === null;
|
||||
if ($forceTimezone) {
|
||||
$resetTimezone = $timezone ?? new DateTimeZone(date_default_timezone_get());
|
||||
$timezone = null;
|
||||
}
|
||||
|
||||
$datetime ??= "now";
|
||||
if ($datetime instanceof \DateTimeInterface) {
|
||||
$timezone ??= $datetime->getTimezone();
|
||||
parent::__construct();
|
||||
$this->setTimestamp($datetime->getTimestamp());
|
||||
$this->setTimezone($timezone);
|
||||
|
||||
} elseif (is_int($datetime)) {
|
||||
parent::__construct("now", $timezone);
|
||||
$this->setTimestamp($datetime);
|
||||
|
||||
} elseif (is_string($datetime)) {
|
||||
$Y = $H = $Z = null;
|
||||
if (preg_match(_utils::DMY_PATTERN, $datetime, $ms)) {
|
||||
$Y = $ms[3] ?? null;
|
||||
if ($Y !== null) $Y = _utils::fix_any_year(intval($Y));
|
||||
else $Y = intval(date("Y"));
|
||||
$m = intval($ms[2]);
|
||||
$d = intval($ms[1]);
|
||||
} elseif (preg_match(_utils::YMD_PATTERN, $datetime, $ms)) {
|
||||
$Y = $ms[1];
|
||||
if (strlen($Y) == 2) $Y = _utils::fix_any_year(intval($ms[1]));
|
||||
else $Y = intval($Y);
|
||||
$m = intval($ms[2]);
|
||||
$d = intval($ms[3]);
|
||||
} elseif (preg_match(_utils::DMYHIS_PATTERN, $datetime, $ms)) {
|
||||
$Y = $ms[3];
|
||||
if ($Y !== null) $Y = _utils::fix_any_year(intval($Y));
|
||||
else $Y = intval(date("Y"));
|
||||
$m = intval($ms[2]);
|
||||
$d = intval($ms[1]);
|
||||
$H = intval($ms[4]);
|
||||
$M = intval($ms[5]);
|
||||
$S = intval($ms[6] ?? 0);
|
||||
} elseif (preg_match(_utils::YMDHISZ_PATTERN, $datetime, $ms)) {
|
||||
$Y = $ms[1];
|
||||
if (strlen($Y) == 2) $Y = _utils::fix_any_year(intval($ms[1]));
|
||||
else $Y = intval($Y);
|
||||
$m = intval($ms[2]);
|
||||
$d = intval($ms[3]);
|
||||
$H = intval($ms[4]);
|
||||
$M = intval($ms[5]);
|
||||
$S = intval($ms[6] ?? 0);
|
||||
$Z = $ms[7] ?? null;
|
||||
}
|
||||
if ($Y !== null) {
|
||||
if ($H === null) $datetime = sprintf("%04d-%02d-%02d", $Y, $m, $d);
|
||||
else $datetime = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $Y, $m, $d, $H, $M, $S);
|
||||
if ($Z !== null) $timezone = new DateTimeZone(_utils::fix_z($Z));
|
||||
}
|
||||
parent::__construct($datetime, $timezone);
|
||||
|
||||
} elseif (is_array($datetime) && ($datetime = _utils::parse_array($datetime)) !== null) {
|
||||
[$Y, $m, $d, $H, $M, $S, $Z] = $datetime;
|
||||
if ($H === null && $M === null && $S === null) {
|
||||
$datetime = sprintf("%04d-%02d-%02d", $Y, $m, $d);
|
||||
} else {
|
||||
$datetime = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $Y, $m, $d, $H ?? 0, $M ?? 0, $S ?? 0);
|
||||
}
|
||||
if ($Z !== null) $timezone = new DateTimeZone(_utils::fix_z($Z));
|
||||
parent::__construct($datetime, $timezone);
|
||||
|
||||
} else {
|
||||
throw new InvalidArgumentException("datetime must be a string or an instance of DateTimeInterface");
|
||||
}
|
||||
|
||||
if ($resetTimezone !== null) $this->setTimezone($resetTimezone);
|
||||
}
|
||||
|
||||
/** @return DateTime|self */
|
||||
function clone(bool $mutable=false): DateTimeInterface {
|
||||
if ($mutable) return clone $this;
|
||||
else return new DateTime($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* modifier cet objet pour que l'heure soit à 00:00:00 ce qui le rend propice
|
||||
* à l'utilisation comme borne inférieure d'une période
|
||||
*/
|
||||
function setStartOfDay(): self {
|
||||
$this->setTime(0, 0);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* modifier cet objet pour que l'heure soit à 23:59:59.999999 ce qui le rend
|
||||
* propice à l'utilisation comme borne supérieure d'une période
|
||||
*/
|
||||
function setEndOfDay(): self {
|
||||
$this->setTime(23, 59, 59, 999999);
|
||||
return $this;
|
||||
}
|
||||
|
||||
function setPrevDay(int $nbDays=1, bool $skipWeekend=false): self {
|
||||
if ($nbDays == 1 && $skipWeekend && $this->wday == 1) {
|
||||
$nbDays = 3;
|
||||
}
|
||||
$this->sub(new \DateInterval("P${nbDays}D"));
|
||||
return $this;
|
||||
}
|
||||
|
||||
function setNextDay(int $nbDays=1, bool $skipWeekend=false): self {
|
||||
if ($nbDays == 1 && $skipWeekend) {
|
||||
$wday = $this->wday;
|
||||
if ($wday > 5) $nbDays = 8 - $this->wday;
|
||||
}
|
||||
$this->add(new \DateInterval("P${nbDays}D"));
|
||||
return $this;
|
||||
}
|
||||
}
|
103
php/src/php/time/_TDateTime.php
Normal file
103
php/src/php/time/_TDateTime.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
namespace nulib\php\time;
|
||||
|
||||
use DateTimeInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
trait _TDateTime {
|
||||
static function with($datetime): self {
|
||||
if ($datetime instanceof static) return $datetime;
|
||||
else return new static($datetime);
|
||||
}
|
||||
|
||||
static function withn($datetime): ?self {
|
||||
if ($datetime === null) return null;
|
||||
elseif ($datetime instanceof static) return $datetime;
|
||||
else return new static($datetime);
|
||||
}
|
||||
|
||||
static function ensure(&$datetime): void {
|
||||
$datetime = static::withn($datetime);
|
||||
}
|
||||
|
||||
static function isa($datetime): bool {
|
||||
if ($datetime === null) return false;
|
||||
if ($datetime instanceof DateTimeInterface) return true;
|
||||
if (is_int($datetime)) return true;
|
||||
if (is_string($datetime)) {
|
||||
return preg_match(_utils::DMY_PATTERN, $datetime) ||
|
||||
preg_match(_utils::YMD_PATTERN, $datetime) ||
|
||||
preg_match(_utils::DMYHIS_PATTERN, $datetime) ||
|
||||
preg_match(_utils::YMDHISZ_PATTERN, $datetime);
|
||||
}
|
||||
if (is_array($datetime)) return _utils::parse_array($datetime) !== null;
|
||||
return false;
|
||||
}
|
||||
|
||||
static function isa_datetime($datetime, bool $frOnly=false): bool {
|
||||
if ($datetime === null) return false;
|
||||
if ($datetime instanceof DateTimeInterface) return true;
|
||||
if (is_int($datetime)) return true;
|
||||
if (is_string($datetime)) {
|
||||
return preg_match(_utils::DMYHIS_PATTERN, $datetime) ||
|
||||
(!$frOnly && preg_match(_utils::YMDHISZ_PATTERN, $datetime));
|
||||
}
|
||||
if (is_array($datetime)) return _utils::parse_array($datetime) !== null;
|
||||
return false;
|
||||
}
|
||||
|
||||
static function isa_date($date, bool $frOnly=false): bool {
|
||||
if ($date === null) return false;
|
||||
if ($date instanceof DateTimeInterface) return true;
|
||||
if (is_int($date)) return true;
|
||||
if (is_string($date)) {
|
||||
return preg_match(_utils::DMY_PATTERN, $date) ||
|
||||
(!$frOnly && preg_match(_utils::YMD_PATTERN, $date));
|
||||
}
|
||||
if (is_array($date)) return _utils::parse_array($date) !== null;
|
||||
return false;
|
||||
}
|
||||
|
||||
function diff($target, $absolute=false): DateInterval {
|
||||
return new DateInterval(parent::diff($target, $absolute));
|
||||
}
|
||||
|
||||
function format($format=self::DEFAULT_FORMAT): string {
|
||||
if (array_key_exists($format, _utils::INT_FORMATS)) {
|
||||
$format = _utils::INT_FORMATS[$format];
|
||||
} elseif (array_key_exists($format, _utils::STRING_FORMATS)) {
|
||||
$format = _utils::STRING_FORMATS[$format];
|
||||
}
|
||||
if (is_callable($format)) return $format($this);
|
||||
else return parent::format($format);
|
||||
}
|
||||
|
||||
function __toString(): string {
|
||||
return $this->format();
|
||||
}
|
||||
|
||||
function __get($name) {
|
||||
if (array_key_exists($name, _utils::INT_FORMATS)) {
|
||||
$format = _utils::INT_FORMATS[$name];
|
||||
if (is_callable($format)) return intval($format($this));
|
||||
else return intval($this->format($format));
|
||||
} elseif (array_key_exists($name, _utils::STRING_FORMATS)) {
|
||||
$format = _utils::STRING_FORMATS[$name];
|
||||
if (is_callable($format)) return $format($this);
|
||||
else return $this->format($format);
|
||||
}
|
||||
throw new InvalidArgumentException("Unknown property $name");
|
||||
}
|
||||
|
||||
function getElapsedAt(?DateTimeInterface $now=null, ?int $resolution=null): string {
|
||||
return Elapsed::format_at($this, $now, $resolution);
|
||||
}
|
||||
|
||||
function getElapsedSince(?DateTimeInterface $now=null, ?int $resolution=null): string {
|
||||
return Elapsed::format_since($this, $now, $resolution);
|
||||
}
|
||||
|
||||
function getElapsedDelay(?DateTimeInterface $now=null, ?int $resolution=null): string {
|
||||
return Elapsed::format_delay($this, $now, $resolution);
|
||||
}
|
||||
}
|
147
php/src/php/time/_utils.php
Normal file
147
php/src/php/time/_utils.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
namespace nulib\php\time;
|
||||
|
||||
use nulib\str;
|
||||
|
||||
class _utils {
|
||||
const DMY_PATTERN = '/^(\d+)\/(\d+)(?:\/(\d+))?$/';
|
||||
const YMD_PATTERN = '/^((?:\d{2})?\d{2})(\d{2})(\d{2})$/';
|
||||
const DMYHIS_PATTERN = '/^(\d+)\/(\d+)(?:\/(\d+))? +(\d+)[h:.](\d+)(?:[:.](\d+))?$/';
|
||||
const YMDHISZ_PATTERN = '/^((?:\d{2})?\d{2})-?(\d{2})-?(\d{2})[tT](\d{2}):?(\d{2}):?(\d{2})?([zZ]|\+\d{2}:?\d{2})?$/';
|
||||
|
||||
/** retourner le nombre de secondes depuis minuit */
|
||||
static function _nbsecs_format(\DateTimeInterface $datetime): string {
|
||||
[$h, $m, $s] = explode(",", $datetime->format("H,i,s"));
|
||||
return $h * 3600 + $m * 60 + $s;
|
||||
}
|
||||
|
||||
static function _YmdHMSZ_format(\DateTimeInterface $datetime): string {
|
||||
$YmdHMS = $datetime->format("Ymd\\THis");
|
||||
$Z = $datetime->format("P");
|
||||
if ($Z === "+00:00") $Z = "Z";
|
||||
return "$YmdHMS$Z";
|
||||
}
|
||||
|
||||
const INT_FORMATS = [
|
||||
"year" => "Y",
|
||||
"month" => "m",
|
||||
"day" => "d",
|
||||
"hour" => "H",
|
||||
"minute" => "i",
|
||||
"second" => "s",
|
||||
"wday" => "N",
|
||||
"wnum" => "W",
|
||||
"nbsecs" => [self::class, "_nbsecs_format"],
|
||||
];
|
||||
const STRING_FORMATS = [
|
||||
"timezone" => "P",
|
||||
"datetime" => "d/m/Y H:i:s",
|
||||
"date" => "d/m/Y",
|
||||
"Ymd" => "Ymd",
|
||||
"YmdHMS" => "Ymd\\THis",
|
||||
"YmdHMSZ" => [self::class, "_YmdHMSZ_format"],
|
||||
];
|
||||
|
||||
/**
|
||||
* corriger une année à deux chiffres qui est située dans le passé et
|
||||
* retourner l'année à 4 chiffres.
|
||||
*
|
||||
* par exemple, si l'année courante est 2019, alors:
|
||||
* - fix_past_year('18') === '2018'
|
||||
* - fix_past_year('19') === '1919'
|
||||
* - fix_past_year('20') === '1920'
|
||||
*/
|
||||
static function fix_past_year(int $year): int {
|
||||
if ($year < 100) {
|
||||
$y = getdate();
|
||||
$y = $y["year"];
|
||||
$r = $y % 100;
|
||||
$c = $y - $r;
|
||||
if ($year >= $r) $year += $c - 100;
|
||||
else $year += $c;
|
||||
}
|
||||
return $year;
|
||||
}
|
||||
|
||||
/**
|
||||
* corriger une année à deux chiffres et retourner l'année à 4 chiffres.
|
||||
* l'année charnière entre année passée et année future est 70
|
||||
*
|
||||
* par exemple, si l'année courante est 2019, alors:
|
||||
* - fix_past_year('18') === '2018'
|
||||
* - fix_past_year('19') === '2019'
|
||||
* - fix_past_year('20') === '2020'
|
||||
* - fix_past_year('69') === '2069'
|
||||
* - fix_past_year('70') === '1970'
|
||||
* - fix_past_year('71') === '1971'
|
||||
*/
|
||||
static function fix_any_year(int $year): int {
|
||||
if ($year < 100) {
|
||||
$y = intval(date("Y"));
|
||||
$r = $y % 100;
|
||||
$c = $y - $r;
|
||||
if ($year >= 70) $year += $c - 100;
|
||||
else $year += $c;
|
||||
}
|
||||
return $year;
|
||||
}
|
||||
|
||||
static function fix_z(?string $Z): ?string {
|
||||
$Z = strtoupper($Z);
|
||||
str::del_prefix($Z, "+");
|
||||
if (preg_match('/^\d{4}$/', $Z)) {
|
||||
$Z = substr($Z, 0, 2) . ":" . substr($Z, 2);
|
||||
}
|
||||
if ($Z === "Z" || $Z === "UTC" || $Z === "00:00") return "UTC";
|
||||
return "GMT+$Z";
|
||||
}
|
||||
|
||||
static function get_value(array $datetime, ?string $key, ?string $k, ?int $index) {
|
||||
$value = null;
|
||||
if ($value === null && $key !== null) $value = $datetime[$key] ?? null;
|
||||
if ($value === null && $k !== null) $value = $datetime[$k] ?? null;
|
||||
if ($value === null && $index !== null) $value = $datetime[$index] ?? null;
|
||||
return $value;
|
||||
}
|
||||
|
||||
static function parse_int(array $datetime, ?string $key, ?string $k, ?int $index, ?int &$part, bool $required = true, ?int $default = null): bool {
|
||||
$part = null;
|
||||
$value = self::get_value($datetime, $key, $k, $index);
|
||||
if ($value === null) {
|
||||
if ($required && $default === null) return false;
|
||||
$part = $default;
|
||||
return true;
|
||||
}
|
||||
if (is_numeric($value)) {
|
||||
$part = intval($value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static function parse_str(array $datetime, ?string $key, ?string $k, ?int $index, ?string &$part, bool $required = true, ?string $default = null): bool {
|
||||
$part = null;
|
||||
$value = self::get_value($datetime, $key, $k, $index);
|
||||
if ($value === null) {
|
||||
if ($required && $default === null) return false;
|
||||
$part = $default;
|
||||
return true;
|
||||
}
|
||||
if (is_string($value)) {
|
||||
$part = $value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static function parse_array(array $datetime): ?array {
|
||||
if (!self::parse_int($datetime, "year", "Y", 0, $year)) return null;
|
||||
if (!self::parse_int($datetime, "month", "m", 1, $month)) return null;
|
||||
if (!self::parse_int($datetime, "day", "d", 2, $day)) return null;
|
||||
self::parse_int($datetime, "hour", "H", 3, $hour, false);
|
||||
self::parse_int($datetime, "minute", "M", 4, $minute, false);
|
||||
self::parse_int($datetime, "second", "S", 5, $second, false);
|
||||
self::parse_str($datetime, "tz", null, 6, $tz, false);
|
||||
return [$year, $month, $day, $hour, $minute, $second, $tz];
|
||||
}
|
||||
}
|
@ -29,13 +29,15 @@ class DateTest extends TestCase {
|
||||
|
||||
function testClone() {
|
||||
$date = self::dt("now");
|
||||
$clone = $date->clone(true);
|
||||
self::assertInstanceOf(MutableDate::class, $clone);
|
||||
$clone = $date->clone();
|
||||
self::assertInstanceOf(DateTime::class, $clone);
|
||||
self::assertInstanceOf(Date::class, $clone);
|
||||
}
|
||||
|
||||
function testConstruct() {
|
||||
$y = date("Y");
|
||||
self::assertSame("05/04/$y", strval(new Date("5/4")));
|
||||
$Y = date("Y");
|
||||
self::assertSame("05/04/$Y", strval(new Date("5/4")));
|
||||
self::assertSame("05/04/2024", strval(new Date("5/4/24")));
|
||||
self::assertSame("05/04/2024", strval(new Date("5/4/2024")));
|
||||
self::assertSame("05/04/2024", strval(new Date("05/04/2024")));
|
||||
|
@ -5,12 +5,8 @@ use DateTimeZone;
|
||||
use nulib\tests\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");
|
||||
$date = new DateTime("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));
|
||||
@ -31,24 +27,36 @@ class DateTimeTest extends TestCase {
|
||||
}
|
||||
|
||||
function testDateTimeZ() {
|
||||
$date = new DateTime("20240405T091523Z");
|
||||
self::assertSame("20240405T131523", $date->YmdHMS);
|
||||
self::assertSame("20240405T131523+04:00", $date->YmdHMSZ);
|
||||
# comme on spécifie la timezone, la valeur Z est ignorée
|
||||
$date = new DateTime("20240405T091523Z", new DateTimeZone("Indian/Reunion"));
|
||||
self::assertSame("20240405T091523", $date->YmdHMS);
|
||||
$date = new DateTime("20240405T091523");
|
||||
self::assertSame("20240405T091523+04:00", $date->YmdHMSZ);
|
||||
|
||||
$date = new DateTime("20240405T091523+02:00", null, null);
|
||||
self::assertSame("20240405T111523+04:00", $date->YmdHMSZ);
|
||||
$date = new DateTime("20240405T091523+02:00", null, true);
|
||||
self::assertSame("20240405T111523+04:00", $date->YmdHMSZ);
|
||||
$date = new DateTime("20240405T091523+02:00", null, false);
|
||||
self::assertSame("20240405T091523+02:00", $date->YmdHMSZ);
|
||||
|
||||
$newtz = new DateTimeZone("+06:00");
|
||||
$date = new DateTime("20240405T091523+02:00", $newtz, null);
|
||||
self::assertSame("20240405T091523+02:00", $date->YmdHMSZ);
|
||||
$date = new DateTime("20240405T091523+02:00", $newtz, false);
|
||||
self::assertSame("20240405T091523+02:00", $date->YmdHMSZ);
|
||||
$date = new DateTime("20240405T091523+02:00", $newtz, true);
|
||||
self::assertSame("20240405T131523+06:00", $date->YmdHMSZ);
|
||||
}
|
||||
|
||||
function testClone() {
|
||||
$date = self::dt("now");
|
||||
$date = new DateTime("now");
|
||||
$clone = $date->clone(true);
|
||||
self::assertInstanceOf(MutableDateTime::class, $clone);
|
||||
$clone = $date->clone();
|
||||
self::assertInstanceOf(DateTime::class, $clone);
|
||||
}
|
||||
|
||||
function testConstruct() {
|
||||
$y = date("Y");
|
||||
self::assertSame("05/04/$y 00:00:00", strval(new DateTime("5/4")));
|
||||
$Y = date("Y");
|
||||
self::assertSame("05/04/$Y 00:00:00", strval(new DateTime("5/4")));
|
||||
self::assertSame("05/04/2024 00:00:00", strval(new DateTime("5/4/24")));
|
||||
self::assertSame("05/04/2024 00:00:00", strval(new DateTime("5/4/2024")));
|
||||
self::assertSame("05/04/2024 00:00:00", strval(new DateTime("05/04/2024")));
|
||||
|
@ -5,72 +5,68 @@ use DateTimeZone;
|
||||
use nulib\tests\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");
|
||||
$from = new MutableDateTime("2024-04-05 09:15:23");
|
||||
|
||||
$delay = new Delay(10, $from);
|
||||
self::assertEquals(self::dt("2024-04-05 09:15:33"), $delay->getDest());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("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());
|
||||
self::assertEquals(new MutableDateTime("2024-04-07 05:00:00"), $delay->getDest());
|
||||
}
|
||||
|
||||
function testElapsed() {
|
||||
|
87
php/tests/php/time/MutableDateTest.php
Normal file
87
php/tests/php/time/MutableDateTest.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
namespace nulib\php\time;
|
||||
|
||||
use DateTimeZone;
|
||||
use nulib\tests\TestCase;
|
||||
|
||||
class MutableDateTest extends TestCase {
|
||||
protected static function dt(string $datetime): MutableDate {
|
||||
return new MutableDate($datetime, new DateTimeZone("Indian/Reunion"));
|
||||
}
|
||||
|
||||
function testDate() {
|
||||
$date = self::dt("2024-04-05 09:15:23");
|
||||
|
||||
self::assertSame("05/04/2024", $date->format());
|
||||
self::assertSame("05/04/2024", strval($date));
|
||||
self::assertSame(2024, $date->year);
|
||||
self::assertSame(4, $date->month);
|
||||
self::assertSame(5, $date->day);
|
||||
self::assertSame(0, $date->hour);
|
||||
self::assertSame(0, $date->minute);
|
||||
self::assertSame(0, $date->second);
|
||||
self::assertSame(5, $date->wday);
|
||||
self::assertSame(14, $date->wnum);
|
||||
self::assertSame("+04:00", $date->timezone);
|
||||
self::assertSame("05/04/2024 00:00:00", $date->datetime);
|
||||
self::assertSame("05/04/2024", $date->date);
|
||||
}
|
||||
|
||||
function testClone() {
|
||||
$date = self::dt("now");
|
||||
$clone = $date->clone(true);
|
||||
self::assertInstanceOf(MutableDate::class, $clone);
|
||||
$clone = $date->clone();
|
||||
self::assertInstanceOf(Date::class, $clone);
|
||||
}
|
||||
|
||||
function testConstruct() {
|
||||
$Y = date("Y");
|
||||
self::assertSame("05/04/$Y", strval(new MutableDate("5/4")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/24")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/2024")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("05/04/2024")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("20240405")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("240405")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("20240405T091523")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("20240405T091523Z")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/2024 9:15:23")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/2024 9.15.23")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/2024 9:15")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/2024 9.15")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/2024 9h15")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/2024 09:15:23")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/2024 09:15")));
|
||||
self::assertSame("05/04/2024", strval(new MutableDate("5/4/2024 09h15")));
|
||||
}
|
||||
|
||||
function testCompare() {
|
||||
$a = new MutableDate("10/02/2024");
|
||||
$b = new MutableDate("15/02/2024");
|
||||
$c = new MutableDate("20/02/2024");
|
||||
$a2 = new MutableDate("10/02/2024");
|
||||
$b2 = new MutableDate("15/02/2024");
|
||||
$c2 = new MutableDate("20/02/2024");
|
||||
|
||||
self::assertTrue($a == $a2);
|
||||
self::assertFalse($a === $a2);
|
||||
self::assertTrue($b == $b2);
|
||||
self::assertTrue($c == $c2);
|
||||
|
||||
self::assertFalse($a < $a);
|
||||
self::assertTrue($a < $b);
|
||||
self::assertTrue($a < $c);
|
||||
|
||||
self::assertTrue($a <= $a);
|
||||
self::assertTrue($a <= $b);
|
||||
self::assertTrue($a <= $c);
|
||||
|
||||
self::assertFalse($c > $c);
|
||||
self::assertTrue($c > $b);
|
||||
self::assertTrue($c > $a);
|
||||
|
||||
self::assertTrue($c >= $c);
|
||||
self::assertTrue($c >= $b);
|
||||
self::assertTrue($c >= $a);
|
||||
}
|
||||
}
|
121
php/tests/php/time/MutableDateTimeTest.php
Normal file
121
php/tests/php/time/MutableDateTimeTest.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace nulib\php\time;
|
||||
|
||||
use DateTimeZone;
|
||||
use nulib\tests\TestCase;
|
||||
|
||||
class MutableDateTimeTest extends TestCase {
|
||||
function testDateTime() {
|
||||
$date = new MutableDateTime("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::assertSame(2024, $date->year);
|
||||
self::assertSame(4, $date->month);
|
||||
self::assertSame(5, $date->day);
|
||||
self::assertSame(9, $date->hour);
|
||||
self::assertSame(15, $date->minute);
|
||||
self::assertSame(23, $date->second);
|
||||
self::assertSame(5, $date->wday);
|
||||
self::assertSame(14, $date->wnum);
|
||||
self::assertEquals("+04:00", $date->timezone);
|
||||
self::assertSame("05/04/2024 09:15:23", $date->datetime);
|
||||
self::assertSame("05/04/2024", $date->date);
|
||||
self::assertSame("20240405", $date->Ymd);
|
||||
self::assertSame("20240405T091523", $date->YmdHMS);
|
||||
self::assertSame("20240405T091523+04:00", $date->YmdHMSZ);
|
||||
}
|
||||
|
||||
function testDateTimeZ() {
|
||||
$date = new MutableDateTime("20240405T091523");
|
||||
self::assertSame("20240405T091523+04:00", $date->YmdHMSZ);
|
||||
|
||||
$date = new MutableDateTime("20240405T091523+02:00", null, null);
|
||||
self::assertSame("20240405T111523+04:00", $date->YmdHMSZ);
|
||||
$date = new MutableDateTime("20240405T091523+02:00", null, true);
|
||||
self::assertSame("20240405T111523+04:00", $date->YmdHMSZ);
|
||||
$date = new MutableDateTime("20240405T091523+02:00", null, false);
|
||||
self::assertSame("20240405T091523+02:00", $date->YmdHMSZ);
|
||||
|
||||
$newtz = new DateTimeZone("+06:00");
|
||||
$date = new MutableDateTime("20240405T091523+02:00", $newtz, null);
|
||||
self::assertSame("20240405T091523+02:00", $date->YmdHMSZ);
|
||||
$date = new MutableDateTime("20240405T091523+02:00", $newtz, false);
|
||||
self::assertSame("20240405T091523+02:00", $date->YmdHMSZ);
|
||||
$date = new MutableDateTime("20240405T091523+02:00", $newtz, true);
|
||||
self::assertSame("20240405T131523+06:00", $date->YmdHMSZ);
|
||||
}
|
||||
|
||||
function testClone() {
|
||||
$date = new MutableDateTime("now");
|
||||
$clone = $date->clone(true);
|
||||
self::assertInstanceOf(MutableDateTime::class, $clone);
|
||||
$clone = $date->clone();
|
||||
self::assertInstanceOf(DateTime::class, $clone);
|
||||
}
|
||||
|
||||
function testConstruct() {
|
||||
$Y = date("Y");
|
||||
self::assertSame("05/04/$Y 00:00:00", strval(new MutableDateTime("5/4")));
|
||||
self::assertSame("05/04/2024 00:00:00", strval(new MutableDateTime("5/4/24")));
|
||||
self::assertSame("05/04/2024 00:00:00", strval(new MutableDateTime("5/4/2024")));
|
||||
self::assertSame("05/04/2024 00:00:00", strval(new MutableDateTime("05/04/2024")));
|
||||
self::assertSame("05/04/2024 00:00:00", strval(new MutableDateTime("20240405")));
|
||||
self::assertSame("05/04/2024 00:00:00", strval(new MutableDateTime("240405")));
|
||||
self::assertSame("05/04/2024 09:15:23", strval(new MutableDateTime("20240405T091523")));
|
||||
self::assertSame("05/04/2024 13:15:23", strval(new MutableDateTime("20240405T091523Z")));
|
||||
self::assertSame("05/04/2024 09:15:23", strval(new MutableDateTime("5/4/2024 9:15:23")));
|
||||
self::assertSame("05/04/2024 09:15:23", strval(new MutableDateTime("5/4/2024 9.15.23")));
|
||||
self::assertSame("05/04/2024 09:15:00", strval(new MutableDateTime("5/4/2024 9:15")));
|
||||
self::assertSame("05/04/2024 09:15:00", strval(new MutableDateTime("5/4/2024 9.15")));
|
||||
self::assertSame("05/04/2024 09:15:00", strval(new MutableDateTime("5/4/2024 9h15")));
|
||||
self::assertSame("05/04/2024 09:15:23", strval(new MutableDateTime("5/4/2024 09:15:23")));
|
||||
self::assertSame("05/04/2024 09:15:00", strval(new MutableDateTime("5/4/2024 09:15")));
|
||||
self::assertSame("05/04/2024 09:15:00", strval(new MutableDateTime("5/4/2024 09h15")));
|
||||
}
|
||||
|
||||
function testCompare() {
|
||||
$a = new MutableDateTime("10/02/2024");
|
||||
$a2 = new MutableDateTime("10/02/2024 8:30");
|
||||
$a3 = new MutableDateTime("10/02/2024 15:45");
|
||||
$b = new MutableDateTime("15/02/2024");
|
||||
$b2 = new MutableDateTime("15/02/2024 8:30");
|
||||
$b3 = new MutableDateTime("15/02/2024 15:45");
|
||||
$x = new MutableDateTime("10/02/2024");
|
||||
$x2 = new MutableDateTime("10/02/2024 8:30");
|
||||
$x3 = new MutableDateTime("10/02/2024 15:45");
|
||||
|
||||
self::assertTrue($a == $x);
|
||||
self::assertFalse($a === $x);
|
||||
self::assertTrue($a2 == $x2);
|
||||
self::assertTrue($a3 == $x3);
|
||||
|
||||
self::assertFalse($a < $a);
|
||||
self::assertTrue($a < $a2);
|
||||
self::assertTrue($a < $a3);
|
||||
self::assertTrue($a < $b);
|
||||
self::assertTrue($a < $b2);
|
||||
self::assertTrue($a < $b3);
|
||||
|
||||
self::assertTrue($a <= $a);
|
||||
self::assertTrue($a <= $a2);
|
||||
self::assertTrue($a <= $a3);
|
||||
self::assertTrue($a <= $b);
|
||||
self::assertTrue($a <= $b2);
|
||||
self::assertTrue($a <= $b3);
|
||||
|
||||
self::assertTrue($b > $a);
|
||||
self::assertTrue($b > $a2);
|
||||
self::assertTrue($b > $a3);
|
||||
self::assertFalse($b > $b);
|
||||
self::assertFalse($b > $b2);
|
||||
self::assertFalse($b > $b3);
|
||||
|
||||
self::assertTrue($b >= $a);
|
||||
self::assertTrue($b >= $a2);
|
||||
self::assertTrue($b >= $a3);
|
||||
self::assertTrue($b >= $b);
|
||||
self::assertFalse($b >= $b2);
|
||||
self::assertFalse($b >= $b3);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user