3

我有具有时间戳的日志文件,例如:

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 将接受的时区?有人可以帮我解决这个问题吗?我合并日志文件的看似微不足道的项目一直在进行,我的老板认为我是个白痴。:-(

4

1 回答 1

7

可惜这么多日志文件使用的时间戳格式选择如此糟糕。我推荐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
于 2012-12-11T00:47:11.893 回答