代码之家  ›  专栏  ›  技术社区  ›  James Finnegan

将DateTime对象用作单独的变量

  •  0
  • James Finnegan  · 技术社区  · 5 年前

    我正在使用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
    
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   Dave Cross    5 年前

    这是因为DateTime对象(像Perl中的所有对象一样)是一个引用。

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

    (注意我用过 overload::StrVal() 这里是因为DateTime重载了字符串化——这通常非常有用,但不在这里。)

    那个大十六进制数是一个内存地址。您的变量实际上并不保存DateTime数据,它保存DateTime数据在内存中的地址(存储在哈希中)。

    获取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;