2010-10-12 13 views
5

En Perl, ¿cómo se podría analizar eficientemente el resultado del comando de fecha de Unix, teniendo en cuenta la zona horaria, y también convertir a UTC?¿Cómo puedo convertir la salida de fecha de Unix en varias zonas horarias a UTC, en Perl?

He leído muchas preguntas similares en stackoverflow, pero pocas parecen tener en cuenta el análisis de varias zonas horarias. En su lugar, parece que configuran la zona horaria de forma manual y suponen que permanece fija.

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

He intentado lo siguiente en vano:

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

Parece que el problema es la zona horaria (% Z). Además, parece que no existe un campo de zona horaria en Time :: Piece, que me obligaría a escribir código personalizado para convertirlo a UTC, lo que parece ... incorrecto.

Contexto: Estoy intentando analizar registros heredados de una variedad de fuentes que usan el comando de fecha de Unix para marcas de tiempo. Idealmente, me gustaría convertir todas las marcas de tiempo a UTC.

Cualquier ayuda sería muy apreciada.

+3

El problema fundamental que tendrá aquí es que las abreviaturas de zona horaria de 3 letras no son únicas. El significado de "EST" depende del país en el que se encuentre. – cjm

+0

Gracias por señalar la ambigüedad. Puedo sustituir lo que sé que es el nombre largo correcto (EST5EDT es correcto para ese registro). Sin embargo, todavía no estoy seguro de cómo capturar la zona horaria sin escribir código personalizado para extraer la zona horaria y luego configurar la zona horaria manualmente en mi módulo de elección (DateTime se ve bastante bien), con la esperanza de asignarme a la cadena correcta. – vlee

Respuesta

5

Si usted sabe cómo eliminar la ambigüedad de las Tzs, simplemente estallar en una mesa de despacho:

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

Gracias, su código definitivamente funciona. Sin embargo, ahora estoy más confundido acerca del identificador% Z. En su código, se crea un nuevo DateTime :: Format :: Strptime para EDT (EST5EDT) y BST (+0100) zonas horarias, en lugar de usar el mismo objeto y analizar toda la cadena con parse_datetime. Intenté "Tue Oct 12 08:00:00 GMT 2010" que funcionaba con el objeto predeterminado. Sin embargo, cuando pruebe "UTC" o "EST5EDT" el obje predeterminado ct croaks con "No reconozco la zona horaria ". Supongo que este es el comportamiento esperado, pero no estoy seguro de por qué. Me pregunto cuáles son las cadenas de zonas horarias reconocibles/aceptables para% Z. – vlee

+0

El analizador Strptime toma una cadena que, si incluye la zona horaria, el analizador intenta pasarla a DateTime :: TimeZone. Si la cadena no incluye la zona horaria, entonces el constructor del analizador necesita el parámetro 'time_zone'. También me costó trabajo encontrar los nombres de zona horaria apropiados, no ambiguos. Básicamente, cualquier cosa de la forma '[- +] \ d {4}' funciona. Espero que esto ayude. –

+0

+1: Esa es una buena respuesta ... – dawg

5

El Perl DateTime FAQ en zonas horarias tiene una buena información sobre por qué EDT y EST no se pueden usar en la mayoría de las conversiones. El problema es que otros países también tienen una zona horaria oriental con la misma abreviatura de 3 letras. EST EDT es ambiguo sin otras pistas.

Puede consultar other modules, o simplemente asumir que "EDT" es lo mismo que "EST5EDT" si eso es cierto.

+0

Agregué "$ input = ~ s/EDT/EST5EDT /;" antes de llamar a strptime, pero strptime aún no analiza la cadena. Además, sigo creyendo que Time :: Piece es insuficiente ya que no almacena la zona horaria, solo "permite" que pase a través de la cadena FORMAT :( – vlee

+0

¡Muchas gracias por señalar la corta ambigüedad del nombre de la zona horaria! – vlee

+1

@vlee: Es posible que necesite usar otro módulo. Hay muchos módulos de CPAN en el grupo 'DateTime :: Format :: *'. – dawg

1

Siempre he encontrado Fecha :: :: Manip parseDate a ser bueno para este tipo de situaciones.

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

Cuando se ejecuta, que produce esto:

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 

que parecen ser lo que estás buscando.

0

estoy un poco tarde en esto, pero GNU date sí es bueno en las fechas de análisis: se

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

No sé cómo se resuelve la ambigüedad EDT sin embargo.

3

Si está utilizando Date :: Time :: Strptime, puede usar %O para el nombre de Olson Time Zone y realizar una corrección manual antes de realizar el análisis.

i.e.si sabe que EDT en su medio de entrada America/New_York, hacer esto:

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

y en lugar de

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

de su tiempo de uso de especificaciones zona

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

+0

¿Sabe dónde están documentados esos formiatos? ¿Cómo los consigue? –

0

Estoy de acuerdo con ingenio h Jander en el comando de fecha. -d y -u son geniales y guardan muchas líneas de código.

Cuestiones relacionadas