我怎样才能在两个日期之间获得星期日阵列?

时间:2016-12-13 20:01:55

标签: perl

我开始时有两个约会。

my $date1 = 01/01/2016;
my $date2 = 05/15/2016;

我需要把星期日的所有日期都放在两个日期之间。

知道我应该从哪里开始吗?

4 个答案:

答案 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");
}