【问题标题】:Computing small time differences in Singapore adds 30 minutes when converted to hh:mm:ss.mmm在新加坡计算小的时差在转换为 hh:mm:ss.mmm 时会增加 30 分钟
【发布时间】:2013-12-04 18:01:50
【问题描述】:

我有这个 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 

为什么?

【问题讨论】:

    标签: perl timezone timezone-offset


    【解决方案1】:

    根据this documentationgettimeofday 函数返回自 unix 纪元(即 1970 年 1 月 1 日 UTC)以来的秒数或微秒数。因为是UTC,所以完全不受时区影响。

    另外,在您的原始代码中,您只是使用 gettimeofday,它将返回 now 的时间戳,而不是 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";
    

    【讨论】:

    • 我认为您没有得到我必须解决的问题:我需要一个字符串来表示非常短的时间的分钟、秒和微秒。 gettimeofday 为我提供了所需的粒度级别。 tv_interval 不会转换为十六进制格式的字符串。直到我错了,$time{} 让我完成了工作。尽管如此,在新加坡,无论我用什么方法来计算经过的时间; print STDOUT $time{'mm:ss.mmm',0}; 产生 30:00.000gettimeofday 此处不涉及。
    • 正如您所指出的,$time{} 将其作为时间戳处理。没什么新鲜的;因此我发布的技巧。到目前为止,我还没有找到更好的方法来获得 - 在地球上的任何地方 - 将 67.934 秒转换为“01:07.934”。有什么建议吗?
    • 我会看看是否可以找到(或编写)一个脚本,用于发出以该格式格式化的经过时间。但是请注意,将其视为一天中的某个时间,您还会遇到一个问题,即 65 分钟之类的值将被视为 1 小时 5 分钟,并且由于您没有显示小时数,您只会看到5 分钟。
    • 谢谢。是的,这就是我需要的:在其他地方捕获了一个多小时的持续时间(实际上是被看门狗杀死的工作)。从这里评论者的输入来看,我确实相信我最好编写自己的实数到六十进制转换器,最终放弃 3600s 以上的值。更多内容供您审核。
    【解决方案2】:

    分析器是...

    新加坡现在与 UTC 时间相差 08:00。 1970 年,它被 08h30 抵消。要求将几秒钟转换为字符串将使我们得到 1970 年,而不是今天的日期和时区。

    通过请求

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

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

    为了在新加坡得到正确的结果,我们必须转移到 1982 年之后,也就是新加坡最后一次更改时区的时候。

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

    作为

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

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

    检查

    zdump -v Asia/Singapore
    

    这就是诀窍。

    【讨论】:

    • 或者您不应该将持续时间视为日期时间。尽管有它的名字,%time 确实是一个日期时间格式化程序。
    • 除了$time{} 之外,是否还有其他功能可以将 67.934 秒转换为地球上任何地方的“01:07.934”?任何建议..除了编码一个实数到六进制转换?但是,我认为我提出的技巧没有任何不切实际的地方,除了它在消耗 cpu 周期方面的性能。
    【解决方案3】:

    这是一个模拟$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});
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-01-28
      • 1970-01-01
      • 1970-01-01
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多