Perl 模块:将日期间隔分成子间隔?
Perl module: break date interval into sub intervals?
假设我有:
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 个日期和所需的间隔最大长度作为参数来调用该方法。
您也可以使用 DateTime and DateTime::Duration, but it should be easy to implement with 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 对象和 returns 类似于您显示的示例的结构。它的工作原理是在循环中向开始日期添加 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;
}
假设我有:
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 个日期和所需的间隔最大长度作为参数来调用该方法。
您也可以使用 DateTime and DateTime::Duration, but it should be easy to implement with 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 对象和 returns 类似于您显示的示例的结构。它的工作原理是在循环中向开始日期添加 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;
}