<pman>Intégration de la branche dev74
This commit is contained in:
		
						commit
						5732c8f4bd
					
				| @ -218,10 +218,10 @@ class A { | ||||
|   static final function filter_f($dest): void { self::filter_if($dest, [cv::class, "f"]);} | ||||
|   static final function filter_pt($dest): void { self::filter_if($dest, [cv::class, "pt"]);} | ||||
|   static final function filter_pf($dest): void { self::filter_if($dest, [cv::class, "pf"]);} | ||||
|   static final function filter_equals($dest, $value): void { self::filter_if($dest, cv::equals($value)); } | ||||
|   static final function filter_not_equals($dest, $value): void { self::filter_if($dest, cv::not_equals($value)); } | ||||
|   static final function filter_same($dest, $value): void { self::filter_if($dest, cv::same($value)); } | ||||
|   static final function filter_not_same($dest, $value): void { self::filter_if($dest, cv::not_same($value)); } | ||||
|   static final function filter_equals($dest, $value): void { self::filter_if($dest, cv::Fequals($value)); } | ||||
|   static final function filter_not_equals($dest, $value): void { self::filter_if($dest, cv::Fnot_equals($value)); } | ||||
|   static final function filter_same($dest, $value): void { self::filter_if($dest, cv::Fsame($value)); } | ||||
|   static final function filter_not_same($dest, $value): void { self::filter_if($dest, cv::Fnot_same($value)); } | ||||
| 
 | ||||
|   #############################################################################
 | ||||
| 
 | ||||
|  | ||||
| @ -817,10 +817,10 @@ class cl { | ||||
|   static final function all_f(?array $array): bool { return self::all_if($array, [cv::class, "f"]);} | ||||
|   static final function all_pt(?array $array): bool { return self::all_if($array, [cv::class, "pt"]);} | ||||
|   static final function all_pf(?array $array): bool { return self::all_if($array, [cv::class, "pf"]);} | ||||
|   static final function all_equals(?array $array, $value): bool { return self::all_if($array, cv::equals($value)); } | ||||
|   static final function all_not_equals(?array $array, $value): bool { return self::all_if($array, cv::not_equals($value)); } | ||||
|   static final function all_same(?array $array, $value): bool { return self::all_if($array, cv::same($value)); } | ||||
|   static final function all_not_same(?array $array, $value): bool { return self::all_if($array, cv::not_same($value)); } | ||||
|   static final function all_equals(?array $array, $value): bool { return self::all_if($array, cv::Fequals($value)); } | ||||
|   static final function all_not_equals(?array $array, $value): bool { return self::all_if($array, cv::Fnot_equals($value)); } | ||||
|   static final function all_same(?array $array, $value): bool { return self::all_if($array, cv::Fsame($value)); } | ||||
|   static final function all_not_same(?array $array, $value): bool { return self::all_if($array, cv::Fnot_same($value)); } | ||||
| 
 | ||||
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ||||
| 
 | ||||
| @ -842,10 +842,10 @@ class cl { | ||||
|   static final function any_f(?array $array): bool { return self::any_if($array, [cv::class, "f"]);} | ||||
|   static final function any_pt(?array $array): bool { return self::any_if($array, [cv::class, "pt"]);} | ||||
|   static final function any_pf(?array $array): bool { return self::any_if($array, [cv::class, "pf"]);} | ||||
|   static final function any_equals(?array $array, $value): bool { return self::any_if($array, cv::equals($value)); } | ||||
|   static final function any_not_equals(?array $array, $value): bool { return self::any_if($array, cv::not_equals($value)); } | ||||
|   static final function any_same(?array $array, $value): bool { return self::any_if($array, cv::same($value)); } | ||||
|   static final function any_not_same(?array $array, $value): bool { return self::any_if($array, cv::not_same($value)); } | ||||
|   static final function any_equals(?array $array, $value): bool { return self::any_if($array, cv::Fequals($value)); } | ||||
|   static final function any_not_equals(?array $array, $value): bool { return self::any_if($array, cv::Fnot_equals($value)); } | ||||
|   static final function any_same(?array $array, $value): bool { return self::any_if($array, cv::Fsame($value)); } | ||||
|   static final function any_not_same(?array $array, $value): bool { return self::any_if($array, cv::Fnot_same($value)); } | ||||
| 
 | ||||
|   #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ||||
|    | ||||
| @ -876,10 +876,10 @@ class cl { | ||||
|   static final function filter_f(?array $array): ?array { return self::filter_if($array, [cv::class, "f"]);} | ||||
|   static final function filter_pt(?array $array): ?array { return self::filter_if($array, [cv::class, "pt"]);} | ||||
|   static final function filter_pf(?array $array): ?array { return self::filter_if($array, [cv::class, "pf"]);} | ||||
|   static final function filter_equals(?array $array, $value): ?array { return self::filter_if($array, cv::equals($value)); } | ||||
|   static final function filter_not_equals(?array $array, $value): ?array { return self::filter_if($array, cv::not_equals($value)); } | ||||
|   static final function filter_same(?array $array, $value): ?array { return self::filter_if($array, cv::same($value)); } | ||||
|   static final function filter_not_same(?array $array, $value): ?array { return self::filter_if($array, cv::not_same($value)); } | ||||
|   static final function filter_equals(?array $array, $value): ?array { return self::filter_if($array, cv::Fequals($value)); } | ||||
|   static final function filter_not_equals(?array $array, $value): ?array { return self::filter_if($array, cv::Fnot_equals($value)); } | ||||
|   static final function filter_same(?array $array, $value): ?array { return self::filter_if($array, cv::Fsame($value)); } | ||||
|   static final function filter_not_same(?array $array, $value): ?array { return self::filter_if($array, cv::Fnot_same($value)); } | ||||
| 
 | ||||
|   #############################################################################
 | ||||
| 
 | ||||
|  | ||||
| @ -49,7 +49,7 @@ class cv { | ||||
|    * contruire une fonction qui retourne vrai si on lui passe en argument une | ||||
|    * valeur égale à $value | ||||
|    */ | ||||
|   static final function equals($value): callable { | ||||
|   static final function Fequals($value): callable { | ||||
|     return function ($arg) use($value) { return $arg == $value; }; | ||||
|   } | ||||
| 
 | ||||
| @ -57,7 +57,7 @@ class cv { | ||||
|    * contruire une fonction qui retourne vrai si on lui passe en argument une | ||||
|    * valeur qui n'est pas égale à $value | ||||
|    */ | ||||
|   static final function not_equals($value): callable { | ||||
|   static final function Fnot_equals($value): callable { | ||||
|     return function ($arg) use($value) { return $arg != $value; }; | ||||
|   } | ||||
| 
 | ||||
| @ -65,7 +65,7 @@ class cv { | ||||
|    * contruire une fonction qui retourne vrai si on lui passe en argument une | ||||
|    * valeur strictement égale à $value | ||||
|    */ | ||||
|   static final function same($value): callable { | ||||
|   static final function Fsame($value): callable { | ||||
|     return function ($arg) use($value) { return $arg === $value; }; | ||||
|   } | ||||
| 
 | ||||
| @ -73,7 +73,7 @@ class cv { | ||||
|    * contruire une fonction qui retourne vrai si on lui passe en argument une | ||||
|    * valeur qui n'est pas strictement égale à $value | ||||
|    */ | ||||
|   static final function not_same($value): callable { | ||||
|   static final function Fnot_same($value): callable { | ||||
|     return function ($arg) use($value) { return $arg !== $value; }; | ||||
|   } | ||||
| 
 | ||||
| @ -231,6 +231,24 @@ class cv { | ||||
| 
 | ||||
|   #############################################################################
 | ||||
| 
 | ||||
|   /** | ||||
|    * tester l'égalité non stricte de deux valeurs, dans le cas où leurs types ne | ||||
|    * sont pas forcément cohérents: par exemple eq("1", 1) === true | ||||
|    * | ||||
|    * cette fonction est utile par exemple quand on veut comparer des données | ||||
|    * provenant d'une base de données: PHP a la facheuse tendance de retourner | ||||
|    * les nombres sous forme de chaines | ||||
|    */ | ||||
|   static final function equals($a, $b): bool { | ||||
|     # cette fonction existe parce que le type des valeurs retournées par la BDD
 | ||||
|     # n'est pas cohérent, "1" au lieu de 1 par exemple
 | ||||
|     if ($a === null) return $b === null; | ||||
|     elseif ($b === null) return $a === null; | ||||
|     elseif (is_scalar($a)) return strval($a) === strval($b); | ||||
|     # ne pas utiliser l'égalité stricte ici: ce sont des objets
 | ||||
|     else return $a == $b; | ||||
|   } | ||||
| 
 | ||||
|   /** retourner -1, 0 ou 1 en fonction de l'ordre relatif entre $a et $b */ | ||||
|   static final function compare($a, $b): int { | ||||
|     if ($a === $b) return 0; | ||||
|  | ||||
| @ -182,11 +182,27 @@ class Capacitor implements ITransactor { | ||||
|     return $this->storage->_delete($this->channel, $filter, $func, $args); | ||||
|   } | ||||
| 
 | ||||
|   function dbUpdate(array $update) { | ||||
|   function dbAll(array $query, ?array $params=null): iterable { | ||||
|     $primaryKeys = $this->channel->getPrimaryKeys(); | ||||
|     return $this->storage->db()->all(cl::merge([ | ||||
|       "select", | ||||
|       "from" => $this->getTableName(), | ||||
|     ], $query), $params, $primaryKeys); | ||||
|   } | ||||
| 
 | ||||
|   function dbOne(array $query, ?array $params=null): ?array { | ||||
|     return $this->storage->db()->one(cl::merge([ | ||||
|       "select", | ||||
|       "from" => $this->getTableName(), | ||||
|     ], $query), $params); | ||||
|   } | ||||
| 
 | ||||
|   /** @return int|false */ | ||||
|   function dbUpdate(array $query, ?array $params=null) { | ||||
|     return $this->storage->db()->exec(cl::merge([ | ||||
|       "update", | ||||
|       "table" => $this->getTableName(), | ||||
|     ], $update)); | ||||
|     ], $query), $params); | ||||
|   } | ||||
| 
 | ||||
|   function close(): void { | ||||
|  | ||||
| @ -414,6 +414,11 @@ class CapacitorChannel implements ITransactor { | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function initStorage(CapacitorStorage $storage): self { | ||||
|     new Capacitor($storage, $this); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function willUpdate(...$transactors): ITransactor { | ||||
|     return $this->capacitor->willUpdate(...$transactors); | ||||
|   } | ||||
| @ -508,8 +513,17 @@ class CapacitorChannel implements ITransactor { | ||||
|     return $this->capacitor->delete($filter, $func, $args); | ||||
|   } | ||||
| 
 | ||||
|   function dbUpdate(array $update) { | ||||
|     return $this->capacitor->dbUpdate($update); | ||||
|   function dbAll(array $query, ?array $params=null): iterable { | ||||
|     return $this->capacitor->dbAll($query, $params); | ||||
|   } | ||||
| 
 | ||||
|   function dbOne(array $query, ?array $params=null): ?array { | ||||
|     return $this->capacitor->dbOne($query, $params); | ||||
|   } | ||||
| 
 | ||||
|   /** @return int|false */ | ||||
|   function dbUpdate(array $query, ?array $params=null) { | ||||
|     return $this->capacitor->dbUpdate($query, $params); | ||||
|   } | ||||
| 
 | ||||
|   function close(): void { | ||||
|  | ||||
| @ -3,6 +3,7 @@ namespace nulib\db; | ||||
| 
 | ||||
| use nulib\A; | ||||
| use nulib\cl; | ||||
| use nulib\cv; | ||||
| use nulib\db\_private\_migration; | ||||
| use nulib\php\func; | ||||
| use nulib\ValueException; | ||||
| @ -479,8 +480,9 @@ abstract class CapacitorStorage { | ||||
|             # ne jamais mettre à jour la clé primaire
 | ||||
|             continue; | ||||
|           } | ||||
|           $pvalue = $praw[$col] ?? null; | ||||
|           if ($value !== $pvalue) $updates[$col] = $value; | ||||
|           if (!cv::equals($value, $praw[$col] ?? null)) { | ||||
|             $updates[$col] = $value; | ||||
|           } | ||||
|         } | ||||
|         if (count($updates) == 1 && array_key_first($updates) == "modified_") { | ||||
|           # si l'unique modification porte sur la date de modification, alors
 | ||||
|  | ||||
							
								
								
									
										12
									
								
								php/src/ext/utils.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								php/src/ext/utils.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| <?php | ||||
| namespace nulib\ext; | ||||
| 
 | ||||
| class utils { | ||||
|   /** générateur d'UUIDv4 "du pauvre" */ | ||||
|   static final function uuidgen(): string { | ||||
|     $data = random_bytes(16); | ||||
|     $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
 | ||||
|     $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
 | ||||
|     return vsprintf("%s%s-%s-%s-%s-%s%s%s", str_split(bin2hex($data), 4)); | ||||
|   } | ||||
| } | ||||
| @ -7,10 +7,10 @@ use nulib\php\func; | ||||
|  * Class Printer: wrapper autour d'une instance de {@link IContent} qui affiche | ||||
|  * le contenu au lieu de le retourner | ||||
|  * | ||||
|  * cette classe est conçue pour wrapper des objets complexes dont le contenu est | ||||
|  * contruit au fur et à mesure: ainsi, les objets peuvent être utilisés tels | ||||
|  * quels dans un contenu, ou alors être wrappés si on veut simplement les | ||||
|  * afficher | ||||
|  * cette classe est conçue pour wrapper des objets complexes dont le contenu | ||||
|  * peut être multiple et est contruit au fur et à mesure: ainsi, les objets | ||||
|  * peuvent être utilisés tels quels dans un contenu, ou alors être wrappés si | ||||
|  * on veut simplement les afficher | ||||
|  */ | ||||
| class Printer implements IPrintable { | ||||
|   function __construct(IContent $content) { | ||||
| @ -24,6 +24,9 @@ class Printer implements IPrintable { | ||||
|     c::write($content); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * afficher le contenu retourné par une méthode spécifique de $content | ||||
|    */ | ||||
|   function __call($name, $args) { | ||||
|     $content = func::call([$this->content, $name], ...$args); | ||||
|     c::write($content); | ||||
|  | ||||
| @ -139,6 +139,12 @@ class DateTime extends \DateTime { | ||||
|     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"); | ||||
| @ -156,6 +162,7 @@ class DateTime extends \DateTime { | ||||
|     "second" => "s", | ||||
|     "wday" => "N", | ||||
|     "wnum" => "W", | ||||
|     "nbsecs" => [self::class, "_nbsecs_format"], | ||||
|   ]; | ||||
|   const STRING_FORMATS = [ | ||||
|     "timezone" => "P", | ||||
| @ -166,14 +173,6 @@ class DateTime extends \DateTime { | ||||
|     "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. | ||||
| @ -282,7 +281,7 @@ 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(self::fix_z($Z)); | ||||
|       } | ||||
|       parent::__construct($datetime, $timezone); | ||||
| 
 | ||||
| @ -295,7 +294,7 @@ class DateTime extends \DateTime { | ||||
|       } 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)); | ||||
|       if ($Z !== null) $timezone ??= new DateTimeZone(self::fix_z($Z)); | ||||
|       parent::__construct($datetime, $timezone); | ||||
| 
 | ||||
|     } else { | ||||
| @ -308,6 +307,10 @@ class DateTime extends \DateTime { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function clone(): self { | ||||
|     return clone $this; | ||||
|   } | ||||
| 
 | ||||
|   function diff($target, $absolute=false): DateInterval { | ||||
|     return new DateInterval(parent::diff($target, $absolute)); | ||||
|   } | ||||
| @ -374,7 +377,7 @@ class DateTime extends \DateTime { | ||||
|   function __get($name) { | ||||
|     if (array_key_exists($name, self::INT_FORMATS)) { | ||||
|       $format = self::INT_FORMATS[$name]; | ||||
|       if (is_callable($format)) return $format($this); | ||||
|       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]; | ||||
|  | ||||
| @ -40,8 +40,8 @@ class Delay { | ||||
|     "s" => [1, 0], | ||||
|   ]; | ||||
| 
 | ||||
|   static function compute_dest(int $x, string $u, ?int $y, DateTime $from): array { | ||||
|     $dest = DateTime::clone($from); | ||||
|   static function compute_dest(int $x, string $u, ?int $y, ?DateTimeInterface $from): array { | ||||
|     $dest = DateTime::with($from)->clone(); | ||||
|     $yu = null; | ||||
|     switch ($u) { | ||||
|     case "w": | ||||
| @ -92,7 +92,7 @@ class Delay { | ||||
|   function __construct($delay, ?DateTimeInterface $from=null) { | ||||
|     if ($from === null) $from = new DateTime(); | ||||
|     if ($delay === "INF") { | ||||
|       $dest = DateTime::clone($from); | ||||
|       $dest = DateTime::with($from)->clone(); | ||||
|       $dest->add(new DateInterval("P9999Y")); | ||||
|       $repr = "INF"; | ||||
|     } elseif (is_int($delay)) { | ||||
|  | ||||
							
								
								
									
										10
									
								
								php/src/php/time/TODO.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								php/src/php/time/TODO.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| # nulib\php\time | ||||
| 
 | ||||
| * Date, DateTime sont immutables par défaut. par exemple, add() retourne un nouvel objet. | ||||
|   ajouter une version des méthodes qui modifie les données en place en les | ||||
|   préfixant de `_` e.g `_add()` | ||||
| 
 | ||||
|   en terme d'implémentation, dériver \DateTime pour supporter les modification | ||||
|   en place, bien que ce ne soit pas le fonctionnement par défaut | ||||
| 
 | ||||
| -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary | ||||
| @ -17,6 +17,11 @@ class str { | ||||
|     return $s; | ||||
|   } | ||||
| 
 | ||||
|   /** s'assure que $s est une chaine s'il n'est pas null */ | ||||
|   static final function ensure(&$s): void { | ||||
|     if ($s !== null) $s = self::with($s); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Retourner $s converti en chaine non nulle, ou "" si $s est fausse selon les | ||||
|    * règles de PHP | ||||
|  | ||||
							
								
								
									
										2
									
								
								php/tests/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								php/tests/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| *.db* | ||||
| *.cache | ||||
							
								
								
									
										1
									
								
								php/tests/db/sqlite/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								php/tests/db/sqlite/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| /capacitor.db* | ||||
| @ -29,7 +29,7 @@ class DateTest extends TestCase { | ||||
| 
 | ||||
|   function testClone() { | ||||
|     $date = self::dt("now"); | ||||
|     $clone = Date::clone($date); | ||||
|     $clone = $date->clone(); | ||||
|     self::assertInstanceOf(DateTime::class, $clone); | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -32,13 +32,17 @@ 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); | ||||
|     self::assertSame("20240405T091523Z", $date->YmdHMSZ); | ||||
|     self::assertSame("20240405T091523+04:00", $date->YmdHMSZ); | ||||
|   } | ||||
| 
 | ||||
|   function testClone() { | ||||
|     $date = self::dt("now"); | ||||
|     $clone = DateTime::clone($date); | ||||
|     $clone = $date->clone(); | ||||
|     self::assertInstanceOf(DateTime::class, $clone); | ||||
|   } | ||||
| 
 | ||||
| @ -51,7 +55,7 @@ class DateTimeTest extends TestCase { | ||||
|     self::assertSame("05/04/2024 00:00:00", strval(new DateTime("20240405"))); | ||||
|     self::assertSame("05/04/2024 00:00:00", strval(new DateTime("240405"))); | ||||
|     self::assertSame("05/04/2024 09:15:23", strval(new DateTime("20240405T091523"))); | ||||
|     self::assertSame("05/04/2024 09:15:23", strval(new DateTime("20240405T091523Z"))); | ||||
|     self::assertSame("05/04/2024 13:15:23", strval(new DateTime("20240405T091523Z"))); | ||||
|     self::assertSame("05/04/2024 09:15:23", strval(new DateTime("5/4/2024 9:15:23"))); | ||||
|     self::assertSame("05/04/2024 09:15:23", strval(new DateTime("5/4/2024 9.15.23"))); | ||||
|     self::assertSame("05/04/2024 09:15:00", strval(new DateTime("5/4/2024 9:15"))); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user