在Perl中减去两个日期字符串,并将其转换为Unix时间并返回

时间:2018-12-13 09:59:24

标签: perl posix

我想在Perl中减去两个时间戳。我通过下面的函数将它们转换为unix-time,然后将unix时间戳转换回原来的样子。在下面的示例中,结果是01:20:00,而不是00:20:00 (我认为这与unix时间戳记1.1.1970 01:00:00的开始有关,但不确定如何解决它)

有什么主意吗?非常感谢您的提前帮助。

use POSIX       qw( strftime );
use Time::Local qw( timelocal );

sub to_epoch {
    $_ = shift;
    my @a = split /\W+/, $_;

    my $b = timelocal($a[5],$a[4],$a[3],$a[2],$a[1],$a[0]);
    return $b;
}

my $h_end   = "2018.11.12 00:50:00";
my $h_start = "2018.11.12 00:30:00";
my $duration = to_epoch($h_end) - to_epoch($h_start);
my $convert_back = POSIX::strftime("%H:%M:%S", localtime($duration));
print $convert_back , "\n";

输出:01:20:00

2 个答案:

答案 0 :(得分:2)

对我有用。但这是因为我在GMT中学习,而您在CET(GMT + 1)中。

缺陷在于您的最后一步。您会混淆两个概念-时间点和持续时间。

您正确地将两个时间点转换为Unix纪元数字,然后减去这些数字以获得它们之间的秒数。该数字是持续时间。您想将该持续时间转换为人类可读的格式。使用localtime()POSIX::strtime()并不是这样做的方法。 POSIX::strftime()localtime()处理的是时间点,而不是持续时间。

您得到的数字是1,200。通过将其传递给localtime(),您的意思是“当转换为本地时区中的日期和时间时,纪元1200是什么?”格林尼治标准时间1970年1月1日午夜20点是1,200。但是在您当地的法兰克福时区,现在是凌晨1点20分。这就是为什么您得到1:20而我得到0:20的原因。

有两种方法可以解决此问题。您可以手动进行转换。

my $duration = 1_200;

my $mins = int($duration/60);
my $secs = $duration % 60;

或者您可以使用适当的日期/时间处理模块,例如DateTime(及其关联的模块DateTime::Duration)。

如果您使用timegm()gmtime()代替timelocal()localtime(),这可能会起作用-但我真的不推荐这种方法,因为它会使时间点和持续时间。

更新:使用DateTime的版本。

#/usr/bin/perl

use strict;
use warnings;

use DateTime;
use DateTime::Format::Strptime;

my $h_end   = '2018.11.12 00:50:00';
my $h_start = '2018.11.12 00:30:00';

my $date_p = DateTime::Format::Strptime->new(
  pattern => '%Y.%m.%d %H:%M:%S'
);

my $duration = $date_p->parse_datetime($h_end)
             - $date_p->parse_datetime($h_start);

printf '%02d:%02d:%02d', $duration->in_units('hours', 'minutes', 'seconds');

答案 1 :(得分:1)

1200,值为$duration,表示以下时间时间戳记:

1970-01-01T01:20:00+01:00
           ^^^^^^^^

解决方案是替换

strftime("%H:%M:%S", localtime($duration));

使用

strftime("%H:%M:%S", gmtime($duration));

这给

1970-01-01T00:20:00Z
           ^^^^^^^^

当然,这仍然是黑客。您不应将持续时间传递给gmtime。请改用适当的模块。

use DateTime::Format::Strptime qw( );

my $format = DateTime::Format::Strptime->new(
   pattern => '%Y.%m.%d %H:%M:%S',
   on_error => 'croak',
);

my $h_end   = $format->parse_datetime('2018.11.12 00:50:00');
my $h_start = $format->parse_datetime('2018.11.12 00:30:00');

my $dur = $h_end - $h_start;
printf "%02d:%02d:%02d\n", $dur->in_units(qw( hours minutes seconds ));

顺便

timelocal($a[5],$a[4],$a[3],$a[2],$a[1],$a[0])

应该是

timelocal($a[5],$a[4],$a[3],$a[2],$a[1]-1,$a[0])