Используя Perl, как мне сравнить даты в виде ГГГГ-ММ-ДД? - PullRequest
10 голосов
/ 12 октября 2010

У меня есть массив с n строками в формате ГГГГ-ММ-ДД (пример, "2010-10-31").

Как сравнить дату со строками в этом массиве?

Например, удалить строки более 30 дней назад?

Ответы [ 8 ]

14 голосов
/ 12 октября 2010

Самое замечательное в YYYY-MM-DD -форматированных датах - это то, что вы можете сравнивать их с помощью простого сравнения строк.В Perl это операторы lt и gt.

В этом случае кажется, что вы просто проверяете, находятся ли даты в массиве раньше или позже заданной целевой даты (который просто "30 дней назад").В этом случае я бы выбрал сначала определение даты 30 дней назад, а затем сравнить ее в виде строки с каждой датой в массиве.Я бы не стал вводить накладные расходы на преобразование всех строк YYYY-MM-DD в «правильные» объекты даты, время эпохи и т. Д. И обратно только ради тестирования, представляющего более раннюю дату.

#!/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;
} 
8 голосов
/ 12 октября 2010

Думаю, есть несколько способов сделать это, но мне нравится Date :: Simple для подобных вещей ..

Пример из документации:

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)

Отлично подходит для арифметики объектов ++.

Только даты, однако , без часов, минут или секунд

5 голосов
/ 12 октября 2010
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 голос
/ 29 октября 2012

Я сделал это примерно так, вроде многословно, но это легко понять, и работа сделана. @ out2 - это двумерный массив, я читаю значения, используя цикл for. В каждом цикле я сравниваю ввод с @ out2, чтобы узнать, является ли это более ранним или более поздним временем / датой. Если это так, я записываю значения в массив, а затем сравниваю следующий вход.

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

ПЕРЕХОД:

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;
}
1 голос
/ 12 октября 2010

Вы можете использовать модуль Time :: ParseDate ,

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
1 голос
/ 12 октября 2010

Хорошее начало - читать Множество дат Perl и сайт DateTime .

Формат YYYY-MM-DD является формой ISO 8601 представление даты.Существуют варианты, которые считаются приемлемыми, такие как YYYY-MM-DD и YYYYMMDD и даже YYMM в более старых данных.Вы должны посмотреть на окончательный список , прежде чем выбрать метод для сравнения этих дат.

Если строки дат ISO 8601: 1) допустимые даты;2) в том же формате с разделителем - или без него;3) отсутствие пробелов в начале и в конце, привлекательным свойством является то, что вы можете сортировать или сравнивать строки с помощью простых лексикографических сравнений строк.

В общем, тогда:

  1. IFF вы не собираетесь проверять, действительны ли даты, и IFF они совпадаютформат и IFF нет пробелов в начале или в конце, вы можете сравнить с другой строкой, представляющей целевую дату в том же формате.

--- В противном случае ---

  1. Выберите модуль CPAN для анализа вашей строки даты (или сопоставьте ее самостоятельно),

  2. Конвертировать в время эпохи, если ваши даты находятся в этом диапазоне (или использовать модуль CPAN, который выполняет более масштабные манипуляции с датой / временем, такие как Date :: Manip или Date :: Calc)

  3. Выполните арифметику по типу времени (время эпохи, абсолютные дни и т. Д.)

  4. Преобразуйте время обратно в нужный вам формат ...

Вот код, который делает это:

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 голосов
/ 31 октября 2013

Чтобы найти минимальную дату в цикле:

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 голосов
/ 29 октября 2012

Почему бы не CORE с 5.10 Time :: Piece и Time :: Seconds , а не первыми результатами поиска 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));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...