nulib/php/src_php/time/DateTime.php

189 lines
5.9 KiB
PHP

<?php
namespace nulib\php\time;
use DateTimeInterface;
use DateTimeZone;
use InvalidArgumentException;
/**
* Class DateTime: une date et une heure
*
* @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 DateTime extends \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])?$/';
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";
}
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",
];
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"],
];
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;
}
/**
* 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;
}
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);
} elseif (is_int($datetime)) {
parent::__construct("now", $timezone);
$this->setTimestamp($datetime);
} elseif (!is_string($datetime)) {
throw new InvalidArgumentException("datetime must be a string or an instance of DateTimeInterface");
} else {
$Y = $H = $Z = null;
if (preg_match(self::DMY_PATTERN, $datetime, $ms)) {
$Y = $ms[3] ?? null;
if ($Y !== null) $Y = self::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)) {
$Y = $ms[1];
if (strlen($Y) == 2) $Y = self::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)) {
$Y = $ms[3];
if ($Y !== null) $Y = self::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)) {
$Y = $ms[1];
if (strlen($Y) == 2) $Y = self::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("UTC");
}
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) {
if (array_key_exists($name, self::INT_FORMATS)) {
$format = self::INT_FORMATS[$name];
if (is_callable($format)) return $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");
}
}