Вычтите две строки даты в Perl с преобразованием во время Unix и возвращением обратно - PullRequest
0 голосов
/ 13 декабря 2018

Я хочу вычесть две метки времени в 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 голосов
/ 13 декабря 2018

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])
0 голосов
/ 13 декабря 2018

Это работает для меня.Но я думаю, это потому, что я в Гринвиче, а вы в CET (GMT + 1).

Недостаток в вашем последнем шаге.Вы путаете два понятия - момент времени и продолжительность.

Вы правильно переводите свои два момента времени в числа эпох Unix, а затем вычитаете эти числа, чтобы получить количество секунд между ними.Это число является продолжительностью.И вы хотите преобразовать эту продолжительность в удобочитаемый формат.Использование localtime() и POSIX::strtime() не способ сделать это.POSIX::strftime() и localtime() имеют дело с точками во времени, а не с продолжительностью.

Число, которое вы получаете, составляет 1200.Передавая это localtime(), вы говорите: «Каково число эпох, равное 1200, при преобразовании в дату и время в моем местном часовом поясе?»1 200 в 20:00 1 января 1970 года по Гринвичу.Но в вашем местном, во Франкфурте, часовом поясе, сейчас 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');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...