The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

use strict;

use Test::More;
plan tests => 17;

use DateTime;
use DateTime::Duration;
use DateTime::Set;
use DateTime::Infinite;

my $res;

my $t0 = new DateTime( year => '1810', month => '05', day => '01' );
my $t1 = new DateTime( year => '1810', month => '08', day => '01' );
my $t2 = new DateTime( year => '1810', month => '11', day => '01' );

{
    # diag( "monthly from 1810-08-01 until infinity" );

    my $_next_month = sub {
            # warn "next of ". $_[0]->datetime;
            $_[0]->truncate( to => 'month' );
            $_[0]->add( months => 1 );
            return $_[0] if $_[0] >= $t1;
            return $t1->clone;
        };
    my $_previous_month = sub {
            # warn "previous of ". $_[0]->datetime;
            my $dt = $_[0]->clone;
            $_[0]->truncate( to => 'month' );
            $_[0]->subtract( months => 1 ) if $_[0] == $dt;
            return $_[0] if $_[0] >= $t1;
            return DateTime::Infinite::Past->new;
        };

    my $months = DateTime::Set->from_recurrence(
        next =>     $_next_month,
        previous => $_previous_month,
        # detect_bounded => 1,
    );

    # contains datetime, semi-bounded set

    is( $months->contains( $t0 ), 0, "does not contain datetime" );
    is( $months->contains( $t0, $t2 ), 0, "does not contain datetime list" );
    is( $months->contains( $t2 ), 1, "contains datetime" );

    is( $months->intersects( $t0 ), 0, "does not intersect datetime" );
    is( $months->intersects( $t0, $t2 ), 1, "intersects datetime list" );
    is( $months->intersects( $t2 ), 1, "intersects datetime" );


    $res = $months->min;
    $res = $res->ymd if ref($res);
    is( $res, '1810-08-01', 
        "min()" );
    $res = $months->max;
    is( ref($res), 'DateTime::Infinite::Future',
        "max()" );

}

{
    # diag( "monthly from infinity until 1810-08-01" );

    my $_next_month = sub {
            # warn "next of ". $_[0]->datetime;
            $_[0]->truncate( to => 'month' );
            $_[0]->add( months => 1 );
            # warn " got ".$_[0]->datetime."\n" if $_[0] <= $t1;
            return $_[0] if $_[0] <= $t1;
            # warn " got Future\n";
            return DateTime::Infinite::Future->new;
        };
    my $_previous_month = sub {
            # warn "previous of ". $_[0]->datetime;
            # warn " got ".$t1->datetime."\n" if $_[0] > $t1;
            return $t1->clone if $_[0] > $t1;
            my $dt = $_[0]->clone;
            $_[0]->truncate( to => 'month' );
            $_[0]->subtract( months => 1 ) if $_[0] == $dt;
            # warn " got ".$_[0]->datetime."\n";
            return $_[0];
        };

    my $months = DateTime::Set->from_recurrence(
        next =>     $_next_month,
        previous => $_previous_month,
        # detect_bounded => 1,
    );

    $res = $months->min;
    # $res = $res->ymd if ref($res);
    is( ref($res), 'DateTime::Infinite::Past',
        "min()" );

    $res = $months->max;
    $res = $res->ymd if ref($res);
    is( $res, '1810-08-01',   
        "max()" );

    is( $months->count, undef, "count" );

}


{
    # diag( "monthly from 1810-08-01 until 1810-11-01" );

    my $_next_month = sub {
            # warn "next of ". $_[0]->datetime;
            $_[0]->truncate( to => 'month' );
            $_[0]->add( months => 1 );
            return $t1->clone if $_[0] < $t1;
            return $_[0] if $_[0] <= $t2;
            return DateTime::Infinite::Future->new;
        };
    my $_previous_month = sub {
            # warn "previous of ". $_[0]->datetime;
            my $dt = $_[0]->clone;
            $_[0]->truncate( to => 'month' );
            $_[0]->subtract( months => 1 ) if $_[0] == $dt;
            return DateTime::Infinite::Past->new if $_[0] < $t1;
            return $_[0] if $_[0] <= $t2;
            return $t2->clone;
        };

    my $months = DateTime::Set->from_recurrence(
        next =>     $_next_month,
        previous => $_previous_month,
        # detect_bounded => 1,
    );

    $res = $months->min;
    $res = $res->ymd if ref($res);
    is( $res, '1810-08-01',
        "min()" );

    $res = $months->max;
    $res = $res->ymd if ref($res);
    is( $res, '1810-11-01',
        "max()" );

    is( $months->count, 4, "count" );

}


{
    # diag( "lists and recurrences are interchangeable" );

    my $set = DateTime::Set->from_datetimes(
        dates => [ $t0, $t1, $t2 ]
    );


    my $months = DateTime::Set->from_recurrence(
        next =>  sub{ 
            my $dt = $set->next( $_[0] ); 
            defined $dt ? $dt : DateTime::Infinite::Future->new;
        },
        previous => sub{ 
            my $dt = $set->previous( $_[0] ); 
            defined $dt ? $dt : DateTime::Infinite::Past->new;
        },
        # detect_bounded => 1,
    );

    $res = $months->min;
    $res = $res->ymd if ref($res);
    is( $res , '1810-05-01',
        "min()" );

    $res = $months->max;
    $res = $res->ymd if ref($res);
    is( $res, '1810-11-01',   
        "max()" );

    is( $months->count, 3, "count" );

}


1;