开始日期和结束日期之间的天数列表

时间:2014-09-08 13:01:20

标签: perl datetime

我试图在Perl中获取开始日期和结束日期之间的每个日期。我最初使用以下代码完成了7天:

use Time::ParseDate;  
my $newdate;
my $newtime;
my @dates = ();

my $newtime->[0] = parsedate($start_datetime);
my $newdate->[0] = strftime("%Y-%m-%d",localtime($newtime->[0]));
push(@dates, $newdate->[0]);
my $newtime->[1] = parsedate($newdate->[0]) + (1 * 24 * 60 * 60);
my $newdate->[1] = strftime("%Y-%m-%d",localtime($newtime->[1]));
push(@dates, $newdate->[1]);
my $newtime->[2] = parsedate($newdate->[1]) + (1 * 24 * 60 * 60);
my $newdate->[2] = strftime("%Y-%m-%d",localtime($newtime->[2]));
push(@dates, $newdate->[2]);
my $newtime->[3] = parsedate($newdate->[2]) + (1 * 24 * 60 * 60);
my $newdate->[3] = strftime("%Y-%m-%d",localtime($newtime->[3]));
push(@dates, $newdate->[3]);
my $newtime->[4] = parsedate($newdate->[3]) + (1 * 24 * 60 * 60);
my $newdate->[4] = strftime("%Y-%m-%d",localtime($newtime->[4]));
push(@dates, $newdate->[4]);
my $newtime->[5] = parsedate($newdate->[4]) + (1 * 24 * 60 * 60);
my $newdate->[5] = strftime("%Y-%m-%d",localtime($newtime->[5]));
push(@dates, $newdate->[5]);
my $newtime->[6] = parsedate($newdate->[5]) + (1 * 24 * 60 * 60);
my $newdate->[6] = strftime("%Y-%m-%d",localtime($newtime->[6]));
push(@dates, $newdate->[6]);
my $newtime->[7] = parsedate($newdate->[6]) + (1 * 24 * 60 * 60);
my $newdate->[7] = strftime("%Y-%m-%d",localtime($newtime->[7]));
push(@dates, $newdate->[7]);

但意识到它应该能够工作任何天数。 所以我尝试了下面的循环,但它对我来说不是很有用

my $newdate;
my $newtime;
my @dates = ();
my $start_date = substr($start_datetime, 0, 10) ; 
$start_date =~ s/-//gi;  
my $end_date = substr($end_datetime, 0, 10) ;
$end_date =~ s/-//gi;

my $days = yyyymmdd_to_rata_die($end_date) - yyyymmdd_to_rata_die($start_date);
my $newtime->[0] = parsedate($xml->{action_content}->{start_date});
my $newdate->[0] = strftime("%Y-%m-%d",localtime($newtime->[0]));
push(@dates, $newdate->[0]);
for(my $x=1;$x<$days;$x++)
{
  my $newtime->[$x] = parsedate($newdate->[$x-1]) + (1 * 24 * 60 * 60);
  my $newdate->[$x] = strftime("%Y-%m-%d",localtime($newtime->[$x]));
  push(@dates, $newdate->[$x]);
}

我在Stackoverflow上的问题中得到了以下函数

sub yyyymmdd_to_rata_die
{
    use integer;
    my ( $y, $m, $d ) = $_[0] =~ /\A([0-9]{4})([0-9]{2})([0-9]{2})\z/
    or return;

    my $adj;

    if ( $m <= 2 ) 
    {
        $y -= ( $adj = ( 14 - $m ) / 12 );
        $m += 12 * $adj;
    }
    elsif ( $m > 14 ) 
    {
        $y += ( $adj = ( $m - 3 ) / 12 );
        $m -= 12 * $adj;
    }

    $d += ( $m * 367 - 1094 ) / 12 + $y % 100 * 1461 / 4 + ( $y / 100 * 36524 + $y / 400 ) - 306;
}

任何援助都会受到极大的谴责

编辑: $ start_datetime =&#34; 2014-09-01 00:00:00&#34 ;; $ end_datetime =&#34; 2014-09-07 23:59:59&#34 ;;

我需要在开始和结束日期之间的所有日子,即。
2014-09-01
2014-09-02
2014-09-03
2014-09-04
2014-09-05
2014-09-06
2014-09-07

2 个答案:

答案 0 :(得分:5)

我建议您使用Time::Piece模块。它不应该需要安装,因为它是Perl 5版本10以来的核心模块。

看起来像这样

use strict;
use warnings;

use Time::Piece;

print days_between('2014-01-01', '2014-12-01'), "\n";

sub days_between {
  my ($start, $end) = map Time::Piece->strptime($_, '%Y-%m-%d'), @_;
  ($end - $start)->days;
}

<强>输出

334

<强>更新

我道歉。我误解了您的要求,并认为您希望两个日期之间的天数。该程序打印一个列表,其中包含两个限制之间的所有日期。如果在标量上下文中调用,它将像以前一样在列表中提供数字天。

use strict;
use warnings;

use Time::Piece;
use Time::Seconds qw/ ONE_DAY /;

printf "%d days:\n", scalar days_from_to('2014-09-01 00:00:00', '2014-09-07 23:59:59');

print "$_\n" for days_from_to('2014-09-01 00:00:00', '2014-09-07 23:59:59');

sub days_from_to {

   my @limits = map /(\d\d\d\d-\d\d-\d\d)/, @_;
   @limits = map Time::Piece->strptime($_, '%Y-%m-%d'), @limits;

   my @dates = ( $limits[0] );
   push @dates, $dates[-1] + ONE_DAY while $dates[-1] < $limits[-1];   

   map $_->ymd, @dates;
}

<强>输出

7 days:
2014-09-01
2014-09-02
2014-09-03
2014-09-04
2014-09-05
2014-09-06
2014-09-07

<强>更新

如果需要更严格的正则表达式拒绝0123-45-67之类的字符串,那么你可以使用这个

my $date_re = qr/(?:
   (?: (?:19|20)[0-9][0-9] ) -
   (?:
      (?: 0?[469] | 11 ) -
      (?: 0?[1-9] | [12][0-9] | 30 )
   |
      (?: 0?[13578] | 1[02] ) -
      (?: 0?[1-9] | [12][0-9] | 3[01] )
   |
      (?: 0?2 ) -
      (?: 0?[1-9] | [12][0-9] )
   )
)/x;

将接受1900年到2099年之间的日期,并确保该月的某一天对该月有效。唯一的附带条件是它将允许任何一年的2月29日。

或者您可能更喜欢使用Regexp::Common::time这样的

use Regexp::Common qw/ time /;

my $date_re = $RE{time}{strftime}{-pat => '%Y-%m-%d'};

但这与2月29日的问题相同。

子程序将如下所示

sub days_from_to {

   my @limits = map /($date_re)/, @_;

   @limits = map Time::Piece->strptime($_, '%Y-%m-%d'), @limits;

   my @dates = ( $limits[0] );
   push @dates, $dates[-1] + ONE_DAY while $dates[-1] < $limits[-1];   

   map $_->ymd, @dates;
}

答案 1 :(得分:0)

使用Time::Piece;

等内容可以让您的生活更轻松
#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

use Time::Piece;
use Time::Seconds;

my $fmt = '%Y-%m-%d';

my $start_date = '2014-09-08';
my $days = 30;

my @dates;
my $date = Time::Piece->strptime($start_date, $fmt);

foreach (1 .. $days) {
  push @dates, $date;
  $date += ONE_DAY;
}

say $_ for @dates;

更新Borodin's answer看起来使用非常类似的方法处理您添加到问题中的输入和所需输出。