Использование объекта DateTime в качестве отдельной переменной - PullRequest
0 голосов
/ 28 апреля 2020

Я работаю с объектами DateTime для обхода дат за недели. У меня есть дата начала и окончания, и я пытаюсь присвоить дату начала другому имени переменной, чтобы я мог работать с этим, не касаясь исходного объекта даты начала. Но когда я добавляю 1 к дате начала, переменная также добавляет 1, даже если я назначил ее до добавления дня. Вот пример кода:

use strict;
use warnings;
use DateTime::Format::Strptime;

my $dt = DateTime->now(time_zone=> 'America/New_York');
my $dateback0 = $dt->strftime('%Y%m%d');
my $dtback7 = DateTime->now(time_zone=> 'America/New_York')->subtract(days => 7);
my $dateback7 = $dtback7->strftime('%Y%m%d');
my $parseseq = DateTime::Format::Strptime->new(pattern => '%Y%m%d');

my $sdate = $dateback7;
my $edate = $dateback0;

$sdate = $parseseq->parse_datetime($sdate);
$edate = $parseseq->parse_datetime($edate);
$sdate->set_formatter($parseseq);
$edate->set_formatter($parseseq);

while($sdate <= $edate) {
  my $watermark = $sdate;
  print "\nBefore Adding Day\n";
  print "watermark $watermark\nsdate     $sdate\nedate     $edate\n";
  $sdate->add(days => 1);
  print "\nAfter Adding Day\n";
  print "watermark $watermark\nsdate     $sdate\nedate     $edate\n";
}

Таким образом, вместо печати начального водяного знака 20200421, он печатает 20200422.


Before Adding Day
watermark 20200421
sdate     20200421
edate     20200428

After Adding Day
watermark 20200422
sdate     20200422
edate     20200428

Before Adding Day
watermark 20200422
sdate     20200422
edate     20200428

After Adding Day
watermark 20200423
sdate     20200423
edate     20200428

Before Adding Day
watermark 20200423
sdate     20200423
edate     20200428

After Adding Day
watermark 20200424
sdate     20200424
edate     20200428

Before Adding Day
watermark 20200424
sdate     20200424
edate     20200428

After Adding Day
watermark 20200425
sdate     20200425
edate     20200428

Before Adding Day
watermark 20200425
sdate     20200425
edate     20200428

After Adding Day
watermark 20200426
sdate     20200426
edate     20200428

Before Adding Day
watermark 20200426
sdate     20200426
edate     20200428

After Adding Day
watermark 20200427
sdate     20200427
edate     20200428

Before Adding Day
watermark 20200427
sdate     20200427
edate     20200428

After Adding Day
watermark 20200428
sdate     20200428
edate     20200428

Before Adding Day
watermark 20200428
sdate     20200428
edate     20200428

After Adding Day
watermark 20200429
sdate     20200429
edate     20200428

1 Ответ

1 голос
/ 29 апреля 2020

Это потому, что объект DateTime (как и все объекты в Perl) является ссылкой.

$ perl -MDateTime -E'say overload::StrVal(DateTime->now)'
DateTime=HASH(0x55dad5805018)

(Обратите внимание, что я использовал overload::StrVal() здесь как DateTime, перегружающий строковое преобразование - которое обычно очень полезно, но не здесь.)

Это большое шестнадцатеричное число является адресом памяти. Ваша переменная фактически не содержит данные DateTime, она хранит адрес в памяти данных DateTime (которые хранятся в ha sh).

Очевидный подход к получению копии вашего объекта DateTime примерно так:

my $obj1 = DateTime->now;
my $obj2 = $obj1;

Но это не очень полезно, поскольку $obj1 и $obj2 теперь оба содержат одинаковую ссылку на адрес памяти . Поэтому, если вы обновите одну из ссылок, вы увидите те же изменения, которые отражены в другой ссылке.

По этой причине класс DateTime включает метод clone(). Это копирует ваш оригинальный объект в новую ячейку памяти и возвращает ссылку на новую копию. Затем вы можете изменить новую копию без изменения оригинала.

my $obj1 = DateTime->now;
my $obj2 = $obj1->clone;

Эта программа демонстрирует проблему (и решение).

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use DateTime;

say 'standard copy assignment';

my $obj1 = DateTime->now;
my $obj2 = $obj1;

say 'before changing $obj1';

say $obj1;
say $obj2;

$obj1->add(days => 1);

say 'after changing $obj1';

# Both dates are changed
say $obj1;
say $obj2;

say 'using clone()';

$obj1 = DateTime->now;
$obj2 = $obj1->clone;

say 'before changing $obj1';

say $obj1;
say $obj2;

$obj1->add(days => 1);

say 'after changing $obj1';

# Only $obj1 is changed
say $obj1;
say $obj2;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...