perl 的带符号的日期时间减法

perl's datetime subtraction with sign

我正在尝试使用 Perl 的 DateTime 用符号从另一天中减去一天。我可以很容易地得到介于两者之间的日子:

sub delta_days {
    my $date1 = shift;
    my $date2 = shift;
    my $d1 = str_to_date_object($date1);
    my $d2 = str_to_date_object($date2);
    return $d2->delta_days($d1)->delta_days();
}
say delta_days('2021-10-21', '1980-8-20');
say delta_days('1980-8-20',  '2021-10-21');

但是这两个调用给出的区别是 15037,没有符号。

遵循文档https://metacpan.org/pod/DateTime

明白了

# not DST
my $dt1 = DateTime->new(
    year      => 2003,
    month     => 5,
    day       => 6,
    time_zone => 'America/Chicago',
);
 
# is DST
my $dt2 = DateTime->new(
    year      => 2003,
    month     => 11,
    day       => 6,
    time_zone => 'America/Chicago',
);
my $dur = $dt2->subtract_datetime($dt1)->days();

我在 How to make DateTime::Duration output only in days? 中检查了一个类似的问题,但我不知道如何在那里获得带符号的差异。

我怎样才能用符号得到天数的差异?

两个问题:如果您想要负偏移量,那么您正在以错误的顺序对日期进行减法,并且对于您正在测试 subtract_datetime() 的日期,持续时间对象将具有 0天。

与此版本比较:

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
use DateTime;
use Data::Dumper;

# not DST
my $dt1 = DateTime->new(
    year      => 2003,
    month     => 5,
    day       => 6,
    time_zone => 'America/Chicago',
);

# is DST
my $dt2 = DateTime->new(
    year      => 2003,
    month     => 11,
    day       => 6,
    time_zone => 'America/Chicago',
    );
say $dt1;
say $dt2;

my $dur = $dt1->subtract_datetime($dt2);
print Dumper({$dur->deltas});

当 运行 产生

2003-05-06T00:00:00
2003-11-06T00:00:00
$VAR1 = {
          'minutes' => 0,
          'nanoseconds' => 0,
          'months' => -6,
          'seconds' => 0,
          'days' => 0
        };

注意 -6 个月的偏移量。 DateTime::Duration 对象不会根据 the documentation 在月份和日期之间转换,所以这看起来像是死胡同。

您首先查看的 delta_days 方法 returns 天数的实际差异,但如前所述,它是一个绝对值。如果第一个日期早于第二个日期,您可以添加一个支票将其转换为负数:

my $days = $dt1->delta_days($dt2)->in_units('days');
$days *= -1 if $dt1 < $dt2;
say $days; # Prints -184

以及您的 delta_days() 函数的变体:

sub delta_days {
    my $date1 = shift;
    my $date2 = shift;
    my $d1 = str_to_date_object($date1);
    my $d2 = str_to_date_object($date2);
    return $d1->delta_days($d2)->in_units('days') * ($d1 < $d2 ? -1 : 1);
}
say delta_days('2021-10-21', '1980-8-20');
say delta_days('1980-8-20',  '2021-10-21');