ある範囲内の日付時刻を、指定間隔で反復するTime::Piece::Iteratorというモジュールを作った

この記事はPerl Advent Calendar 2014の18日目の記事です。

17日目の記事はid:kfly8さんの間接オブジェクト記法のアンチパターンでした。

Time::Piece::Iterator

Time::Piece::Iterator

指定した範囲内の日付時刻を、指定した間隔で反復するモジュールが欲しかったのですが、見つからなかったので作成しました。(他にあれば教えて下さい!)
日付とか年月を引数に取るバッチを、特定の期間分(半年とか)実行したい時に使ってます。

使い方

Time::Pieceオブジェクトを2つ渡すと、その範囲内の日付時刻を返してくれます。

use Time::Piece;
use Time::Piece::Iterator;

my $iterator = day_iterator(
    localtime->strptime('2014/01/01', '%Y/%m/%d'),
    localtime->strptime('2014/01/03', '%Y/%m/%d'),
);

while( my $t = $iterator->next ) {
    print $t->ymd, "\n";
}
2014-01-01
2014-01-02
2014-01-03

間隔はdayだけではなくsecond/minute/hour/week/month/yearを用意しています。
例えば、今年の各月最初の平日を知りたい場合はこんなかんじです。

use Time::Piece;
use Time::Piece::Iterator;
use Time::Seconds;
use Calendar::Japanese::Holiday;

sub is_weekday {
    my $t = shift;
    my $is_holiday = isHoliday( $t->year, $t->mon, $t->mday, 1 );
    if( $is_holiday || $t->wday == 1 || $t->wday == 7 ) {
        return 0;
    }
    return 1;
}

my $from = localtime->strptime('2014-01-01', '%Y-%m-%d');
my $to = $from->add_years(1) - ONE_DAY;

my $iterator = month_iterator( $from, $to );
while( my $t = $iterator->next ) {
    while( !is_weekday($t) ) {
        $t += ONE_DAY;
    }
    print $t->ymd, "\n";
}

用意されている間隔では要件が満たせない場合は、自分で設定することが出来ます。
以下の例は、間隔が3週間の場合です。

my $iterator = custom_iterator(
    $from, $to,
    sub {
        my ($t, $sign) = @_;
        return $t + ( $sign * 3 * ONE_WEEK );
    }
);

第1引数が現在の日付時刻、第2引数が符号(plus or minus)です。過去に遡ることも出来るので、符号を受け取れるようにしています。

注意点

month_iteratorとyear_iteratorはTime::Pieceのadd_monthsとadd_yearsを使っているので、月末の扱いに注意する必要があります。

今後

無限iteratorを可能にする、Time::Piece::Plusも扱えるようにする、などを考えています。

あと、CPANには上げていないのですが、使っていただける方がいれば上げたいなと思っています。

明日

19日目はid:y_uukiさんです!