如何深度复制DateTime对象?


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

现在$date1$date2包含相同的日期-从现在起三年。我想创建两个单独的日期时间,其中一个是从字符串中解析出来的,另一个是添加了三年的时间。目前,我已经像这样修改了它:

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

但这似乎是一个可怕的骇客。有没有“正确”的方法来深度复制DateTime对象?

Answers:


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

更新:

如果要复制而不是引用现有的DT对象,请使用clone,而不是=

$a = clone $b;


12
我在示例中使用了一个新的DateTime来演示这一点,但现在假设DateTime是从某些不透明的API返回的,而我不能再次调用它。例如,我有一个处理订单的函数,该函数返回一个DateTime,这是客户下一次下订单的时间。调用该函数以创建副本会产生我不想要的副作用。
Billy ONeal 2010年

我没有实际测试过,但是在php.net上提到它仅适用于PHP 5.3和更高版本。
雨果·德·亨里格

@hugo:是的,DateTime类需要PHP 5.3。
Billy ONeal

11
就在我以为自己对PHP有所了解的时候,我了解了一个新的运算符。
2014年

必须这样做将现有的Carbon对象复制到另一个变量。这工作了。
racl101 2015年

111

使用克隆运算符克隆日期:

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

默认情况下,克隆是浅的,但对于DateTime来说足够深。在您自己的对象中,您可以定义__clone()magic方法来克隆属性(即子对象),这些属性在父对象发生更改时应该被克隆。

(我不确定文档为什么会认为需要克隆对象的一个​​好例子就是GTK。谁在PHP中使用GTK?)


1
谢谢您的回答,但是您怎么知道它对于DateTime足够深?哪些属性保留为引用,哪些属性按值复制?例如,我可以更改时间和时区,它不会影响克隆吗?
大卫

1
@David:我知道它对DateTime足够深,因为我尝试了它,并且对我有用。我没有尝试更改时区或其他任何东西,而只是尝试更改基本时间和日期。
rjmunro

3
使用Xdebug,var_dump($ date1)报告它包含'date'=>字符串,'timezone_type'=> int和'timezone'=>字符串。由于它似乎不包含任何数组或对象,仅包含基本标量,因此浅表克隆应该可以。
CJ丹尼斯

46

PHP 5.5.0引入了DateTimeImmutable。此类的添加修改方法返回新对象。

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

4
请注意,不幸的是,您不能仅将替换DateTimeDateTimeImmutable。至少有IntlDateFormatter::formatObject一点是不喜欢不可变的(返回false而不是格式化的字符串)。
user276648 '16

1
哦!尽管我梦dream以求,但我始终不知道它的存在。和所有在5.5回来的路上...

2
像一些菜鸟一样,我只是通过DateTime在for循环中修改对象来遇到面向对象的陷阱:D这很好地解决了它……
Wilt

3
@ user276648此错误现已在php 7.1.5中修复php.net/ChangeLog-7.php#7.1.5
jontro

11

TLDR:

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

(浅拷贝enaugh - 深层复制-ING的DateTime品牌(目前)没有任何意义

就那么简单 :)

说明“ php从另一个日期时间创建日期时间对象”:

  1. clone关键字使常规拷贝- enaugh此情况下(为什么=>见下文)
  2. 将其包装起来可以()计算返回新创建对象的表达式clone
  3. ->modify() 因此被调用并修改新对象
  4. DateTime::modify(...) docs:

    返回DateTime对象以进行方法链接或失败时返回FALSE。

  5. $date2现在包含新创建和修改的克隆/副本,而$date1保持不变

为什么您不需要在此处进行复制:

仅当需要复制作为引用的属性的目标时,才需要进行深层复制/克隆,但这是:

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

输出:

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

因此没有引用,只是简单的类型=>无需深度复制。


1

您应该将其更改DateTimeDateTimeImmutable

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

那么您可以在上调用任何方法DateTime而不必担心其更改


这实际上是对另一个问题的答案。
Billy ONeal

@BillyONeal我可能没有完全解释如何做,但是这是解决此问题的方法,因为此问题的根源在于如何在add上调用方法来date2更改value的值,date1并且DateTime除非您具有DateTimeImmutable
Hossein Shahdoost
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.