我开始时有两个约会。
my $date1 = 01/01/2016;
my $date2 = 05/15/2016;
我需要把星期日的所有日期都放在两个日期之间。
知道我应该从哪里开始吗?
答案 0 :(得分:6)
Your solution is good,但它可能消耗大量内存,从而创建一个可能永远不会被使用的星期日数组。 DateTime对象不小也不便宜。
另一种选择是迭代器,每次调用它时都会生成下一个星期日。它按需生成每个星期日,而不是事先计算它们。这样可以节省内存并支持潜在的无限星期日。
use strict;
use warnings;
use v5.10;
sub sundays_iterator {
my($start, $end) = @_;
# Since we're going to modify it, copy it.
$start = $start->clone;
# Move to Sunday, if necessary.
$start->add( days => 7 - $start->day_of_week );
# Create an iterator using a closure.
# This will remember the values of $start and $end as
# they were when the function was returned.
return sub {
# Clone the date to return it without modifications.
# We always start on a Sunday.
my $date = $start->clone;
# Move to the next Sunday.
# Do this after cloning because we always start on Sunday.
$start->add( days => 7 );
# Return Sundays until we're past the end date
return $date <= $endDate ? $date : ();
};
}
返回一个闭包,一个匿名子程序,它记住它创建的词法变量。类似于内向外对象,附加数据的函数。然后,您可以像任何子例程引用一样调用它。
my $sundays = sundays_iterator($startDate, $endDate);
while( my $sunday = $sundays->() ) {
say $sunday;
}
好处是节省了大量内存,如果您将日期作为用户输入,这一点尤为重要:恶意攻击者可能会要求您使用大量服务器来消耗大量内存。
它还允许您将列表生成与使用列表分开。现在,您可以通过一种在日期范围内生成星期日的通用方法(或稍微调整一周中的任何一天)。
缺点是它可能比在循环中构建数组慢一点......但可能并不明显。 Perl中的函数调用相对较慢,因此对每个星期日进行一次函数调用将比循环慢,但调用那些DateTime方法(调用其他调用其他方法的方法)将淹没该成本。与使用DateTime相比,调用迭代器函数是一个下降。
答案 1 :(得分:3)
您应该从挑选模块开始。我偏向DateTime,使用DateTime::Format::Strptime进行解析。
use DateTime qw( );
use DateTime::Format::Strptime qw( );
my $start_date = "01/01/2016";
my $end_date = "05/15/2016";
my $format = DateTime::Format::Strptime->new(
pattern => '%m/%d/%Y',
time_zone => 'local',
on_error => 'croak',
);
my $start_dt = $format->parse_datetime($start_date)->set_formatter($format);
my $end_dt = $format->parse_datetime($end_date )->set_formatter($format);
my $sunday_dt = $start_dt->clone->add( days => 7 - $start_dt->day_of_week );
while ($sunday_dt <= $end_dt) {
print "$sunday_dt\n";
$sunday_dt->add( days => 7 );
}
注意:你真的不应该使用DateTime->new
作为Bill使用和Schwern认可的。这不是DateTime的推荐用法,因为它创建的代码更加复杂且容易出错。如您所见,使用格式化程序将代码大小减半。
注意:Schwern提倡使用迭代器,用我的答案的最后四行替换4倍的代码(他的答案中的所有代码)。这种高水平的复杂性没有理由!他详细说明了迭代器保存了多少内存,但它根本没有保存。
答案 2 :(得分:2)
DateTime :: Set使构造迭代器变得容易:
use DateTime::Format::Strptime ();
use DateTime::Set ();
my $start_date = "01/01/2016";
my $end_date = "05/15/2016";
my $format = DateTime::Format::Strptime->new(
pattern => '%m/%d/%Y',
time_zone => 'local',
on_error => 'croak',
);
my $iterator = DateTime::Set->from_recurrence(
start => $format->parse_datetime($start_date)->set_formatter($format),
end => $format->parse_datetime($end_date)->set_formatter($format),
recurrence => sub { $_[0]->add( days => 7 - $_[0]->day_of_week || 7 ) }, # next Sunday after $_[0]
);
while ( my $date = $iterator->next ) {
say $date;
}
答案 3 :(得分:0)
这是我提出的,但如果有更好的方法,请告诉我。
use DateTime;
my $date1 = "1/1/2016";
my $date2 = "5/15/2016";
my ($startMonth, $startDay, $startYear) = split(/\//, $date1);
my ($endMonth, $endDay, $endYear) = split(/\//, $date2);
my $startDate = DateTime->new(
year => $startYear,
month => $startMonth,
day => $startDay
);
my $endDate = DateTime->new(
year => $endYear,
month => $endMonth,
day => $endDay
);
my @sundays;
do {
my $date = DateTime->new(
year => $startDate->year,
month => $startDate->month,
day => $startDate->day
);
push @sundays, $date if ($date->day_of_week == 7);
$startDate->add(days => 1);
} while ($startDate <= $endDate);
foreach my $sunday (@sundays) {
print $sunday->strftime("%m/%d/%Y");
}