2008-09-18 26 views
36

Dada una fecha/hora como una matriz de (año, mes, día, hora, minuto, segundo), cómo la convertiría a hora de época, es decir, la cantidad de segundos desde 1970-01-01 00:00: 00 GMT?¿Cómo convierto una fecha/hora a hora de época (tiempo Unix/segundos desde 1970) en Perl?

Pregunta adicional: Si se le da la fecha/hora como una cadena, ¿cómo la analizaría primero en la matriz (y, m, d, h, m, s)?

+1

Esta pregunta tiene extraños votos a favor – jdelator

+2

advertencia obligatoria: Unix Epoch solo se define para los años 1901 - 2038. – Piskvor

+5

@Piskvor: Eso no es estrictamente cierto. Y la época se define como el 1 de enero de 1970, no como un rango. Sin embargo, ese es el mejor rango que obtendrá para una marca de tiempo UNIX si lo representa en un entero de 32 bits (que es una convención generalizada). –

Respuesta

19

Esta es la forma más sencilla de obtener el tiempo UNIX:

use Time::Local; 
timelocal($second,$minute,$hour,$day,$month-1,$year); 

Nota el orden inverso de los argumentos y que enero es el mes 0. Para muchas más opciones, vea el módulo de CPAN DateTime.

En cuanto al análisis sintáctico, vea el módulo Date::Parse de CPAN. Si realmente necesita hacerse elegante con el análisis de fecha, el Date::Manip puede ser útil, aunque su propia documentación lo advierte, ya que lleva una gran cantidad de equipaje (sabe cosas como feriados comerciales comunes, por ejemplo) y otras soluciones son mucho Más rápido.

Si sabe algo sobre el formato de las fechas/horas que analizará, una simple expresión regular puede ser suficiente, pero probablemente sea mejor utilizar un módulo de CPAN adecuado. Por ejemplo, si sabe que las fechas siempre estarán en orden YMDHMS, use el módulo CPAN DateTime::Format::ISO8601.


Por mi propia referencia, si no otra cosa, a continuación es una función que uso para una aplicación en la que sé las fechas estarán siempre en orden YMDHMS con la totalidad o parte de la parte "HMS" opcional. Acepta cualquier delimitador (p. Ej., "2009-02-15" o "2009.02.15"). Devuelve el tiempo Unix correspondiente (segundos desde 1970-01-01 00:00:00 GMT) o -1 si no pudo analizarlo (lo que significa que es mejor asegurarse de que nunca necesitará legítimamente analizar la fecha 1969- 12-31 23:59:59). También presume que los dos dígitos del año XX hasta "69" se refieren a "20XX", de lo contrario "19XX" (por ejemplo, "50-02-15" significa 2050-02-15 pero "75-02-15" significa 1975- 02-15).

use Time::Local; 

sub parsedate { 
    my($s) = @_; 
    my($year, $month, $day, $hour, $minute, $second); 

    if($s =~ m{^\s*(\d{1,4})\W*0*(\d{1,2})\W*0*(\d{1,2})\W*0* 
       (\d{0,2})\W*0*(\d{0,2})\W*0*(\d{0,2})}x) { 
    $year = $1; $month = $2; $day = $3; 
    $hour = $4; $minute = $5; $second = $6; 
    $hour |= 0; $minute |= 0; $second |= 0; # defaults. 
    $year = ($year<100 ? ($year<70 ? 2000+$year : 1900+$year) : $year); 
    return timelocal($second,$minute,$hour,$day,$month-1,$year); 
    } 
    return -1; 
} 
+0

¿Qué ventaja le da a Time :: Local que POSIX (strftime) más común no lo haga? –

+3

strftime es para dar salida al tiempo, timelocal es 'ingresarlos'. –

+1

$ month-1 para el mes indexado cero !!!!!! Trataré de no olvidar eso de nuevo. –

33

Si está utilizando el módulo DateTime, puede llamar al método epoch() en un objeto DateTime, ya que eso es lo que piensa de como el tiempo UNIX.

El uso de DateTimes le permite convertir bastante fácilmente objetos de época, hasta la fecha.

