ฉันจะคัดลอกวัตถุ DateTime อย่างละเอียดได้อย่างไร


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

ตอนนี้$date1และ$date2มีวันที่เดียวกัน - สามปีนับจากนี้ ฉันต้องการสร้างเวลาที่แยกจากกันสองวันโดยหนึ่งซึ่งแยกวิเคราะห์จากสตริงและอีกสามปีที่เพิ่มเข้ามา ตอนนี้ฉันได้แฮ็กแล้วเป็นแบบนี้:

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

แต่ดูเหมือนว่าเป็นการแฮ็กที่น่ากลัว มีวิธี "ถูกต้อง" ในการคัดลอกวัตถุ DateTime อย่างละเอียดหรือไม่

คำตอบ:


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

ปรับปรุง:

หากคุณต้องการคัดลอกมากกว่ากรอบอ้างอิง DT วัตถุที่มีอยู่ใช้ไม่ได้clone=

$a = clone $b;


12
ฉันใช้ DateTime ใหม่ในตัวอย่างเพื่อสาธิตประเด็น แต่ตอนนี้สมมติว่า DateTime ถูกส่งคืนจาก API ทึบแสงบางตัวที่ฉันไม่สามารถเรียกซ้ำได้ ตัวอย่างเช่นฉันมีฟังก์ชันที่จัดการคำสั่งซื้อที่ส่งคืน DateTime ซึ่งเป็นเวลาที่ลูกค้าสามารถสั่งซื้อได้ในครั้งถัดไป การเรียกใช้ฟังก์ชันเพื่อสร้างสำเนาทำให้เกิดผลข้างเคียงที่ฉันไม่ต้องการ
Billy ONeal

ฉันไม่ได้ทดสอบจริง แต่มีการกล่าวถึงใน php.net ว่าใช้ได้เฉพาะกับ PHP 5.3 ขึ้นไปเท่านั้น
hugo der hungrige

@hugo: ใช่คลาส DateTime ต้องใช้ PHP 5.3
Billy ONeal

11
เมื่อฉันคิดว่าฉันเข้าใจ PHP แล้วฉันก็เรียนรู้เกี่ยวกับตัวดำเนินการใหม่
kr094

ต้องทำสิ่งนี้เพื่อคัดลอกวัตถุคาร์บอนที่มีอยู่ไปยังตัวแปรอื่น สิ่งนี้ได้ผล
racl101

111

โคลนวันที่ด้วยตัวดำเนินการโคลน :

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

โคลนจะตื้นตามค่าเริ่มต้น แต่ลึกพอสำหรับ DateTime ในอ็อบเจ็กต์ของคุณเองคุณสามารถกำหนด__clone()เมธอดมายากลเพื่อโคลนคุณสมบัติ (เช่นอ็อบเจกต์ลูก) ที่เหมาะสมที่จะโคลนเมื่ออ็อบเจกต์หลักเปลี่ยนไป

(ฉันไม่แน่ใจว่าทำไมเอกสารถึงคิดว่าเป็นตัวอย่างที่ดีในการโคลนวัตถุคือ GTK ใครใช้ GTK ใน PHP)


1
ขอบคุณสำหรับคำตอบ แต่คุณจะรู้ได้อย่างไรว่ามันลึกพอสำหรับ DateTime? แอตทริบิวต์ใดที่ยังคงอ้างอิงและถูกคัดลอกโดยค่า ตัวอย่างเช่นฉันสามารถเปลี่ยนเวลาและเขตเวลาและจะไม่มีผลกับการโคลนหรือไม่?
เดวิด

1
@David: ฉันรู้ว่ามันลึกพอสำหรับ DateTime เพราะฉันลองแล้วมันก็ได้ผลสำหรับฉัน ฉันไม่ได้ลองเปลี่ยนเขตเวลาหรือสิ่งอื่นใดเพียงแค่เวลาและวันที่พื้นฐาน
rjmunro

3
การใช้ Xdebug, var_dump ($ date1) จะรายงานว่ามีสตริง 'date' => string, 'timezone_type' => int & 'timezone' => เนื่องจากดูเหมือนว่าจะไม่มีอาร์เรย์หรือวัตถุใด ๆ เพียงแค่สเกลาร์พื้นฐานการโคลนแบบตื้นก็น่าจะใช้ได้
CJ Dennis

46

PHP 5.5.0 แนะนำDateTimeImmutable เพิ่มและแก้ไขวิธีการของคลาสนี้ส่งคืนอ็อบเจ็กต์ใหม่

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

4
โปรดทราบว่าน่าเสียดายที่คุณไม่สามารถสลับDateTimeไฟล์DateTimeImmutable. อย่างน้อยก็มีIntlDateFormatter::formatObjectที่ไม่ชอบไม่เปลี่ยนรูป (ส่งกลับfalseแทนสตริงที่จัดรูปแบบ)
user276648

1
โอ้! ฉันไม่เคยรู้มาก่อนเลยแม้ว่าฉันจะฝันถึงมันมานาน และย้อนกลับไปใน 5.5 ...
Ben

2
เช่นเดียวกับ noob บางตัวฉันเพิ่งพบข้อผิดพลาดที่มุ่งเน้นวัตถุโดยการแก้ไขDateTimeวัตถุของฉันใน for loop: D สิ่งนี้แก้ไขได้อย่างดี ...
เหี่ยว

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

(การคัดลอกแบบตื้นคือวันที่และเวลาที่คัดลอกลึกทำให้ (ในปัจจุบัน) ไม่มีความหมาย )

ง่ายๆแค่นั้น :)

คำอธิบาย "php create datetime object from another datetime":

  1. cloneคำหลักที่ทำให้ปกติตื้นสำเนา - enaugh สำหรับกรณีนี้ (ทำไม => ดูด้านล่าง)
  2. ห่อด้วยการ()ประเมินนิพจน์ที่ส่งคืนวัตถุที่สร้างขึ้นใหม่โดยclone
  3. ->modify() จึงถูกเรียกใช้และปรับเปลี่ยนออบเจ็กต์ใหม่
  4. DateTime::modify(...) เอกสาร:

    ส่งคืนอ็อบเจ็กต์ 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

คุณควรเปลี่ยนDateTimeเป็นDateTimeImmutable

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

จากนั้นคุณสามารถเรียกใช้วิธีการใดก็ได้DateTimeโดยไม่ต้องกังวลเกี่ยวกับการเปลี่ยนแปลง


นี่เป็นคำตอบสำหรับคำถามอื่น
Billy ONeal

@BillyONeal ฉันอาจไม่ได้อธิบายวิธีการทั้งหมด แต่นี่เป็นวิธีแก้ปัญหานี้เนื่องจากที่มาของปัญหานี้คือวิธีการเรียกใช้เมธอดaddในdate2การเปลี่ยนแปลงค่าของdate1และไม่มีวิธีคัดลอกค่าของDateTimeตัวแปรเว้นแต่คุณจะมีDateTimeImmutable
Hossein Shahdoost
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.