让我说我有:
dateA : sometime in the past(ex: 2013-01-01T00:00:00)(yyyy-mm-dd)
dateB :now
intervalLength: from dateA - now
如果间隔长度大于30天,我需要生成一个数组,其中每个位置将包含一个具有每个子区间的开始和结束的对象:
intervalLength[0]: {start:2013-01-01T00:00:00, end:2013-01-30T00:00:00}
intervalLength[1]: {start:2013-01-30T00:00:01, end:2013-02-28T00:00:00}
...
intervalLength[n]: "last interval/rest" < 30days
所以基本上我需要一个可以将日期间隔分成更小的子区间的模块。理想情况下,我可以通过传递2个日期和所需的间隔最大长度作为参数来调用方法。
答案 0 :(得分:1)
您可以使用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;
}