2010-10-12 45 views
10

Tengo una matriz con cadenas n en formato YYYY-MM-DD (Ejemplo, "2010-10-31").Usando Perl, ¿cómo comparo las fechas en forma de YYYY-MM-DD?

¿Cómo comparo una fecha con las cadenas en esta matriz?

Por ejemplo, elimine las cadenas hace más de 30 días?

+1

Por qué votaron abajo? Bastante buena pregunta, ¿sí? – Xailor

+2

Me encanta cómo esta pregunta provocó una discusión entre los puristas y los pragmáticos. – paddy

Respuesta

5
use strict; use warnings; 
use DateTime(); 
use DateTime::Duration(); 
use DateTime::Format::Natural(); 

my $parser = DateTime::Format::Natural->new; 
my $now = DateTime->now; 
my $delta = DateTime::Duration->new(days => 30); 
my $cutoff = $now->subtract_duration($delta); 

my @new_dates = map { $_->[1] } 
       grep { -1 == $_->[0] } 
       map { 
        chomp; 
        [ 
         DateTime->compare(
          $parser->parse_datetime($_), 
          $cutoff 
         ), 
         $_ 
        ] 
       } <DATA>; 

print "@new_dates"; 

__DATA__ 
2010-07-31 
2010-08-31 
2010-09-30 
2010-10-31 
+1

Simplemente use 'my $ now = DateTime-> now;' No es necesario analizar el resultado de 'localtime'. – daotoad

+0

Gracias daotoad. –

+0

OP buscando el formato 'YYYY-MM-DD', no' YYYY/MM/DD' –

1

Un buen comienzo es leer The Many Dates of Perl y el sitio DateTime.

El formato YYYY-MM-DD es una forma de representación de fecha de ISO 8601. Hay variantes de él que se consideran aceptables, como YYYY-MM-DD y YYYYMMDD e incluso YYMM en datos anteriores. Debería ver un definitive list antes de elegir un método para comparar estas fechas.

Si las cadenas de fechas de ISO 8601 son: 1) fechas válidas; 2) en el mismo formato con o sin el delimitador -; 3) carente de espacio en blanco inicial y final, una propiedad atractiva es que puede ordenar o comparar las cadenas con simples comparaciones de cadenas lexicográficas.

En general, entonces:

  1. IFF no se va a comprobar si las fechas son válidas y IFF que son el mismo formato, y IFF no hay espacios en blanco iniciales o finales , puede comparar con otra cadena que representa la fecha objetivo en ese mismo formato.

--- --- De lo contrario

  1. Decidir sobre un módulo CPAN para analizar su cadena de fecha (o coincidir con usted mismo),

  2. Convertir a efeméride del instante si si las fechas están en ese rango, (o usan un módulo CPAN que hace una manipulación de fecha/hora a mayor escala como Date :: Manip o Date :: Calc)

  3. Realice la aritmética en el tipo de tiempo (época, días absolutos, whateve r)

  4. convertir el tiempo de nuevo en el formato que desea ...

Aquí está el código que hace que:

use warnings; use strict; 
use Date::Calc qw/:all/; 

my (@date_strings, @abs_days); 

my $target=Date_to_Days(2010, 1, 15); 

# set @date_string to "YYYY-MM-DAY" between some dates 
for my $AbsDay(Date_to_Days(2009,1,1)..Date_to_Days(2011,12,31)) { 
    my ($year, $mon, $day)=Add_Delta_Days(1,1,1,$AbsDay-1); 
    my $s="$year-$mon-$day"; 
    push @date_strings, $s; 
} 

foreach my $s (@date_strings) { 
    my ($year, $mon, $day); 

    if(($year, $mon, $day)=$s=~/^(\d+)-(\d+)-(\d+)/) { 
     my $days=Date_to_Days($year, $mon, $day); 
     push @abs_days, $days 
      if ($target-$days <= 30 && $target-$days >= -30); 
    } 
} 

print "absolute day way:\n"; 
foreach my $days (@abs_days) { 
    my ($year, $mon, $day)=Add_Delta_Days(1,1,1,$days-1); 
    print "$year-$mon-$day\n"; 
} 
+0

¡Demasiado trabajo! :) –

+0

@brian d foy: Respetuosamente, no estoy de acuerdo. Mi código aquí no es tan hermoso como para estar seguro, pero usar una biblioteca de CPAN bien probada como Date :: Calc (escrita en C) o Date :: Time vale la pena. ¡La mayor parte del trabajo aquí solo genera las cuerdas para probar! : -} Con la solución lexicográfica que defiende, debe verificar (o esperar) que la entrada tiene los mismos delimitadores, es una fecha válida y no tiene espacios en blanco iniciales o finales. Una vez que haces eso, ¿es realmente menos trabajo? – dawg

+1

Sí, he estado en situaciones en las que es mucho menos trabajo, y la diferencia entre terminar en un tiempo razonable y nunca. –

1

Usted puede utilizar Time::ParseDate módulo,

use strict; 
use warning; 
use Time::ParseDate; 

my @dates = ('2010-10-12', '2010-09-14', '2010-08-12', '2010-09-13'); 
my @dates = 
    grep {parsedate($_, NO_RELATIVE => 1, UK => 1) > parsedate('-30 days') }@dates; 
#output: 2010-10-12 2010-09-14 
8

Guess hay más de una manera de hacerlo, pero me gusta Date::Simple para cosas como esta ..

Un ejemplo de los documentos:

use Date::Simple ('date', 'today'); 

# Difference in days between two dates: 
$diff = date('2001-08-27') - date('1977-10-05'); 

