需要比较Perl的时间;如何处理时区?

时间:2012-12-11 00:33:05

标签: perl datetime

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

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”。 我发现了这个说明:“强烈建议您不要将这些名称用于显示以外的任何名称。这些名称不是官方的,其中许多只是Olson数据库维护者的发明。而且,这些名称不是例如,-0500和+ 1000 / + 1100都有一个“EST”。“ 和其他地方一样:“时区的短名称并不是唯一的,所以任何从这个名称确定实际时区的尝试都需要猜测。请改用长名称。”

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

1 个答案:

答案 0 :(得分: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