开发者

What is the optimal way to loop between two dates in Perl?

开发者_运维技巧

What is the optimal/clearest way to loop between two dates in Perl? There are plenty of modules on CPAN that deal with such matter, but is there any rule of thumb for iterating between two dates?


For everything that uses Date manipulation DateTime is probably the best module out there. To get all dates between two dates with your own increment use something like this:

#!/usr/bin/env perl
use strict;
use warnings;
use DateTime;

my $start = DateTime->new(
    day   => 1,
    month => 1,
    year  => 2000,
);

my $stop = DateTime->new(
    day   => 10,
    month => 1,
    year  => 2000,
);


while ( $start->add(days => 1) < $stop ) {
    printf "Date: %s\n", $start->ymd('-');
}

This will output:

Date: 2000-01-02
Date: 2000-01-03
Date: 2000-01-04
Date: 2000-01-05
Date: 2000-01-06
Date: 2000-01-07
Date: 2000-01-08
Date: 2000-01-09


These days, most people would recommend using DateTime:

use DateTime;   

my $start = DateTime->new(...); # create two DateTime objects
my $end   = DateTime->new(...);

while ($start <= $end) {
    print $start->ymd, "\n";
    $start->add(days => 1);
}


I'm offering up a Time::Piece solution, because - unlike DateTime it's a core module (as of perl 5.9.5):

#!/usr/bin/env perl

use strict;
use warnings;
use Time::Piece;
use Time::Seconds;

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

my $start = '2016-01-22';
my $end   = '2016-03-11';


my $start_t = Time::Piece->strptime( $start, $FORMAT );
my $end_t   = Time::Piece->strptime( $end,   $FORMAT );

while ( $start_t <= $end_t ) {
   print $start_t ->strftime($FORMAT), "\n";
   $start_t += ONE_DAY;
}

Both Time::Piece and Time::Seconds are core as of perl 5.9.5. The latter is only needed for ONE_DAY - otherwise you can just add 60 * 60 * 24 instead.

This has the advantage of being able to parse fairly arbitrary date formats.


I think the "best" way to do that depends a lot on what you're doing between these two days.

In many cases, a simple for (0..31) loop will suffice.

In other cases, you may wish to use an epoch value, and add/subtract 86400 seconds on each itteration.

In one application I've written, I do exactly this, using a DateTime object that I add one day to for each iteration. This is likely overkill for many applications, though.


As of 2020, another option would be to use Time::Moment that have very good performances (see the benchmarks) through a clear interface.

A reimplementation of Sobrique's answer would be:

#!/usr/bin/env perl

use strict;
use warnings;
use Time::Moment;

# Same than 'Y-%m-%d'
my $FORMAT = '%F';

my $start = '2020-01-22';
my $end   = '2020-03-11';

my $start_t = Time::Moment->from_string( $start . 'T00Z' );
my $end_t   = Time::Moment->from_string( $end . 'T00Z' );

while ( $start_t <= $end_t ) {
   print $start_t ->strftime( $FORMAT ), "\n";
   $start_t->plus_days( 1 );
}

Time::Moment isn't a core module, but if you need some extra speed, it can help a bit compared to Time::Piece and DateTime. Plus, the interface is really easy to read. The date parsing capabilities are maybe a bit less restrictive.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