Issue converting epoch time to standard convention

2019-09-21 20:03发布

In Perl I am trying to convert a variable containing millisecond epoch time variable $adjSendTime to standard convention using the following code:

$timeLabel = sprintf("%02d:%02d:%02d", (($adjSendTime/3600000) % 24), (($adjSendTime/60000) % 60), $currentSecond);

The issue is that whenever it has reach the 59th second the minute portion of the time will be on minute higher than it should be. The output looks something like

11:58:57
11:58:58
11:59:59
11:59:00
11:59:01

The calculation for $adjSendTime is as follows:

# Determine number of hours between local time and UTC.
# This code is needed to compare timestamps in local time with those in epoch time.
# This code only works in time zones with current time less than UTC time.
@timeArray = gmtime();
$utcHour = $timeArray[2];
@timeArray = localtime();
$localHour = $timeArray[2];
# calculate number of milliseconds between current time and UTC time
$utcShift = ($utcHour - $localHour + 24) % 24;
$utcShift = $utcShift*60*60*1000;

...

if ($field[$i] =~ /^\[29997]=/) {
  $sendTimestamp = $field[$i];
  $sendTimestamp =~ s/.*=(\d*).*/$1/;
  # convert send time to local time.
  $adjSendTime = $sendTimestamp - $utcShift;
}

The calculation for $currentSecond is in two different parts of the code. The first piece occurs the first time through the loop when $FIRST = 1;. $FIRST is never reset to 1 again after this if statement is executed.

$second = ($adjSendTime/1000) % 60;
if ($FIRST) {
  $currentSecond = $second;
  $prevSeqId = $seqId;
  $FIRST = 0;
}

and in the subroutine resetCounters where every value that is calculated in the script is reinitialize to 0. This subroutine is called is called at the start of every new second in the input log file.

sub resetCounters
# -----------------------------------------------------------
# resets all metrics counters
# -----------------------------------------------------------
{
  $tps = 0;
  $mps = 0;
  $batch = 0;
  $maxBatch = 0;
  $avgBatch = 0;
  $latency = 0;
  $latencySum = 0;
  $maxLatency = 0;
  $avgLatency = 0;
  $overThreshold = 0;
  $percentOver = 0;
  $zeroLatencyCount = 0;
  $currentSecond = $second;
  @latencies = ();
}

Can anyone help me figure out why Perl would be doing this because when I find the remainder via long hand division I do not have this issue? I realize that I could use datetime to possibly find the hour and minute for this calculation, but I would still be interested in determining what is wrong with my calculation so that I can hopefully avoid issues like this in the future when using % in Perl.

标签: perl time epoch
3条回答
老娘就宠你
2楼-- · 2019-09-21 20:36

This can be achived simply using core tools: POSIX's strftime in combination with localtime.

use POSIX qw( strftime );
say strftime('%H:%M:%S', localtime( $mstime/1000 ));

DateTime is a great module, but it's needlessly slow for this job. And much wordier.

use DateTime qw( );
my $dt = DateTime->from_epoch(epoch => $mstime/1000, time_zone => 'local');
say $dt->strftime('%H:%M:%S');
查看更多
我命由我不由天
3楼-- · 2019-09-21 20:39

Here is a simple version which uses two built-in perl functions:

my $timeLabel = sprintf "%02d:%02d:%02d", (localtime($adjSendTime/1000))[2,1,0];

For anything much more complicated than this, I would likely use an external module, but I just don't see much point in doing so for this particular need.

查看更多
Lonely孤独者°
4楼-- · 2019-09-21 20:40

Why reinvent the wheel when you can use DateTime?

use strict;
use warnings;
use DateTime;

my $epoch=1254121251.352329;

my $dt = DateTime->from_epoch(epoch=>$epoch,time_zone => "America/Chicago");

print $dt->ymd . " " . $dt->hms . "\n";

The output is:

2009-09-28 02:00:51
查看更多
登录 后发表回答