【问题标题】:What is the optimal way to loop between two dates in Perl?在 Perl 中循环两个日期的最佳方式是什么?
【发布时间】:2011-10-01 03:46:20
【问题描述】:

在 Perl 中循环两个日期的最佳/最清晰的方法是什么? CPAN 上有很多模块可以处理此类问题,但是是否有任何经验法则可以在两个日期之间进行迭代?

【问题讨论】:

标签: perl date loops iteration


【解决方案1】:

对于所有使用日期操作的东西,DateTime 可能是最好的模块。要使用您自己的增量获取两个日期之间的所有日期,请使用以下内容:

#!/usr/bin/env perl
use strict;
use warnings;
use DateTime;

my $start = DateTime->new(
    day   => 1,
    month => 1,
    year  => 2000,
);

my $stop = DateTime->new(
    day   => 10,
    month => 1,
    year  => 2000,
);


while ( $start->add(days => 1) < $stop ) {
    printf "Date: %s\n", $start->ymd('-');
}

这将输出:

Date: 2000-01-02
Date: 2000-01-03
Date: 2000-01-04
Date: 2000-01-05
Date: 2000-01-06
Date: 2000-01-07
Date: 2000-01-08
Date: 2000-01-09

【讨论】:

  • 这有点令人困惑,因为它是在 $start 之后的第二天开始的
  • 这个解决方案最好的部分:如果我需要循环几小时、几分钟、几秒、几个月或几年,我现在知道该怎么做。
【解决方案2】:

如今,大多数人会推荐使用DateTime

use DateTime;   

my $start = DateTime->new(...); # create two DateTime objects
my $end   = DateTime->new(...);

while ($start <= $end) {
    print $start->ymd, "\n";
    $start->add(days => 1);
}

【讨论】:

    【解决方案3】:

    我提供了一个 Time::Piece 解决方案,因为 - 与 DateTime 不同,它是一个核心模块(从 perl 5.9.5 开始):

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    use Time::Piece;
    use Time::Seconds;
    
    my $FORMAT = '%Y-%m-%d';
    
    my $start = '2016-01-22';
    my $end   = '2016-03-11';
    
    
    my $start_t = Time::Piece->strptime( $start, $FORMAT );
    my $end_t   = Time::Piece->strptime( $end,   $FORMAT );
    
    while ( $start_t <= $end_t ) {
       print $start_t ->strftime($FORMAT), "\n";
       $start_t += ONE_DAY;
    }
    

    Time::PieceTime::Seconds 都是 perl 5.9.5 的核心。后者仅对 ONE_DAY 需要 - 否则您可以添加 60 * 60 * 24 代替。

    这具有能够解析相当任意的日期格式的优势。

    【讨论】:

      【解决方案4】:

      我认为做到这一点的“最佳”方式很大程度上取决于这两天你在做什么

      在许多情况下,一个简单的for (0..31) 循环就足够了。

      在其他情况下,您可能希望使用 epoch 值,并在每次迭代时加/减 86400 秒。

      在我编写的一个应用程序中,我正是这样做的,使用一个DateTime 对象,我为每次迭代添加一天。不过,这对于许多应用程序来说可能是多余的。

      【讨论】:

      • 向纪元添加秒数的方法通常会因 DST(夏令时)而失败。我曾经在我的申请中这样做过一次,但半年后出现了奇怪的错误。
      • 希望您的所有日期操作都在不适用 DST 的 UTC 中进行。 :) 理智的程序员会这样做...呵呵 但即便如此,我从未声称这是每个案例的解决方案。
      • 在您的应用程序中,您可能希望显示当地时间。如果您对 UTC 进行数学运算并转换为当地时间,则可能是您的代码中断了。例如,将 1 小时 1 分钟添加到 UTC 时间 27.3.2011 01:00:00 并将其转换为“Europe/Berlin”将导致错误,因为日期无效。如果您在进行数学运算之前设置了 time_zone,则您的代码可以正常工作并返回与时间 03:01:00 相同的日期。
      【解决方案5】:

      从 2020 年开始,另一种选择是通过清晰的界面使用性能非常好的Time::Moment(参见the benchmarks)。

      Sobrique 答案的重新实现是:

      #!/usr/bin/env perl
      
      use strict;
      use warnings;
      use Time::Moment;
      
      # Same than 'Y-%m-%d'
      my $FORMAT = '%F';
      
      my $start = '2020-01-22';
      my $end   = '2020-03-11';
      
      my $start_t = Time::Moment->from_string( $start . 'T00Z' );
      my $end_t   = Time::Moment->from_string( $end . 'T00Z' );
      
      while ( $start_t <= $end_t ) {
         print $start_t ->strftime( $FORMAT ), "\n";
         $start_t->plus_days( 1 );
      }
      

      Time::Moment 不是核心模块,但如果您需要一些额外的速度,与Time::PieceDateTime 相比,它会有所帮助。另外,界面非常易于阅读。日期解析功能可能限制较少。

      【讨论】:

        猜你喜欢
        • 2013-11-26
        • 2013-06-14
        • 1970-01-01
        • 2014-02-18
        • 2010-09-14
        • 1970-01-01
        • 2015-09-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多