2016-12-13 61 views
1

我开始有两个日期。如何在两个日期之间获得星期日的阵列?

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

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

任何想法,我应该开始?

+1

[DATETIME](HTTPS:// metacpan。org/pod/DateTime) – stevieb

+0

您应该先选择一个模块。我偏爱[DateTime](http://search.cpan.org/perldoc?DateTime),使用[DateTime :: Format :: Strptime](http://search.cpan.org/perldoc?DateTime ::格式:: Strptime)进行解析。 – ikegami

+0

星期日是*还是包括*两个日期? – Borodin

回答

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相比,调用迭代器函数是存储桶中的一项功能。

+0

如果内存中的内存不足,他可以简单地将'print'$ sunday \ n“;'移动到第一个循环是一个问题[对不起,当我进行更新时,我不知道你会回答] – ikegami

+0

@ikegami这将使用星期日列表来生成星期日列表,迭代器允许星期日列表(并在任何工作日进行小小调整)可用于任何目的,而无需预先生成它们。 – Schwern

+0

是从'print $ sunday-> strftime(“%m /%d /%Y”);'转换为'说$星期日;'故意的? – ikegami

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"); 
} 
+0

由于星期几是连续的(除非我们进入Julian vs Gregorian日历间隔),您可以通过首先将'$ startDate'移动到一个7(或6)来加速星期天一次使用'$ startDate-> day_of_week',然后增加7天来获得下个星期日,直到你通过'$ endDate'。它也可能是'my $ date = $ startDate-> clone'的快速发型。最后,Time :: Piece的工作速度可能比DateTime快,因为它的包袱少了很多。 – Schwern

+2

@ikegami虽然你的修改非常好,但它们从根本上改变了别人的答案。相反,把它们作为自己的回答或评论。 – Schwern

+0

我不同意。答案中的问题应该在答案中得到解决。不应该克隆。现在有三个答案都说'使用DateTime' – ikegami

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比尔使用和Schwern拥有的认可。这不是推荐使用的DateTime,因为它会创建更加复杂且容易出错的代码。如您所见,使用格式化程序将代码大小减半。


注:Schwern拥有提倡使用迭代器的,有一些(他回答所有的代码)再更换我的回答的最后四行的4倍。没有理由导致高水平的复杂性!他详细说明了迭代器保存多少内存,但是根本没有保存。

2

的DateTime ::集使构建一个迭代容易:

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; 
} 
+0

不错!我不知道这个模块。 – ikegami

相关问题