您可以使用DateTime 和DateTime::Duration 来做到这一点,但使用Time::Piece 也应该很容易实现。关键是你需要自己计算。
sub get_interval {
my ( $start, $end ) = @_;
# to get a 30 day timespan we need to add 29
# 01. to 30. = 1 + 29
my $twenty_nine_days = DateTime::Duration->new( days => 29 );
my @return;
while ( ( my $step = $start + $twenty_nine_days ) < $end ) {
push @return, { start => $start->ymd, end => $step->ymd };
$start = $step;
}
push @return, { start => $start->ymd, end => $end->ymd };
return \@return;
}
此函数采用两个 DateTime 对象并返回与您展示的示例类似的结构。它的工作原理是在循环中将开始日期添加 29 天,直到超过结束日期。
我们需要使用 29 天而不是 30 天,因为我们想要 30 天的间隔,但是开始日期已经是第一天,所以是 29。
让我们试试这个调用:
use Data::Printer;
p get_interval(
DateTime->new( year => 2016, month => 1, day => 1 ),
DateTime->new( year => 2016, month => 12, day => 8 )
);
输出使用Data::Printer:
\ [
[0] {
end "2016-01-30",
start "2016-01-01"
},
[1] {
end "2016-02-28",
start "2016-01-30"
},
[2] {
end "2016-03-28",
start "2016-02-28"
},
[3] {
end "2016-04-26",
start "2016-03-28"
},
[4] {
end "2016-05-25",
start "2016-04-26"
},
[5] {
end "2016-06-23",
start "2016-05-25"
},
[6] {
end "2016-07-22",
start "2016-06-23"
},
[7] {
end "2016-08-20",
start "2016-07-22"
},
[8] {
end "2016-09-18",
start "2016-08-20"
},
[9] {
end "2016-10-17",
start "2016-09-18"
},
[10] {
end "2016-11-15",
start "2016-10-17"
},
[11] {
end "2016-12-08",
start "2016-11-15"
}
]
我也写了一些单元测试。它们绝不是完整的,但它们提供了一个很好的起点。
use strict;
use warnings;
use feature 'say';
use Data::Printer;
use Test::More;
use Test::Deep;
use DateTime;
sub get_date {
my ( $year, $month, $day ) = @_;
return DateTime->new( year => $year, month => $month, day => $day );
}
cmp_deeply get_interval( get_date( 2013, 1, 1 ), get_date( 2013, 2, 20 ) ),
[
{ start => '2013-01-01', end => '2013-01-30', },
{ start => '2013-01-30', end => '2013-02-20', },
],
'one and a bit intervals';
cmp_deeply get_interval( get_date( 2013, 1, 5 ), get_date( 2013, 1, 7 ) ),
[ { start => '2013-01-05', end => '2013-01-07', }, ],
'three days in one month';
cmp_deeply get_interval( get_date( 2013, 1, 30 ), get_date( 2013, 2, 1 ) ),
[ { start => '2013-01-30', end => '2013-02-01', }, ],
'three days over two month';
cmp_deeply get_interval( get_date( 2013, 1, 01 ), get_date( 2014, 1, 31 ) ),
[
{ start => '2013-01-01', end => '2013-01-30', },
{ start => '2013-01-30', end => '2013-02-28', },
{ start => '2013-02-28', end => '2013-03-29', },
{ start => '2013-03-29', end => '2013-04-27', },
{ start => '2013-04-27', end => '2013-05-26', },
{ start => '2013-05-26', end => '2013-06-24', },
{ start => '2013-06-24', end => '2013-07-23', },
{ start => '2013-07-23', end => '2013-08-21', },
{ start => '2013-08-21', end => '2013-09-19', },
{ start => '2013-09-19', end => '2013-10-18', },
{ start => '2013-10-18', end => '2013-11-16', },
{ start => '2013-11-16', end => '2013-12-15', },
{ start => '2013-12-15', end => '2014-01-13', },
{ start => '2014-01-13', end => '2014-01-31', },
],
'three days over two month';
# what happens if start > end?
done_testing;
sub get_interval {
my ( $start, $end ) = @_;
# to get a 30 day timespan we need to add 29
# 01. to 30. = 1 + 29
my $twenty_nine_days = DateTime::Duration->new( days => 29 );
my @return;
while ( ( my $step = $start + $twenty_nine_days ) < $end ) {
push @return, { start => $start->ymd, end => $step->ymd };
$start = $step;
}
push @return, { start => $start->ymd, end => $end->ymd };
return \@return;
}