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"); } }