@kyanny's blog

私は天才ではありません。ただ、人より長く一つの事と付き合っていただけです - アインシュタイン

DateTime::Span の勉強

DateTime::Span は「二つの日付の間の期間」を表現するクラスで、 DateTime::Set ディストリビューションに含まれている。

DateTime::Set は「複数の日の集まり」を表現するクラスで、「家族の誕生日」とか、あと「毎週水曜」みたいな繰り返しの日付も表現できるらしい。両方をあわせた DateTime::SpanSet とういのもある。

DateTime::Span には duration メソッドがあり、これは DateTime::Duration オブジェクトを返す。 DateTime::Duration は二つの日時の間の差をもっているが、 DateTimeのマニュアルにあるように、年月日時分秒はそれぞれ可換な単位ではないので、 DateTime::Duration オブジェクトが「何秒」という単位で保持している時間を「何日」へと変換することはできない。(で、合ってるよな・・・)。

で、 DateTime::Span クラスの duration メソッドは「秒」単位での時間を返すので、「この日とこの日の間は何日あったか」がわからない。ここを少し追ってみたら、 duration メソッドは DateTime クラスの subtract_datetime_absolute メソッドを呼んで DateTime::Duration オブジェクトを作って返しており、 subtract_datetime_absolute メソッドは「秒」と「ミリ秒」の単位を使うので、どうしても「何日あるか」は得られそうにない、ということがわかった。

なので、どうしても DateTime::Span を使いつつ「何日」という単位で時間の差をとりたい場合は、

*DateTime::Span::duration_days = sub {
    my $dur;

    eval {
        local $SIG{__DIE__};   # don't want to trap this (rt ticket 5434)
        $dur = $_[0]->end->delta_days( $_[0]->start )
    };
    $@ = undef;  # clear the eval() error message
    
    return $dur if defined $dur;

    return DateTime::Infinite::Future->new -
           DateTime::Infinite::Past->new;

};

こんなのを書いて(DateTime::Span::duration のまんまコピペ、 delta_days のところだけ違う) $span->duration_days() を呼んだりすればよい。

・・・けど、ここまでするなら DateTime::Span を使わずに素直に $dt1->delta_days($dt2) を使えばいいじゃん、と思った。つまり、 DateTime::Span があまり富豪的でないのは、 DateTime と DateTime::Duration のみで十分に豊かな表現が可能だからということなのかもしれない。