# Offset $n days from now: 
$date = today() + $n; 
print "$date\n"; # uses ISO 8601 format (YYYY-MM-DD) 

Es muy bueno para hacer aritmética en los objetos ++.

fechas Sólo sin embargo, no hay horas, minutos o segundos

14

La gran cosa acerca de YYYY-MM-DD fechas con formato es que se puede comparar el uso de comparación de cadena sencilla. En Perl, esos son los operadores lt y gt.

En este caso, parece que solo desea comprobar si las fechas en la matriz son anteriores o posteriores a una fecha objetivo determinada (que simplemente es "hace 30 días"). Para ese caso, el enfoque que tomaría sería primero determinar cuál era la fecha de hace 30 días y luego compararlo como una cadena contra cada fecha en la matriz. No introduciría la sobrecarga de convertir todas las cadenas YYYY-MM-DD en objetos de fecha "adecuados", tiempos de época, etc. y solo para probar, que representa la fecha anterior.

#!/usr/bin/env perl 

use strict; 
use warnings; 

my $thirty_days = 30 * 24 * 60 * 60; 
my ($old_day, $old_month, $old_year) = (localtime(time - $thirty_days))[3..5]; 
my $cutoff = sprintf('%04d-%02d-%02d', 
        $old_year + 1900, $old_month + 1, $old_day); 

my @dates = ('2010-10-12', '2010-09-12', '2010-08-12', '2010-09-13'); 
for my $date (@dates) { 
    print "$date\n" if $date gt $cutoff; 
} 
+2

Estoy sorprendido de que tanta gente saltara a soluciones tan complicadas e imbéciles. Tenemos un artículo, esta es solo esta solución en _Programación efectiva de Perl_. :) –

+0

Esto supone que no hay errores en las fechas de origen, como '2010-02-29' que se comparará lexicográficamente, pero aún así será una fecha errónea. Un paquete de fecha señalaría el error de fecha de origen, que parecería ser parte del trabajo del guión, ¿no? – dawg

+0

La prueba '$ date gt $ cutoff' fallará con: 1) delimitadores que no sean' -' utilizados en 'sprintf'; 2) con espacios en blanco iniciales en cadenas que deben coincidir; 3) espacios en blanco al final con cadenas que no deberían coincidir. Casi todas las bibliotecas de análisis de fecha CPAN manejarán esas 3 situaciones y muchas más. Una vez que verifica los delimitadores y elimina los espacios en blanco iniciales y finales, ¿está realmente ahorrando tanto "sobrecarga" o código? Date :: Calc está escrito en C y es muy rápido. Fecha :: El tiempo es increíblemente rico en funciones. Ambos verifican las entradas de fecha válidas, donde este método no lo hace. – dawg

1

Lo hice así, tipo de detallado, pero es fácil de entender y hace el trabajo. @ out2 es una matriz 2d, estoy leyendo valores usando un ciclo for. Cada ciclo comparo la entrada con @out2 para ver si es una hora/fecha anterior o posterior. Si es así, escribo los valores en la matriz y luego comparo la siguiente entrada.

if ($year < $out2[$j][7]) { 
    $lt = 1; 
    goto JUMP; 
} 
if ($year > $out2[$j][7]) { 
    $gt = 1; 
    goto JUMP; 
} 
if ($month < $out2[$j][5]) { 
    $lt = 1; 
    goto JUMP; 
} 
if ($month > $out2[$j][5]) { 
    $gt = 1; 
    goto JUMP; 
} 
if ($day < $out2[$j][6]) { 
    $lt = 1; 
    goto JUMP; 
} 
if ($day > $out2[$j][6]) { 
    $gt = 1; 
    goto JUMP; 
} 
if ($time < $out2[$j][4]) { 
    $lt = 1; 
    goto JUMP; 
} 
if ($time > $out2[$j][4]) { 
    $gt = 1; 
    goto JUMP; 
} 

SALTO:

if ($lt == 1) { 
    $out2[$j][2] = "$dtime $month\/$day\/$year"; 
    $out2[$j][4] = $time; 
    $out2[$j][5] = $month; 
    $out2[$j][6] = $day; 
    $out2[$j][7] = $year; 
    $lt = 0; 
    } 

if ($gt == 1) { 
    $out2[$j][3] = "$dtime $month\/$day\/$year"; 
    $out2[$j][4] = $time; 
    $out2[$j][5] = $month; 
    $out2[$j][6] = $day; 
    $out2[$j][7] = $year; 
    $gt = 0; 
} 
0

Por qué no la clave a partir del 5.10 Time::Piece y Time::Seconds, no los primeros resultados de una búsqueda de CPAN?

use strict; 
use warnings; 
use Time::Piece(); # we don't need to include overloaded locatime 
use Time::Seconds; 
use Data::Dumper; 

my @dates = qw/2010-10-31 2012-10-16 2011-09-08/; 

my $now = Time::Piece::localtime(); 

my @date_objects = map { 
    Time::Piece->strptime($_, '%F') # %F is the same as %Y-%m-%d 
} @dates; 

my @filtered_dates = grep { 
    $now - $_ < (ONE_DAY * 30) 
} @date_objects; 

print Dumper(map($_->strftime('%F'), @filtered_dates)); 
0

para encontrar una fecha mínima en un bucle:

var minDate = ...; 
var maxDate = ...; 

foreach my $date (@$dates) { 
    if ($minDate gt $date){ # Less than. 
     $minDate = $date; # Minimal date. 
    } 
    if ($minDate lt $date){ # Greater than. 
     $minDate = $date; # Maxamal date. 
    } 
} 
+0

¿Qué tiene esto que ver con la pregunta? – Toto

Cuestiones relacionadas