Comment copier en profondeur un objet DateTime?

118
$date1 = $date2 = new DateTime();
$date2->add(new DateInterval('P3Y'));

Maintenant $date1et $date2contiennent la même date - dans trois ans. Je voudrais créer deux datetimes séparés, l'un qui est analysé à partir d'une chaîne et l'autre avec trois ans ajoutés. Actuellement, je l'ai piraté comme ceci:

$date2 =  new DateTime($date1->format(DateTime::ISO8601));

mais cela semble être un hack horrible. Existe-t-il un moyen "correct" de copier en profondeur un objet DateTime?

Billy ONeal
la source

Réponses:

171
$date1 = new DateTime();
$date2 = new DateTime();
$date2->add(new DateInterval('P3Y'));

Mettre à jour:

Si vous souhaitez copier plutôt que référencer un objet DT existant, utilisez clone, pas =.

$a = clone $b;

Amy B
la source
12
J'ai utilisé un nouveau DateTime dans l'exemple pour démontrer le point, mais pour l'instant, supposons que DateTime soit renvoyé par une API opaque que je ne peux pas simplement appeler à nouveau. Par exemple, j'ai une fonction qui gère les commandes qui retourne un DateTime qui est le moment où le client peut passer une commande. L'appel de la fonction pour créer une copie produit des effets secondaires que je ne veux pas.
Billy ONeal
Je ne l'ai pas testé en fait, mais il est mentionné sur php.net qu'il n'est disponible que pour PHP 5.3 et supérieur.
hugo der hungrige
@hugo: Oui, la classe DateTime nécessite PHP 5.3.
Billy ONeal
11
Juste au moment où je pensais maîtriser PHP, j'apprends un nouvel opérateur.
kr094
J'ai dû faire cela pour copier un objet Carbon existant dans une autre variable. Cela a fonctionné.
racl101
111

Clonez la date avec l' opérateur de clonage :

$date1 = new DateTime();
$date2 = clone $date1;
$date2->add(new DateInterval('P3Y'));

Les clones sont peu profonds par défaut, mais suffisamment profonds pour un DateTime. Dans vos propres objets, vous pouvez définir la __clone()méthode magique pour cloner les propriétés (c'est-à-dire les objets enfants) qu'il est logique de cloner lorsque l'objet parent change.

(Je ne sais pas pourquoi la documentation pense qu'un bon exemple de besoin de cloner un objet est GTK. Qui utilise GTK en PHP?)

rjmunro
la source
1
Merci pour la réponse, mais comment savez-vous que c'est assez profond pour DateTime? Quels attributs restent des références et lesquels sont copiés par valeur? Par exemple, je peux changer l'heure et le fuseau horaire et cela n'affectera pas le clone?
David le
1
@David: Je sais que c'est assez profond pour DateTime parce que je l'ai essayé, et cela a fonctionné pour moi. Je n'ai pas essayé de changer le fuseau horaire ou autre chose, juste l'heure et la date de base.
rjmunro
3
En utilisant Xdebug, var_dump ($ date1) signale qu'il contient 'date' => string, 'timezone_type' => int & 'timezone' => string. Puisqu'il ne semble pas contenir de tableaux ou d'objets, juste des scalaires de base, un clone superficiel devrait convenir.
CJ Dennis
46

PHP 5.5.0 a introduit DateTimeImmutable . Les méthodes d' ajout et de modification de cette classe renvoient de nouveaux objets.

$date1 = new DateTimeImmutable();
$date2 = $date1->add(new DateInterval('P3Y'));
Jardin Alexandre
la source
4
Notez que malheureusement, vous ne pouvez pas simplement échanger un DateTimeavec un DateTimeImmutable. Il y a au moins IntlDateFormatter::formatObjectça n'aime pas les immuables (retourne falseau lieu de la chaîne formatée).
user276648
1
Oh! Je n'ai jamais su que cela existait, même si j'en ai longtemps rêvé. et tout le chemin du retour en 5,5 ...
Ben
2
Comme certains noob, je viens de rencontrer un piège orienté objet en modifiant mon DateTimeobjet dans une boucle for: D Cela l'a bien résolu ...
Wilt
3
@ user276648 Ce bogue est maintenant corrigé dans php 7.1.5 php.net/ChangeLog-7.php#7.1.5
jontro
11

TLDR:

$date1 = new DateTime();
$date2 = (clone $date1)->modify('+3 years');

(La copie superficielle est enaugh - La copie profonde de DateTime n'a (actuellement) aucun sens )

Aussi simple que cela :)

Explication "php crée un objet datetime à partir d'un autre datetime":

  1. Le clonemot-clé fait une copie régulière superficielle - enaugh pour ce cas (pourquoi => voir ci-dessous)
  2. L'envelopper avec ()évalue l'expression renvoyant l'objet nouvellement créé enclone
  3. ->modify() est donc appelé et modifie le nouvel objet
  4. DateTime::modify(...) docs:

    Renvoie l'objet DateTime pour le chaînage de méthodes ou FALSE en cas d'échec.

  5. $date2contient maintenant le clone / copie nouvellement créé et modifié, tout en $date1restant inchangé

Pourquoi vous n'avez pas besoin de copier en profondeur ici:

La copie / clonage profond n'est nécessaire que lorsque vous devez copier des cibles de propriétés qui sont des références , mais ceci:

class TestDateTime extends DateTime{
  public function test(){
   //*this* way also outputs private variables if any...
   var_dump( get_object_vars($this) );    
  }
}
$test = (new TestDateTime())->test();

les sorties:

array(3) {
  ["date"]=>
  string(26) "2019-08-21 11:38:48.760390"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}

il n'y a donc pas de références, juste des types simples => pas besoin de copier en profondeur .

jave.web
la source
1

Vous devriez changer votre DateTimeenDateTimeImmutable

// from date time
$date = \DateTimeImmutable::createFromMutable($mutableDate)

alors vous pouvez appeler n'importe quelle méthode sur le DateTimesans vous soucier du changement

Hossein Shahdoost
la source
C'est vraiment une réponse à une question différente.
Billy ONeal
Je pourrais @BillyONeal n'a pas expliqué pleinement comment, mais cela est une solution à ce problème comme la source de ce problème est de savoir comment appeler la méthode addsur les date2changements de la valeur date1et il n'y a aucun moyen de copier la valeur de DateTimevariable , sauf si vous avez unDateTimeImmutable
Hossein Shahdoost