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?

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;


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?)


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

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 .


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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.