在 Perl 中减去两个日期字符串并转换为 unix 时间并返回
Subtract two date strings in Perl with conversion to unix time and reverting back
我想在 Perl 中减去两个时间戳。我通过下面的函数将它们转换为 unix 时间,并将 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
对我有用。但我认为那是因为我在 GMT 而你在 CET (GMT+1)。
缺陷出现在你的最后一步。您混淆了两个概念 - 时间点和持续时间。
您正确地将两个时间点转换为 Unix 纪元数字,然后减去这些数字以获得它们之间的秒数。这个数字是一个持续时间。并且您想将该持续时间转换为人类可读的格式。使用 localtime()
和 POSIX::strtime()
不是这样做的方法。 POSIX::strftime()
和 localtime()
处理时间点,而不是持续时间。
您得到的数字是 1,200。通过将其传递给 localtime()
你是说 "what is the epoch number 1,200 when converted to a date and time in my local timezone?" 1,200 是格林威治标准时间 1970 年 1 月 1 日午夜过后 20 点。但在您当地的法兰克福时区,现在是凌晨 1 点 20 分。这就是为什么你得到 1:20 而我得到 0:20.
有几种方法可以解决这个问题。您可以手动进行转换。
my $duration = 1_200;
my $mins = int($duration/60);
my $secs = $duration % 60;
或者您可以使用适当的 date/time 处理模块,例如 DateTime (along with its associated module DateTime::Duration)。
如果您使用 timegm()
和 gmtime()
代替 timelocal()
和 localtime()
,它可能会起作用 - 但我真的不推荐这种方法,因为它会使时间点和持续时间之间的混淆。
更新:一个使用日期时间的版本。
#/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');
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
^^^^^^^^
当然,这仍然是一个hack。您不应该将持续时间传递给 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])
我想在 Perl 中减去两个时间戳。我通过下面的函数将它们转换为 unix 时间,并将 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
对我有用。但我认为那是因为我在 GMT 而你在 CET (GMT+1)。
缺陷出现在你的最后一步。您混淆了两个概念 - 时间点和持续时间。
您正确地将两个时间点转换为 Unix 纪元数字,然后减去这些数字以获得它们之间的秒数。这个数字是一个持续时间。并且您想将该持续时间转换为人类可读的格式。使用 localtime()
和 POSIX::strtime()
不是这样做的方法。 POSIX::strftime()
和 localtime()
处理时间点,而不是持续时间。
您得到的数字是 1,200。通过将其传递给 localtime()
你是说 "what is the epoch number 1,200 when converted to a date and time in my local timezone?" 1,200 是格林威治标准时间 1970 年 1 月 1 日午夜过后 20 点。但在您当地的法兰克福时区,现在是凌晨 1 点 20 分。这就是为什么你得到 1:20 而我得到 0:20.
有几种方法可以解决这个问题。您可以手动进行转换。
my $duration = 1_200;
my $mins = int($duration/60);
my $secs = $duration % 60;
或者您可以使用适当的 date/time 处理模块,例如 DateTime (along with its associated module DateTime::Duration)。
如果您使用 timegm()
和 gmtime()
代替 timelocal()
和 localtime()
,它可能会起作用 - 但我真的不推荐这种方法,因为它会使时间点和持续时间之间的混淆。
更新:一个使用日期时间的版本。
#/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');
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
^^^^^^^^
当然,这仍然是一个hack。您不应该将持续时间传递给 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])