【问题标题】:Need to compare times in Perl; how to deal with timezone?需要在 Perl 中比较时间;如何处理时区?
【发布时间】:2012-12-11 00:33:05
【问题描述】:

我的日志文件的时间戳如下:

Fri Nov 30 10:19:35:152.92 PST 2012
Fri Nov 30 10:19:35:228.8 PST 2012
or even:
Thu Nov 29 14:20:58:3.44 PST 2012
Fri Nov 30 10:27:50:742 PST 2012

我对 Perl 很陌生,但这里的每个人都在使用它,所以我正在努力快速学习它(刚刚开始这项工作)。我需要能够比较时间戳(我正在合并时间可能重叠的日志文件,并且需要在结果文件中连续的所有时间戳)。 这是我提取时间并将其格式化为我可以比较的字符串的子例程:

my %months = ( 'Jan'=>1, 'Feb'=>2, 'Mar'=>3, 'Apr'=>4, 'May'=>5, 'Jun'=>6, 'Jul'=>7,
'Aug'=>8, 'Sep'=>9, 'Oct'=>10, 'Nov'=>11, 'Dec'=>12);

sub to_comparable {
    my $date = shift;
    my ($mmm, $d, $H, $M, $S, $mils, $fra, $tz, $Y) = $date =~ 
        m{^<\w{3} (\w{3}) (\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2}):(\d{1,3})[.]{0,1}(\d{0,2}) (\w{3}) (\d{4})>}
            or return undef;
    if ($mils eq "") { $mils = 0; }
    if ($fra eq "") { $fra = 0; }
    my $m = $months{$mmm};
    return sprintf('%04d%02d%02d%02d%02d%02d%03d%02d',$Y,$m,$d,$H,$M,$S,$mils,$fra);
}

只要时间戳都来自同一个时区,它就可以正常工作。但是,我想确保它们适用于与标准时间和夏令时的更改重叠的日志(或者如果我从其他时区获取日志)。我想也许 DateTime 包可以解决问题,但我对如何使用时区来获得可比较的时间感到困惑。除了用于时区的内容外,我可以创建一个日期/时间对象。 在我的测试中,我在映射月份后添加了以下内容:

    my $ns = sprintf('%03d.%02d',$mils,$fra);
    $ns *= 1000;

    my $dt = DateTime->new(
      year       => $Y,
      month      => $m,
      day        => $d,
      hour       => $H,
      minute     => $M,
      second     => $S,
      nanosecond => $ns,
      time_zone  => "$tz",
  );

这会导致错误“无效偏移:PST”。 我找到了注释:“强烈建议您不要将这些名称用于显示以外的任何用途。这些名称不是官方名称,其中许多只是奥尔森数据库维护者的发明。而且,这些名称不是唯一的。例如,在 -0500 和 +1000/+1100 处都有一个“EST”。 和其他地方:“时区的短名称不是唯一的,因此任何从此类名称确定实际时区的尝试都涉及猜测。请改用长名称。”

我无法控制给定的时区显示,所以我现在不知道该怎么做。如果我使用“PST8PDT”或“America/Los_Angeles”,如何指示给定时间是标准时间还是夏令时?美国的时区是否可以转换为 DateTime 将接受的时区?有人可以帮我解决这个问题吗?我合并日志文件的看似微不足道的项目一直在进行,我的老板认为我是个白痴。 :-(

【问题讨论】:

  • 你不是白痴,你很小心。有些人会忽略这一点,然后其他人将不得不找到他们的错误。

标签: perl datetime


【解决方案1】:

很遗憾,这么多日志文件使用的时间戳格式选择如此糟糕。我推荐RFC 3339(这也是ISO 8601 格式之一)。

不管怎样,直接说问题。

创建一个映射,将源系统的标识符定义转换为构造函数的time_zone 参数接受的标准名称或偏移量。

my %time_zones = (
   EST => '-0500',
   PST => '-0800',
   PDT => '-0700',
   ...
);

然后只需使用 time_zone 参数传递偏移量。

$ perl -MDateTime -E'say
   DateTime->new(
      year => 2012, month => 11, day => 4,
      hour => 1, minute => 16, second => 0,
      time_zone => "-0800",
   )->epoch;
'
1352020560

$ perl -MDateTime -E'say
   DateTime->new(
      year => 2012, month => 11, day => 4,
      hour => 1, minute => 16, second => 0,
      time_zone => "-0700",
   )->epoch;
'
1352016960

$ perl -E'say 1352020560 - 1352016960'
3600

【讨论】:

  • 时代愚蠢比遗憾更糟糕。 :-) 如果我将所有时间(当前使用 PST 或 PDT)转换为使用“America/Los_Angeles”,它怎么可能知道在秋季重复小时内某个时间是 PST 还是 PDT?例如,如果我在“America/Los_Angeles”时区创建 2012 年 11 月 4 日凌晨 1:15 的 DateTime 对象,它如何知道时间是标准时间还是夏令时?
  • 如果我理解你写的内容,我可能会。 :-) 对不起,我对 Perl 很陌生,我不明白。在任何情况下,我都不想从 DateTime 对象中获取偏移量。使用 DateTime->new 创建 DateTime 对象不允许输入偏移量,因此我仍然看不到如何将标准与夏令时放入对象中。因此,如果我有 2012 年 11 月 4 日凌晨 1:16 PST 和 2012 年 11 月 4 日凌晨 1:15 PDT 的时间,我如何将它们转换为一种格式,它会告诉我 PDT 时间在 PST 之后时间?
  • 非常感谢!我之前没有建立哈希表的连接。澄清确实有帮助。我已经以这种方式实现了它(将 PST、PDT 等散列到时区偏移量)并且它现在可以工作了。耶!哦,但是嘘!我太新了,无法投票给你的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-05
  • 2011-09-23
  • 2022-06-20
  • 1970-01-01
  • 2017-12-18
  • 2013-02-04
  • 1970-01-01
相关资源
最近更新 更多