Alternativly, localtime y gmtime convertirá una época en una matriz que contiene el día de mes y el año, y timelocal y timegm del Time::Local module hará lo contrario, la conversión de una serie de elementos de tiempo (segundos, minutos, días, ... , meses, etc.) en una época.

+0

Siempre es mejor utilizar una función de biblioteca para realizar una operación común que rodar su propia solución (con errores). –

+0

La solución 'timelocal' a continuación es mejor porque DateTime no es parte de una instalación estándar de Perl –

-4

Si solo está buscando una utilidad de línea de comandos (es decir, no es algo que reciba llamadas de otras funciones), pruebe esta secuencia de comandos. Se supone la existencia de la fecha de GNU (presente en casi cualquier sistema Linux):

#! /usr/bin/perl -w 

use strict; 

$_ = (join ' ', @ARGV); 
$_ ||= <STDIN>; 

chomp; 

if (/^[\d.]+$/) { 
    print scalar localtime $_; 
    print "\n"; 
} 
else { 
    exec "date -d '$_' +%s"; 
} 

Así es como funciona:

$ Time now 
1221763842 

$ Time yesterday 
1221677444 

$ Time 1221677444 
Wed Sep 17 11:50:44 2008 

$ Time '12:30pm jan 4 1987' 
536790600 

$ Time '9am 8 weeks ago' 
1216915200 
+0

Ese es el tipo de cosa que debería ser un script de shell, o debería usarse para usar un módulo Perl adecuado para hacer el trabajo en mi humilde opinión . –

+0

¿Por qué reescribirlo como un script de shell? Funciona perfectamente tal como está y me resulta extremadamente útil sobre una base diaria. – raldi

+0

No es una solución perl si se basa en el comando de fecha del sistema. No funciona en Unix o Windows. – kkoolpatz

-3

Un filtro que convierte cualquier fecha en varios formatos relacionados con ISO (¿y quién usaría algo más después de leer the writings de Mighty Kuhn?) En la entrada estándar a segundo-desde-el-efeméride de tiempo en la salida estándar podría servir para ilustrar las dos partes:

[email protected]:~$ cat `which isoToEpoch` 
#!/usr/bin/perl -w 
use strict; 
use Time::Piece; 
# sudo apt-get install libtime-piece-perl 
while (<>) { 
    # date --iso=s: 
    # 2007-02-15T18:25:42-0800 
    # Other matched formats: 
    # 2007-02-15 13:50:29 (UTC-0800) 
    # 2007-02-15 13:50:29 (UTC-08:00) 
    s/(\d{4}-\d{2}-\d{2}([T ])\d{2}:\d{2}:\d{2})(?:\.\d+)? ?(?:\(UTC)?([+\-]\d{2})?:?00\)?/Time::Piece->strptime ($1, "%Y-%m-%d$2%H:%M:%S")->epoch - (defined ($3) ? $3 * 3600 : 0)/eg; 
    print; 
} 
[email protected]:~$ 
2

Mi analizador de fecha y hora favorita es DateTime::Format::ISO8601 Una vez que tenga que trabajar, tendrá un DateTime objeto, fácilmente convertible a Epoch segundos con época()

6

Get Date::Manip from CPAN, entonces:

use Date::Manip; 
$string = '18-Sep-2008 20:09'; # or a wide range of other date formats 
$unix_time = UnixDate(ParseDate($string), "%s"); 

edición:

Fecha :: i Manip es grande y lento, pero muy flexible en el análisis sintáctico, y es puro perl. Úselo si tiene prisa cuando escribe código y sabe que no tendrá prisa cuando lo esté ejecutando.

p. Ej. Úselo para analizar las opciones de línea de comando una vez en la puesta en marcha, pero no lo use analizando grandes cantidades de datos en un servidor web ocupado.

Ver the authors comments.

(Gracias al autor del primer comentario más abajo)

+6

No, no lo haga. La documentación de Date :: Manip tiene una gran sección que intenta convencerlo de que no la use. –

+2

Fecha :: Manip te traicionará cuando seas más vulnerable. Use DateTime. :) – Kevin

+0

@AristotlePagaltzis, donde "trata de convencerte de que no lo uses" significa que solo "dice que puede hacer todo con un calendario, pero permite que muchos otros módulos puedan realizar muchas cosas menos que todas". Por lo tanto, todo lo que scottc ya dijo, con más énfasis en Date :: Manip es una solución única. –

