2010-10-12 48 views
5

在Perl中,如何高效地解析unix的date命令的输出,并考虑到时区并转换为UTC?如何在Perl中将多个时区的unix日期输出转换为UTC?

我已阅读了很多关于stackoverflow的类似问题,但似乎很少考虑解析多个时区。相反,他们似乎手动设置时区并假定它保持不变。

# Example Input Strings: 
my @inputs = (
       'Tue Oct 12 06:31:48 EDT 2010', 
       'Tue Oct 12 07:49:54 BST 2010', 
      ); 

我尝试以下无济于事:

foreach my $input (@inputs) { 
    my $t = Time::Piece->strptime($input, 
           '%a %b %d %T %Z %Y'); 
    print $t->cdate, "\n"; 
} 

看来问题是时区(%Z)。此外,时区字段似乎并不存在于Time :: Piece中,这需要我编写自定义代码才能转换为UTC,这看起来......错了。

上下文: 我试图解析来自各种使用unix date命令的时间戳记源的遗留日志。理想情况下,我想将所有时间戳转换为UTC。

任何帮助将不胜感激。

+3

您将在这里遇到的根本问题是3个字母的时区缩写不是唯一的。 “EST”的含义取决于您所在的国家。 – cjm 2010-10-12 18:14:27

+0

谢谢您指出含糊之处。我可以替换我所知道的正确的长名称(EST5EDT对于该日志是正确的)。不过,我仍然不确定如何在不编写自定义代码的情况下提取时区,然后在我选择的模块中手动设置时区(DateTime看起来相当不错),希望能够映射到正确的字符串。 – vlee 2010-10-12 18:38:47

回答

5

如果你知道如何来澄清对TZS,只是弹出他们到调度表:

use strict; use warnings; 
use DateTime::Format::Strptime(); 

my @inputs = (
    'Tue Oct 12 06:31:48 EDT 2010', 
    'Tue Oct 12 07:49:54 BST 2010', 
); 

my %tz_dispatch = (
    EDT => build_parser('EST5EDT'), 
    BST => build_parser('+0100'), 
    # ... etc 
    default => build_parser(), 
); 

for my $input (@inputs) { 
    my ($parser, $date) = parse_tz($input, %tz_dispatch); 
    print $parser->parse_datetime($date), "\n"; 
} 

sub build_parser { 
    my ($tz) = @_; 

    my %conf = (
     pattern => '%a %b %d %T %Z %Y', 
     on_error => 'croak', 
    ); 
    @conf{qw/time_zone pattern/} = ($tz, '%a %b %d %T %Y') 
    if $tz; 

    return DateTime::Format::Strptime->new(%conf); 
} 

sub parse_tz { 
    my ($date, %tz_dispatch) = @_; 
    my (@date) = split /\s/, $date; 

    my $parser = $tz_dispatch{splice @date, 4, 1}; 

    return $parser 
    ? ($parser, join ' ', @date) 
    : ($tz_dispatch{default}, $date); 
} 
+0

谢谢,你的代码绝对有效。但是现在我对%Z标识符更加困惑。在你的代码中,为EDT创建了一个新的DateTime :: Format :: Strptime EST5EDT)和BST(+0100)时区,而不是使用相同的对象,并使用parse_datetime解析整个字符串,我尝试过“Tue Oct 12 08:00:00 GMT 2010”,它与默认对象一起工作。尝试“UTC”或“EST5EDT”作为默认对象ct咕with着“我不认识时区”。我猜这是预期的行为,但我不知道为什么。我想知道%Z的可识别/可接受的时区字符串是什么。 – vlee 2010-10-13 14:36:54

+0

Strptime解析器需要一个字符串,如果它包含时区,解析器会尝试将它传递到DateTime :: TimeZone。如果字符串不包含时区,则解析器构造函数需要'time_zone'参数。我也很难找出适当的,不含糊的时区名称。基本上,任何形式为'[ - +] \ d {4}'的工作。希望这可以帮助。 – 2010-10-13 18:32:51

+0

+1:这是一个很好的答案... – dawg 2010-10-15 18:23:06

5

时区中的Perl DateTime FAQ有很好的背景说明为什么EDT和EST不能用于大多数转换。问题在于其他国家也有一个东部时区,具有相同的3字母缩写。没有其他线索的情况下,东部时间EDT是模棱两可的。

你可能会看other modules,或者假设“EDT”与“EST5EDT”相同,如果是的话。

+0

我加了“$ input =〜s/EDT/EST5EDT /;”在调用strptime之前,但strptime仍然无法解析字符串。另外,我仍然相信Time :: Piece是不够的,因为它不存储时区,它只允许它通过FORMAT字符串:( – vlee 2010-10-12 18:34:08

+0

非常感谢您指出短时区名称模糊不清! – vlee 2010-10-12 18:34:39

+1

@vlee:你可能需要使用另一个模块,'DateTime :: Format :: *'组中有很多CPAN模块。 – dawg 2010-10-12 18:38:01

1

我总是发现日期:: MANIP :: ParseDate是良好的,这些各种各样的情况。

use strict; 
use warnings qw<FATAL all>; 
use Date::Manip qw<ParseDate UnixDate>; 

my @inputs = (
    q<Tue Oct 12 06:31:48 EDT 2010>, 
    q<Tue Oct 12 07:49:54 BST 2010>, 
); 

sub date2epoch($) { 
    my $user_string = shift(); 
    my $timestamp = ParseDate($user_string); 
    my $seconds  = UnixDate($timestamp, "%s"); 
    return $seconds; 
} 

sub epoch2utc($) { 
    my $seconds = shift(); 
    return gmtime($seconds) . q< UTC>; 
} 

for my $random_date (@inputs) { 
    my $epoch_seconds = date2epoch($random_date); 
    my $normal_date = epoch2utc($epoch_seconds); 
    print "$random_date == $normal_date\n"; 
} 

运行时,产生这样的:

Tue Oct 12 06:31:48 EDT 2010 == Tue Oct 12 10:31:48 2010 UTC 
Tue Oct 12 07:49:54 BST 2010 == Tue Oct 12 06:49:54 2010 UTC 

这似乎是你在找什么。

0

我对这个有点晚,但GNU date本身就是善于分析日期:

$ date -u -d 'Thu Oct 14 01:17:00 EDT 2010' 
Thu Oct 14 05:17:00 UTC 2010 

我不知道它是如何解决的EDT歧义虽然。

3

如果您使用的是Date :: Time :: Strptime,则可以使用%O作为Olson时区名称,并在解析之前执行手动修正。

即如果你知道是EDT在你的输入是指美国/纽约,这样做:你的时区规范使用

$time_in =~ s{EDT}{America/New_York};

,取而代之的

%a %b %d %T %Z %Y

%a %b %d %T %O %Y

+0

你知道那些甲酸盐记录在哪里吗?你是如何得到它们的? – 2012-11-26 12:01:37

0

我同意智慧h Jander在日期命令。 -d和-u非常好,节省了很多代码行。

相关问题