使用Perl,如何以YYYY-MM-DD的形式比较日期?

时间:2010-10-12 00:14:53

标签: perl

我的数组中包含n个字符串格式为YYYY-MM-DD(例如,“2010-10-31”)。

如何将日期与此数组中的字符串进行比较?

例如,超过30天前删除字符串?

8 个答案:

答案 0 :(得分:14)

关于YYYY-MM-DD的好处 - 格式化日期是您可以使用简单的字符串比较来比较它们。在Perl中,这是ltgt运算符。

在这种情况下,听起来你只是想检查数组中的日期是早于还是晚于给定的目标日期(恰好是“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;
} 

答案 1 :(得分:8)

猜猜有多种方法可以做到,但我喜欢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)

对于对象++进行算术运算非常棒。

只有日期,没有小时,分钟或秒

答案 2 :(得分: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

答案 3 :(得分:1)

一个良好的开端是阅读The Many Dates of PerlDateTime网站。

YYYY-MM-DD格式是ISO 8601日期表示的一种形式。它的变体被认为是可接受的,例如旧版数据中的YYYY-MM-DDYYYYMMDD甚至YYMM。在选择比较这些日期的方法之前,您应该查看definitive list

如果ISO 8601日期字符串是:1)有效日期; 2)使用或不使用-分隔符的相同格式; 3)缺少前导和尾随空格,一个有吸引力的属性是你可以用简单的字典字符串比较对字符串进行排序或比较。

一般情况下:

  1. IFF 您不会检查日期是否有效且 IFF 它们的格式是否相同,而 IFF 则没有在前导或尾随空格中,您可以使用相同格式与另一个表示目标日期的字符串进行比较。
  2. ---否则---

    1. 决定使用CPAN模块解析日期字符串(或自己匹配),

    2. 如果您的日期在该范围内,则转换为纪元时间(或使用比日期:: Manip或Date :: Calc更大规模日期/时间操作的CPAN模块)

    3. 对时间类型(纪元时间,绝对天数等)执行算术

    4. 将时间转换回您想要的格式......

    5. 这是执行此操作的代码:

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

答案 4 :(得分:1)

您可以使用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

答案 5 :(得分:1)

我是这样做的,有点冗长,但很容易理解并完成工作。 @ 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;
}

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

答案 6 :(得分:0)

为什么不是自5.10 Time::PieceTime::Seconds以来的CORE,而不是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));

答案 7 :(得分:0)

要在循环中查找最小日期:

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