У меня есть массив с строками n
в формате YYYY-MM-DD (Пример, "2010-10-31" ).
Как сравнить дату со строками в этом массиве?
Например, удалите строки более 30 дней назад?
У меня есть массив с строками n
в формате YYYY-MM-DD (Пример, "2010-10-31" ).
Как сравнить дату со строками в этом массиве?
Например, удалите строки более 30 дней назад?
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
Великая вещь в 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;
}
Угадайте, есть ли еще один способ сделать это, но мне нравится 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)
Это отлично подходит для выполнения арифметики на объектах ++.
Только даты, но, не часы, минуты или секунды
Хорошим началом является чтение Много дат Perl и DateTime.
Формат YYYY-MM-DD представляет собой форму представления ISO 8601. Существуют варианты, которые считаются приемлемыми, например YYYY-MM-DD
и YYYYMMDD
и даже YYMM
в более старых данных. Перед тем, как выбрать метод сравнения этих дат, просмотрите окончательный список.
Если строками даты ISO 8601 являются: 1) действительные даты; 2) в том же формате с разделителем -
или без него; 3) Отсутствие пробелов в ведущей и конечной пробелах, привлекательным свойством является то, что вы можете сортировать или сравнивать строки с простыми лексикографическими сопоставлениями строк.
В общем случае:
--- В противном случае ---
Определите модуль CPAN для анализа вашей строки даты (или ее соответствия),
Преобразуйте в эпоху, если если ваши даты находятся в этом диапазоне (или используйте модуль CPAN, который выполняет более масштабную обработку даты и времени, например Date:: Manip или Date:: Calc)
Выполните арифметику по типу времени (время эпохи, абсолютные дни, что угодно)
Преобразуйте время обратно в желаемый формат...
Вот код, который делает это:
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";
}
Вы можете использовать модуль 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
Я сделал это так, вроде многословного, но легко понять и выполнить свою работу. @out2 - это 2d-массив, я читаю значения с использованием цикла 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;
}
Почему не CORE с 5.10 Time:: Piece и Время:: Секунды, а не первые несколько результатов поиска 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));
Чтобы найти минимальную дату в цикле:
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.
}
}