¿Cómo hago una copia profunda de un objeto DateTime?

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

Ahora $date1y $date2contienen la misma fecha, dentro de tres años. Me gustaría crear dos fechas y horas separadas, una que se analiza a partir de una cadena y otra con tres años agregados. Actualmente lo he pirateado así:

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

pero eso parece un truco horrendo. ¿Existe una forma "correcta" de realizar una copia profunda de un objeto DateTime?

Billy ONeal
fuente

Respuestas:

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

Actualizar:

Si desea copiar en lugar de hacer referencia a un objeto DT existente, utilice clone, no =.

$a = clone $b;

Amy B
fuente
12
Usé un nuevo DateTime en el ejemplo para demostrar el punto, pero por ahora supongo que DateTime se devuelve desde alguna API opaca a la que no puedo volver a llamar. Por ejemplo, tengo una función que maneja pedidos que devuelve un DateTime, que es cuando el cliente puede realizar un pedido a continuación. Llamar a la función para crear una copia produce efectos secundarios que no quiero.
Billy ONeal
En realidad, no lo he probado, pero en php.net se menciona que solo está disponible para PHP 5.3 y versiones posteriores.
hugo der hungrige
@hugo: Sí, la clase DateTime requiere PHP 5.3.
Billy ONeal
11
Justo cuando pensaba que entendía PHP, me enteré de un nuevo operador.
kr094
Tuve que hacer esto para copiar un objeto Carbon existente a otra variable. Esto funcionó.
racl101
111

Clone la fecha con el operador de clonación :

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

Los clones son superficiales por defecto, pero lo suficientemente profundos para un DateTime. En sus propios objetos, puede definir el __clone()método mágico para clonar las propiedades (es decir, los objetos secundarios) que tienen sentido para ser clonadas cuando cambia el objeto principal.

(No estoy seguro de por qué la documentación cree que un buen ejemplo de la necesidad de clonar un objeto es GTK. ¿Quién usa GTK en PHP?)

rjmunro
fuente
1
Gracias por la respuesta, pero ¿cómo sabes que es lo suficientemente profundo para DateTime? ¿Qué atributos siguen siendo referencias y cuáles se copian por valor? Por ejemplo, ¿puedo cambiar la hora y la zona horaria y no afectará al clon?
David
1
@David: Sé que es lo suficientemente profundo para DateTime porque lo probé y funcionó para mí. No intenté cambiar la zona horaria ni ninguna otra cosa, solo la hora y la fecha básicas.
rjmunro
3
Usando Xdebug, var_dump ($ date1) informa que contiene 'date' => string, 'timezone_type' => int & 'timezone' => string. Dado que no parece contener matrices u objetos, solo escalares básicos, un clon superficial debería estar bien.
CJ Dennis
46

PHP 5.5.0 introdujo DateTimeImmutable . agregar y modificar métodos de esta clase devolver nuevos objetos.

$date1 = new DateTimeImmutable();
$date2 = $date1->add(new DateInterval('P3Y'));
Alexander Garden
fuente
4
Tenga en cuenta que, lamentablemente, no puede simplemente intercambiar un DateTimepor un DateTimeImmutable. Hay al menos IntlDateFormatter::formatObjectque no le gustan los inmutables (devoluciones en falselugar de la cadena formateada).
user276648
1
¡Oh! De alguna manera nunca supe que esto existía, aunque siempre lo he soñado. y todo el camino de regreso en 5.5 ...
Ben
2
Como un novato, acabo de encontrar un error orientado a objetos al modificar mi DateTimeobjeto en un bucle for: D Esto lo resolvió muy bien ...
Wilt
3
@ user276648 Este error ahora está corregido en php 7.1.5 php.net/ChangeLog-7.php#7.1.5
jontro
11

TLDR:

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

(Copia superficial es enaugh - Profundo copia-ing DateTime marcas (en la actualidad) sin sentido )

Simple como eso :)

Explicación "php crea un objeto de fecha y hora desde otra fecha y hora":

  1. La clonepalabra clave hace una copia superficial regular , aprobada para este caso (por qué => ver más abajo)
  2. Envolviéndolo con ()evalúa la expresión que devuelve el objeto recién creado porclone
  3. ->modify() Por lo tanto, se invoca y modifica el nuevo objeto.
  4. DateTime::modify(...) docs:

    Devuelve el objeto DateTime para el encadenamiento de métodos o FALSE en caso de error.

  5. $date2ahora contiene el clon / copia recién creado y modificado, mientras que $date1permanece sin cambios

Por qué no es necesario realizar una copia profunda aquí:

La copia / clonación profunda solo es necesaria cuando necesita copiar destinos de propiedades que son referencias , pero esto:

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();

salidas:

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

por lo que no hay referencias, solo tipos simples => no es necesario realizar una copia profunda .

jave.web
fuente
1

Deberías cambiar tu DateTimeaDateTimeImmutable

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

entonces puedes llamar a cualquier método en el DateTimesin preocuparte de que cambie

Hossein Shahdoost
fuente
Esta es realmente una respuesta a una pregunta diferente.
Billy ONeal
@BillyONeal que pueda tener no explican completamente la forma, pero esto es una solución a este problema, ya que el origen de este problema es la forma de llamar al método addde date2cambios en el valor de date1y no hay manera de copiar el valor de DateTimela variable a menos que tenga unaDateTimeImmutable
Hossein Shahdoost