2010-10-12 12 views
5

In Perl, come si analizzerebbe in modo efficiente l'output del comando data di unix, tenendo conto del fuso orario e convertendolo in UTC?Come posso convertire l'output di data unix su più fusi orari su UTC, in Perl?

Ho letto molte domande simili su StackOverflow, ma poche sembrano prendere in considerazione l'analisi di fusi orari multipli. Invece sembrano impostare manualmente il fuso orario e assumerlo per rimanere fisso.

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

Ho provato quanto segue senza alcun risultato:

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

sembra che il problema è il fuso orario (% Z). Inoltre, un campo di fuso orario non sembra esistere in Time :: Piece, il che richiederebbe che io scriva codice personalizzato per convertire in UTC, il che sembra ... sbagliato.

Contesto: Sto tentando di analizzare i registri legacy da varie fonti che utilizzano il comando data di unix per i timestamp. Idealmente, mi piacerebbe convertire tutti i timestamp in UTC.

Qualsiasi aiuto sarebbe molto apprezzato.

+3

Il problema fondamentale che si avrà qui è che le abbreviazioni del fuso orario di 3 lettere non sono uniche. Il significato di "EST" dipende dal paese in cui ti trovi. – cjm

+0

Grazie per aver sottolineato l'ambiguità. Posso sostituire quello che so essere il nome lungo corretto (EST5EDT è corretto per quel registro). Tuttavia, non sono ancora sicuro di come catturare il fuso orario senza scrivere codice personalizzato per estrarre il fuso orario e quindi impostare manualmente il fuso orario nel mio modulo di scelta (DateTime sembra abbastanza bello), sperando che mappassi la stringa corretta. – vlee

risposta

5

Se sai come per disambiguare le TZ, semplicemente pop in una tabella la spedizione:

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

Grazie, il tuo codice funziona sicuramente, ma ora sono più confuso sull'identificatore% Z. Nel tuo codice, un nuovo DateTime :: Format :: Strptime viene creato per EDT (EST5EDT) e BST (+0100) fusi orari, invece di utilizzare lo stesso oggetto e analizzare l'intera stringa con parse_datetime. Ho provato "Tue Oct 12 08:00:00 GMT 2010" che ha funzionato con l'oggetto predefinito. prova "UTC" o "EST5EDT" l'oggetto predefinito ct croaks con "Non riconosco il fuso orario ". Immagino che questo sia un comportamento previsto, ma non sono sicuro del perché. Mi chiedo quali siano le stringhe timezone riconoscibili/accettabili per% Z. – vlee

+0

Il parser Strptime prende una stringa che, se include il fuso orario, il parser tenta di passarlo a DateTime :: TimeZone. Se la stringa non include il fuso orario, il costruttore del parser necessita del parametro 'time_zone'. Ho anche avuto difficoltà a capire i nomi appropriati, non ambigui, relativi al fuso orario. Fondamentalmente, qualsiasi cosa del modulo '[- +] \ d {4}' funziona. Spero che questo ti aiuti. –

+0

+1: questa è una buona risposta ... – dawg

5

Il Perl DateTime FAQ su fusi orari ha una buona conoscenza del motivo per cui EDT ed EST non possono essere utilizzati nella maggior parte delle conversioni. Il problema è che altri paesi hanno anche un fuso orario orientale con la stessa lettera di 3 lettere. EST EDT è ambiguo senza altri indizi.

Si potrebbe guardare other modules, o semplicemente assumere che "EDT" è la stessa di "EST5EDT" se è vero.

+0

Ho aggiunto "$ input = ~ s/EDT/EST5EDT /;" prima di chiamare strptime, ma strptime non riesce ancora ad analizzare la stringa. Inoltre, ritengo che Time :: Piece non sia sufficiente poiché non memorizza il fuso orario, lo "consente" solo di passare attraverso la stringa FORMAT :( – vlee

+0

Grazie mille per aver sottolineato l'ambiguità del nome timezone breve però! – vlee

+1

@vlee: potrebbe essere necessario utilizzare un altro modulo Ci sono molti moduli CPAN nel gruppo 'DateTime :: Format :: *' – dawg

1

Ho sempre trovato Data :: Manip :: parsedate per essere buono per questo tipo di situazioni.

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

Quando viene eseguito, che produce questo:

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 

che sembrano essere quello che stai cercando.

0

Sono un po 'in ritardo su questo, ma GNU date sé è bravo a date parsing:

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

Io non so come si risolve l'ambiguità EDT però.

3

Se si utilizza Date :: Time :: Strptime, è possibile utilizzare %O per il nome del fuso orario di Olson ed eseguire una correzione manuale prima di analizzare.

, ad es.se sa che EDT nel vostro ingresso significa America/New_York, fare questo:

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

invece di

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

per il vostro tempo uso zona spec

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

+0

Sai dove sono documentati quei formandi? Come li hai ottenuti? –

0

Sono d'accordo h Jander su comando data. -d e -u sono fantastici e risparmiano un sacco di linee di codice.

Problemi correlati