2

Posiblemente uno de los mejores ejemplos de 'Hay más de un camino para hacer esto", con o sin la ayuda de CPAN.

Si usted tiene control sobre lo que se pasa como una 'fecha/hora', sugiero que vaya por la ruta DateTime, ya sea usando una subclase específica Date :: Time :: Format, o usando DateTime :: Format :: Strptime si hay no es compatible con el formato de fecha loco (ver datetime FAQ para más detalles). En general, Date :: Time es el camino a seguir si quieres hacer algo serio con el resultado: pocas clases en CPAN son tan anal retentivo y obsesivo ively exacto.

Si esperas cosas raras de forma libre, inclúyelo en el método str2time() de Date::Parse, que te dará un valor de segundos después de la época que podrás ejecutar de manera perversa, sin la sobrecarga de Date::Manip .

2

Hay muchos módulos de manipulación de fechas en CPAN. Mi favorito particular es DateTime y puede usar el strptime modules para analizar las fechas en formatos arbitrarios. También hay muchos módulos DateTime :: Format en CPAN para manejar formatos de fecha especializados, pero strptime es el más genérico.

7
$ENV{TZ}="GMT"; 
POSIX::tzset(); 
$time = POSIX::mktime($s,$m,$h,$d,$mo-1,$y-1900); 
+0

hora de época ya está en GMT, no hay zona horaria. Al establecer explícitamente TZ = GMT, ¿no está analizando el tiempo como ** desde ** la zona horaria GMT en lugar de la fecha/hora local? (y creo que debería usar TZ = UTC, GMT es la zona horaria de un país) –

+0

@ ThomasGuyot-Sionnest mi intención era ilustrar que mktime se convierte de hora local a segundos de época, por lo que necesita saber en qué hora local está su entrada y configure TZ en consecuencia (o simplemente suponga que TZ ya está configurado para lo que desea, pero eso es descuidado) – ysth

+0

Creo que es seguro asumir que la mayoría de la gente querrá usar la hora local y eso es lo que está configurado en la computadora/servidor. Entiendo que la zona es importante cuando se trabaja con diferentes regiones, pero creo que mostrar un ejemplo de configuración de TZ a GMT distrae de la respuesta real e incluso puede inducir a los novatos a utilizar la zona horaria incorrecta para la entrada de tiempo. OTOH obtienes un punto de bonificación por llamar a tzset() después de cambiar TZ;) –

2

Para mayor referencia, un un trazador de líneas que se puede aplicar en, por ejemplo, !#/bin/sh scripts.

EPOCH="`perl -e 'use Time::Local; print timelocal('${SEC}','${MIN}','${HOUR}','${DAY}','${MONTH}','${YEAR}'),\"\n\";'`" 

Sólo recuerde para evitar valores octales!

+0

¿Por qué usar Perl cuando puedes usar la fecha de GNU infinitamente más flexible? 'date -d" STRING "+% s' permite casi cualquier cosa, desde" ayer "hasta" 90 días "[después de ahora] a elementos más tradicionales como" 15 de marzo de 2016 "y también se puede exportar a casi cualquier formato (' +% s' es epoch, ver 'date --help' por toneladas más). (Es muy malo Perl 'DateTime' no tiene esta funcionalidad). –

7

Sé que esta es una pregunta antigua, pero pensé en ofrecer otra respuesta.

Time::Piece es núcleo como de Perl 5.9.5

Esto permite el análisis de tiempo en formatos elección a través del método de strptime.

ej .:

my $t = Time::Piece->strptime("Sunday 3rd Nov, 1943", 
           "%A %drd %b, %Y"); 

La parte útil es - porque es un objeto sobrecargado, puede utilizarlo para comparaciones numéricas.

p. Ej.

if ($t < time()) { #do something } 

O si se accede a él en un contexto de cadena:

print $t,"\n"; 

que se obtiene:

Wed Nov 3 00:00:00 1943 

Hay un montón de métodos de acceso que permiten algunas otras transformadas basadas tiempo útil surtidos . https://metacpan.org/pod/Time::Piece

Cuestiones relacionadas