Como copiar em profundidade um objeto DateTime?

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

Agora $date1e $date2contenha a mesma data - daqui a três anos. Gostaria de criar duas datas separadas, uma que é analisada de uma string e outra com três anos adicionados a ela. Atualmente eu hackeei assim:

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

mas isso parece um truque horrendo. Existe uma maneira "correta" de copiar profundamente um objeto DateTime?

Billy ONeal
fonte

Respostas:

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

Atualizar:

Se você deseja copiar em vez de fazer referência a um objeto DT existente, use clone, não =.

$a = clone $b;

Amy B
fonte
12
Usei um novo DateTime no exemplo para demonstrar o ponto, mas, por enquanto, suponha que o DateTime seja retornado de alguma API opaca que não posso chamar novamente. Por exemplo, eu tenho uma função que lida com pedidos que retorna um DateTime que é quando o cliente pode fazer um pedido a seguir. Chamar a função para criar uma cópia produz efeitos colaterais que não quero.
precisa saber é o seguinte
Na verdade, eu não testei, mas é mencionado no php.net que isso só está disponível para o PHP 5.3 e superior.
Hugo der hungrige
@ Hugo: Sim, a classe DateTime requer PHP 5.3.
Billy ONeal
11
Quando pensava ter um conhecimento do PHP, aprendi sobre um novo operador.
kr094
Tinha que fazer isso para copiar um objeto Carbon existente para outra variável. Isso funcionou.
racl101
111

Clone a data com o operador clone :

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

Por padrão, os clones são rasos, mas suficientemente profundos para um DateTime. Em seus próprios objetos, você pode definir o __clone()método mágico para clonar as propriedades (ou seja, objetos filhos) que fazem sentido serem clonadas quando o objeto pai é alterado.

(Não sei por que a documentação considera um bom exemplo da necessidade de clonar um objeto é o GTK. Quem usa o GTK no PHP?)

rjmunro
fonte
1
Obrigado pela resposta, mas como você sabe que é profunda o suficiente para o DateTime? Quais atributos permanecem referências e quais são copiados por valor? Por exemplo, posso alterar a hora e o fuso horário e isso não afetará o clone?
David
1
@ David: Eu sei que é profundo o suficiente para o DateTime, porque eu tentei, e funcionou para mim. Não tentei alterar o fuso horário ou qualquer outra coisa, apenas a hora e a data básicas.
Rjmunro
3
Usando o Xdebug, var_dump ($ date1) informa que ele contém 'date' => string, 'timezone_type' => int & 'timezone' => string. Como ele não parece conter matrizes ou objetos, apenas escalares básicos, um clone superficial deve ser bom.
CJ Dennis
46

O PHP 5.5.0 introduziu DateTimeImmutable . adicionar e modificar métodos dessa classe retornam novos objetos.

$date1 = new DateTimeImmutable();
$date2 = $date1->add(new DateInterval('P3Y'));
Alexander Garden
fonte
4
Observe que, infelizmente, você não pode simplesmente trocar a DateTimecom a DateTimeImmutable. Há pelo menos IntlDateFormatter::formatObjectque não gosta de imutáveis ​​(retorna em falsevez da string formatada).
user276648
1
ah! De alguma forma, eu nunca soube que isso existia, embora eu sonhe com isso há muito tempo. e todo o caminho de volta em 5,5 ...
Ben
2
Como alguns de noob Eu só encontrou uma armadilha orientada a objeto, modificando o meu DateTimeobjeto em um loop: D Esta muito bem resolvido ...
Wilt
3
@ user276648 Este erro foi corrigido no PHP 7.1.5 php.net/ChangeLog-7.php#7.1.5
jontro
11

TLDR:

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

(Cópia superficial é enaugh - Profundo DateTime marcas-ing cópia (atualmente) não sentido )

Simples assim :)

Explicação "php create objeto datetime de outro datetime":

  1. A clonepalavra-chave faz uma cópia rasa regular - basta para este caso (por que => veja abaixo)
  2. O agrupamento ()avalia a expressão retornando o objeto recém-criado porclone
  3. ->modify() é, portanto, chamado e modifica o novo objeto
  4. DateTime::modify(...) docs:

    Retorna o objeto DateTime para encadeamento de método ou FALSE em falha.

  5. $date2agora contém o clone / cópia recém-criado e modificado, enquanto $date1permanece inalterado

Por que você não precisa copiar em profundidade aqui:

A cópia / clone profunda é necessária apenas quando você precisa copiar destinos de propriedades que são referências , mas isso:

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

saídas:

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

portanto, não há referências, apenas tipos simples => não há necessidade de copiar em profundidade .

jave.web
fonte
1

Você deve mudar DateTimeparaDateTimeImmutable

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

então você pode chamar qualquer método no DateTimesem se preocupar com isso mudar

Hossein Shahdoost
fonte
Esta é realmente uma resposta para uma pergunta diferente.
Billy ONeal
@BillyONeal eu poderia ter não totalmente explicado como, mas esta é uma solução para este problema, como a origem deste problema é como chamar o método addem date2alterações do valor do date1e não há maneira de copiar o valor da DateTimevariável, a menos que você tem umDateTimeImmutable
Hossein Shahdoost