在转换为hh:mm:ss.mmm时,计算新加坡的小时差会增加30分钟

时间:2013-12-04 18:01:50

标签: perl timezone timezone-offset

我有这个Perl脚本,我需要监视DBI调用的执行时间。

在欧洲(法国),我没有问题:报告2秒执行时间为2秒。

在新加坡的计算机上运行的同一个脚本报告30分2秒。

为什么?

use strict;
use Time::Format qw(%time);
use Time::HiRes qw(gettimeofday);

my $time_start = gettimeofday();

sleep 2;  # some action goes here

my $stat_perf = gettimeofday() - $time_start;
print STDOUT $time{'mm:ss.mmm', $stat_perf} . " \n";

法国的输出是

00:02.000 

在新加坡运行的相同脚本产生:

30:02.001 

为什么?

3 个答案:

答案 0 :(得分:2)

根据this documentationgettimeofday函数返回自unix时期以来的秒或微秒,即1970年1月1日 UTC 。因为它是UTC格式,所以它根本不受时区的影响。

此外,在原始代码中,您只使用gettimeofday,它将从现在返回时间戳,而不是从1970年开始。但在您建议的答案中,出于某种原因,你已经设置了时间戳,这对你没有多大帮助。

是的,几乎每个时区都有历史,包括新加坡。 You can see it in the TZDB here。但你错了它在这个时代是+8:30。实际上是+7:30。 You can verify also on this site。但无论如何都没关系,因为就像我说的那样,gettimeofday严格按UTC工作。

我认为问题在于你如何解释结果。你有最后一行:

print STDOUT $time{'mm:ss.mmm', $stat_perf} . " \n";

但是$stat_perf是经过的持续时间,而不是您可以视为时间戳的值。您可能不应该将其传递给$time,因为使用本地时区并期望完整的时间戳。

此外,您可能希望使用tv_interval,如图所示in the examples

更新

我搜索了CPAN档案,我确定某个地方有一个模块用于格式化经过的持续时间,但我似乎无法找到它。无论如何,自己写这个并不难。在这里,这应该工作:

my $min = $stat_perf / 60;
my $sec = ($stat_perf * 1000 % 60000) / 1000;
my $elapsed = sprintf("%02u:%06.3f", $min, $sec);
print STDOUT $elapsed . "\n";

答案 1 :(得分:0)

anser是......

新加坡现在距离UTC偏离08小时。在1970年,它被抵消了08:30。要求将几秒钟转换为字符串将使我们达到1970年,而不是今天的日期和时区。

通过请求

print STDOUT $time{'mm:ss.mmm', 2} . " \n";

系统调整到1970(epoch)时区偏移。

为了在新加坡取得正确的结果,我们必须转移到1982年之后,当时新加坡改变了最后一个时区。

print STDOUT $time{'mm:ss.mmm', 2 + 1356994800} . " \n";

作为

UNIX_TIMESTAMP('2013-01-01 00:00:00')  = 1356994800

我们只关注日期的时间部分,所以这样做。

检查

zdump -v Asia/Singapore

这就是诀窍。

答案 2 :(得分:0)

这是一个模拟$time{}的脚本,用于将实数转换为表示其整数部分的mm:ss六十进制转换的字符串,并与格式为微秒的十进制余数连接。

由于这将成为库的一部分,因此可以设置保护措施以避免使用错误的参数调用它。

我希望我没有错过任何东西。

use strict;
use Time::Format qw(%time);


# ----------------------------------------------------------
# A substitute to $time{} as we have issues with TZ offsets at epoch days in some part of the World
# A real to sexagesimal converter
# Format will be set to match $time{'mm:ss.mmm', $stat_perf};
sub microTime {
  return '' unless (my ($intertime) = @_);
  return '' unless (ref ($intertime) eq '');
  return '' unless (sprintf("%s", $intertime) =~ m/^(?:[\d]+)(?:\.(?:[\d]+))?$/);
  my $intNum = int($intertime);
  "a" =~ /a/; # Resets regex buffers
  sprintf ("%.03f", $intertime - $intNum) =~ m,\.([\d]+),;
  my $intDec = $1;  # It's always defined
  my $intUnder = $intNum % 3600;
  my $intMin = int($intUnder / 60);
  my $intSec = $intUnder % 60;
  return sprintf ("%02d:%02d.%03d", $intMin, $intSec, $intDec);
}


  my $stat_perf;

  $stat_perf = 345.987;
  $stat_perf = 345;
  $stat_perf = 3945.987;
  $stat_perf = 0;
  $stat_perf = 3945.918733;


  print STDOUT sprintf (" >> %s\n", &microTime ($stat_perf));
  print STDOUT sprintf (" == %s\n", $time{'mm:ss.mmm', $stat_perf});