如何在Perl中将多个时区的unix日期输出转换为UTC?

时间:2010-10-12 18:01:24

标签: perl date timezone

在Perl中,如何有效地解析unix的date命令的输出,考虑时区,还转换为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。

非常感谢任何帮助。

6 个答案:

答案 0 :(得分:5)

关于时区的Perl DateTime FAQ有很好的背景知道为什么在大多数转换中不能使用EDT和EST。问题是其他国家的东部时区也有相同的3个字母缩写。 EST EDT模糊不清,没有其他线索。

您可以查看other modules,或者只是假设“EDT”与“EST5EDT”相同,如果这是真的。

答案 1 :(得分:5)

如果您知道如何消除TZ的歧义,只需将它们弹出到调度表中即可:

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);
}

答案 2 :(得分:3)

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

即。如果您知道输入中的EDT意味着America / New_York,请执行以下操作:

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

而不是

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

用于您的时区规范使用

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

答案 3 :(得分:1)

我总是发现Date :: 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

似乎是你正在寻找的东西。

答案 4 :(得分:0)

我有点迟到,但GNU date本身擅长解析日期:

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

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

答案 5 :(得分:0)

我同意Jander on date命令。 -d和-u非常棒,可以节省很多代码行。