Bagaimana cara saya menyalin objek DateTime?

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

Sekarang $date1dan $date2berisi tanggal yang sama - tiga tahun dari sekarang. Saya ingin membuat dua waktu terpisah, satu yang diurai dari string dan satu lagi dengan tiga tahun ditambahkan ke dalamnya. Saat ini saya telah meretasnya seperti ini:

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

tapi itu sepertinya peretasan yang menghebohkan. Apakah ada cara yang "benar" untuk menyalin mendalam objek DateTime?

Billy ONeal
sumber

Jawaban:

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

Memperbarui:

Jika Anda ingin menyalin daripada mereferensikan objek DT yang ada, gunakan clonebukan =.

$a = clone $b;

Amy B
sumber
12
Saya menggunakan DateTime baru dalam contoh untuk mendemonstrasikan maksudnya, tetapi untuk saat ini asumsikan DateTime dikembalikan dari beberapa API buram yang tidak bisa saya panggil lagi. Misalnya, saya memiliki fungsi yang menangani pesanan yang mengembalikan DateTime yaitu saat pelanggan selanjutnya dapat melakukan pemesanan. Memanggil fungsi untuk membuat salinan menghasilkan efek samping yang tidak saya inginkan.
Billy ONeal
Sebenarnya saya belum mengujinya, tetapi disebutkan di php.net bahwa ini hanya tersedia untuk PHP 5.3 dan yang lebih tinggi.
hugo der hungrige
@hugo: Ya, kelas DateTime membutuhkan PHP 5.3.
Billy ONeal
11
Tepat ketika saya berpikir saya telah memahami PHP, saya belajar tentang operator baru.
kr094
Harus melakukan ini untuk menyalin objek Carbon yang ada ke variabel lain. Ini berhasil.
racl101
111

Kloning tanggal dengan operator klon :

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

Klon dangkal secara default, tetapi cukup dalam untuk DateTime. Dalam objek Anda sendiri, Anda dapat menentukan __clone()metode ajaib untuk mengkloning properti (yaitu objek anak) yang masuk akal untuk dikloning saat objek induk berubah.

(Saya tidak yakin mengapa dokumentasi menganggap contoh yang baik tentang perlunya mengkloning objek adalah GTK. Siapa yang menggunakan GTK di PHP?)

rjmunro
sumber
1
Terima kasih atas jawabannya, tetapi bagaimana Anda tahu itu cukup dalam untuk DateTime? Atribut mana yang tetap menjadi referensi dan mana yang disalin oleh nilai? Misalnya, saya dapat mengubah waktu dan zona waktu dan itu tidak akan memengaruhi klon?
David
1
@David: Saya tahu ini cukup dalam untuk DateTime karena saya mencobanya, dan itu berhasil untuk saya. Saya tidak mencoba mengubah zona waktu atau hal lainnya, hanya waktu dan tanggal dasar.
rjmunro
3
Menggunakan Xdebug, var_dump ($ date1) melaporkan bahwa itu berisi 'date' => string, 'timezone_type' => int & 'timezone' => string. Karena tampaknya tidak berisi larik atau objek apa pun, hanya skalar dasar, klon dangkal seharusnya baik-baik saja.
CJ Dennis
46

PHP 5.5.0 memperkenalkan DateTimeImmutable . menambah dan mengubah metode kelas ini mengembalikan objek baru.

$date1 = new DateTimeImmutable();
$date2 = $date1->add(new DateInterval('P3Y'));
Alexander Garden
sumber
4
Perhatikan bahwa sayangnya Anda tidak bisa hanya menukar a DateTimedengan a DateTimeImmutable. Setidaknya ada IntlDateFormatter::formatObjectyang tidak suka yang tidak dapat diubah (mengembalikan falsealih-alih string yang diformat).
pengguna276648
1
oh! Saya entah bagaimana tidak pernah tahu ini ada, meskipun saya sudah lama memimpikannya. dan kembali ke 5.5 ...
Ben
2
Seperti beberapa noob, saya baru saja menemukan perangkap berorientasi objek dengan memodifikasi DateTimeobjek saya dalam loop for: D Ini menyelesaikannya dengan baik ...
Wilt
3
@ user276648 Bug ini sekarang telah diperbaiki di php 7.1.5 php.net/ChangeLog-7.php#7.1.5
jontro
11

TLDR:

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

(Salinan dangkal sekarang - Penyalinan mendalam DateTime membuat (saat ini) tidak masuk akal )

Sederhana seperti itu :)

Penjelasan "php buat objek datetime dari datetime lain":

  1. Kata clonekunci membuat salinan biasa yang dangkal - enaugh untuk kasus ini (mengapa => lihat di bawah)
  2. Membungkusnya dengan ()mengevaluasi ekspresi yang mengembalikan objek yang baru dibuat olehclone
  3. ->modify() oleh karena itu dipanggil dan memodifikasi objek baru
  4. DateTime::modify(...) dokumen:

    Mengembalikan objek DateTime untuk rangkaian metode atau FALSE jika gagal.

  5. $date2sekarang berisi clone / copy yang baru dibuat & dimodifikasi, sementara $date1tetap tidak berubah

Mengapa Anda tidak perlu menyalin dalam di sini:

Salin / kloning mendalam hanya diperlukan, saat Anda perlu menyalin target properti yang menjadi referensi , tetapi ini:

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

keluaran:

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

jadi tidak ada referensi, cukup tipe sederhana => tidak perlu deep copy.

jave.web
sumber
1

Anda harus mengubah Anda DateTimemenjadiDateTimeImmutable

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

kemudian Anda dapat memanggil metode apa pun DateTimetanpa khawatir akan berubah

Hossein Shahdoost
sumber
Ini benar-benar jawaban untuk pertanyaan yang berbeda.
Billy ONeal
@BillyONeal Saya mungkin belum menjelaskan sepenuhnya bagaimana, Tapi ini adalah solusi untuk masalah ini karena sumber dari masalah ini adalah bagaimana memanggil metode addpada date2perubahan nilai date1dan tidak ada cara untuk menyalin nilai DateTimevariabel kecuali Anda memilikiDateTimeImmutable
Hossein Shahdoost