--- /dev/null
+# Copyright (c) 2003 Flavio Soibelmann Glock. All rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package DateTime::SpanSet;
+
+use strict;
+
+use DateTime::Set;
+use DateTime::Infinite;
+
+use Carp;
+use Params::Validate qw( validate SCALAR BOOLEAN OBJECT CODEREF ARRAYREF );
+use vars qw( $VERSION );
+
+use constant INFINITY => 100 ** 100 ** 100 ;
+use constant NEG_INFINITY => -1 * (100 ** 100 ** 100);
+$VERSION = $DateTime::Set::VERSION;
+
+sub iterate {
+ my ( $self, $callback ) = @_;
+ my $class = ref( $self );
+ my $return = $class->empty_set;
+ $return->{set} = $self->{set}->iterate(
+ sub {
+ my $span = bless { set => $_[0] }, 'DateTime::Span';
+ $callback->( $span->clone );
+ $span = $span->{set}
+ if UNIVERSAL::can( $span, 'union' );
+ return $span;
+ }
+ );
+ $return;
+}
+
+sub map {
+ my ( $self, $callback ) = @_;
+ my $class = ref( $self );
+ die "The callback parameter to map() must be a subroutine reference"
+ unless ref( $callback ) eq 'CODE';
+ my $return = $class->empty_set;
+ $return->{set} = $self->{set}->iterate(
+ sub {
+ local $_ = bless { set => $_[0]->clone }, 'DateTime::Span';
+ my @list = $callback->();
+ my $set = $class->empty_set;
+ $set = $set->union( $_ ) for @list;
+ return $set->{set};
+ }
+ );
+ $return;
+}
+
+sub grep {
+ my ( $self, $callback ) = @_;
+ my $class = ref( $self );
+ die "The callback parameter to grep() must be a subroutine reference"
+ unless ref( $callback ) eq 'CODE';
+ my $return = $class->empty_set;
+ $return->{set} = $self->{set}->iterate(
+ sub {
+ local $_ = bless { set => $_[0]->clone }, 'DateTime::Span';
+ my $result = $callback->();
+ return $_ if $result;
+ return;
+ }
+ );
+ $return;
+}
+
+sub set_time_zone {
+ my ( $self, $tz ) = @_;
+
+ # TODO - use iterate() instead
+
+ my $result = $self->{set}->iterate(
+ sub {
+ my %tmp = %{ $_[0]->{list}[0] };
+ $tmp{a} = $tmp{a}->clone->set_time_zone( $tz ) if ref $tmp{a};
+ $tmp{b} = $tmp{b}->clone->set_time_zone( $tz ) if ref $tmp{b};
+ \%tmp;
+ },
+ backtrack_callback => sub {
+ my ( $min, $max ) = ( $_[0]->min, $_[0]->max );
+ if ( ref($min) )
+ {
+ $min = $min->clone;
+ $min->set_time_zone( 'floating' );
+ }
+ if ( ref($max) )
+ {
+ $max = $max->clone;
+ $max->set_time_zone( 'floating' );
+ }
+ return Set::Infinite::_recurrence->new( $min, $max );
+ },
+ );
+
+ ### this code enables 'subroutine method' behaviour
+ $self->{set} = $result;
+ return $self;
+}
+
+sub from_spans {
+ my $class = shift;
+ my %args = validate( @_,
+ { spans =>
+ { type => ARRAYREF,
+ optional => 1,
+ },
+ }
+ );
+ my $self = {};
+ my $set = Set::Infinite::_recurrence->new();
+ $set = $set->union( $_->{set} ) for @{ $args{spans} };
+ $self->{set} = $set;
+ bless $self, $class;
+ return $self;
+}
+
+sub from_set_and_duration {
+ # set => $dt_set, days => 1
+ my $class = shift;
+ my %args = @_;
+ my $set = delete $args{set} ||
+ carp "from_set_and_duration needs a 'set' parameter";
+
+ $set = $set->as_set
+ if UNIVERSAL::can( $set, 'as_set' );
+ unless ( UNIVERSAL::can( $set, 'union' ) ) {
+ carp "'set' must be a set" };
+
+ my $duration = delete $args{duration} ||
+ new DateTime::Duration( %args );
+ my $end_set = $set->clone->add_duration( $duration );
+ return $class->from_sets( start_set => $set,
+ end_set => $end_set );
+}
+
+sub from_sets {
+ my $class = shift;
+ my %args = validate( @_,
+ { start_set =>
+ { # can => 'union',
+ optional => 0,
+ },
+ end_set =>
+ { # can => 'union',
+ optional => 0,
+ },
+ }
+ );
+ my $start_set = delete $args{start_set};
+ my $end_set = delete $args{end_set};
+
+ $start_set = $start_set->as_set
+ if UNIVERSAL::can( $start_set, 'as_set' );
+ $end_set = $end_set->as_set
+ if UNIVERSAL::can( $end_set, 'as_set' );
+
+ unless ( UNIVERSAL::can( $start_set, 'union' ) ) {
+ carp "'start_set' must be a set" };
+ unless ( UNIVERSAL::can( $end_set, 'union' ) ) {
+ carp "'end_set' must be a set" };
+
+ my $self;
+ $self->{set} = $start_set->{set}->until(
+ $end_set->{set} );
+ bless $self, $class;
+ return $self;
+}
+
+sub start_set {
+ if ( exists $_[0]->{set}{method} &&
+ $_[0]->{set}{method} eq 'until' )
+ {
+ return bless { set => $_[0]->{set}{parent}[0] }, 'DateTime::Set';
+ }
+ my $return = DateTime::Set->empty_set;
+ $return->{set} = $_[0]->{set}->start_set;
+ $return;
+}
+
+sub end_set {
+ if ( exists $_[0]->{set}{method} &&
+ $_[0]->{set}{method} eq 'until' )
+ {
+ return bless { set => $_[0]->{set}{parent}[1] }, 'DateTime::Set';
+ }
+ my $return = DateTime::Set->empty_set;
+ $return->{set} = $_[0]->{set}->end_set;
+ $return;
+}
+
+sub empty_set {
+ my $class = shift;
+
+ return bless { set => Set::Infinite::_recurrence->new }, $class;
+}
+
+sub clone {
+ bless {
+ set => $_[0]->{set}->copy,
+ }, ref $_[0];
+}
+
+
+sub iterator {
+ my $self = shift;
+
+ my %args = @_;
+ my $span;
+ $span = delete $args{span};
+ $span = DateTime::Span->new( %args ) if %args;
+
+ return $self->intersection( $span ) if $span;
+ return $self->clone;
+}
+
+
+# next() gets the next element from an iterator()
+sub next {
+ my ($self) = shift;
+
+ # TODO: this is fixing an error from elsewhere
+ # - find out what's going on! (with "sunset.pl")
+ return undef unless ref $self->{set};
+
+ if ( @_ )
+ {
+ my $max;
+ $max = $_[0]->max if UNIVERSAL::can( $_[0], 'union' );
+ $max = $_[0] if ! defined $max;
+
+ return undef if ! ref( $max ) && $max == INFINITY;
+
+ my $span = DateTime::Span->from_datetimes( start => $max );
+ my $iterator = $self->intersection( $span );
+ my $return = $iterator->next;
+
+ return $return if ! defined $return;
+ return $return if ! $return->intersects( $max );
+
+ return $iterator->next;
+ }
+
+ my ($head, $tail) = $self->{set}->first;
+ $self->{set} = $tail;
+ return $head unless ref $head;
+ my $return = {
+ set => $head,
+ };
+ bless $return, 'DateTime::Span';
+ return $return;
+}
+
+# previous() gets the last element from an iterator()
+sub previous {
+ my ($self) = shift;
+
+ return undef unless ref $self->{set};
+
+ if ( @_ )
+ {
+ my $min;
+ $min = $_[0]->min if UNIVERSAL::can( $_[0], 'union' );
+ $min = $_[0] if ! defined $min;
+
+ return undef if ! ref( $min ) && $min == INFINITY;
+
+ my $span = DateTime::Span->from_datetimes( end => $min );
+ my $iterator = $self->intersection( $span );
+ my $return = $iterator->previous;
+
+ return $return if ! defined $return;
+ return $return if ! $return->intersects( $min );
+
+ return $iterator->previous;
+ }
+
+ my ($head, $tail) = $self->{set}->last;
+ $self->{set} = $tail;
+ return $head unless ref $head;
+ my $return = {
+ set => $head,
+ };
+ bless $return, 'DateTime::Span';
+ return $return;
+}
+
+# "current" means less-or-equal to a DateTime
+sub current {
+ my $self = shift;
+
+ my $previous;
+ my $next;
+ {
+ my $min;
+ $min = $_[0]->min if UNIVERSAL::can( $_[0], 'union' );
+ $min = $_[0] if ! defined $min;
+ return undef if ! ref( $min ) && $min == INFINITY;
+ my $span = DateTime::Span->from_datetimes( end => $min );
+ my $iterator = $self->intersection( $span );
+ $previous = $iterator->previous;
+ $span = DateTime::Span->from_datetimes( start => $min );
+ $iterator = $self->intersection( $span );
+ $next = $iterator->next;
+ }
+ return $previous unless defined $next;
+
+ my $dt1 = defined $previous
+ ? $next->union( $previous )
+ : $next;
+
+ my $return = $dt1->intersected_spans( $_[0] );
+
+ $return = $previous
+ if !defined $return->max;
+
+ bless $return, 'DateTime::SpanSet'
+ if defined $return;
+ return $return;
+}
+
+sub closest {
+ my $self = shift;
+ my $dt = shift;
+
+ my $dt1 = $self->current( $dt );
+ my $dt2 = $self->next( $dt );
+ bless $dt2, 'DateTime::SpanSet'
+ if defined $dt2;
+
+ return $dt2 unless defined $dt1;
+ return $dt1 unless defined $dt2;
+
+ $dt = DateTime::Set->from_datetimes( dates => [ $dt ] )
+ unless UNIVERSAL::can( $dt, 'union' );
+
+ return $dt1 if $dt1->contains( $dt );
+
+ my $delta = $dt->min - $dt1->max;
+ return $dt1 if ( $dt2->min - $delta ) >= $dt->max;
+
+ return $dt2;
+}
+
+sub as_list {
+ my $self = shift;
+ return undef unless ref( $self->{set} );
+
+ my %args = @_;
+ my $span;
+ $span = delete $args{span};
+ $span = DateTime::Span->new( %args ) if %args;
+
+ my $set = $self->clone;
+ $set = $set->intersection( $span ) if $span;
+
+ # Note: removing this line means we may end up in an infinite loop!
+ return undef if $set->{set}->is_too_complex; # undef = no begin/end
+
+ # return if $set->{set}->is_null; # nothing = empty
+ my @result;
+ # we should extract _copies_ of the set elements,
+ # such that the user can't modify the set indirectly
+
+ my $iter = $set->iterator;
+ while ( my $dt = $iter->next )
+ {
+ push @result, $dt
+ if ref( $dt ); # we don't want to return INFINITY value
+ };
+
+ return @result;
+}
+
+# Set::Infinite methods
+
+sub intersection {
+ my ($set1, $set2) = ( shift, shift );
+ my $class = ref($set1);
+ my $tmp = $class->empty_set();
+ $set2 = $set2->as_spanset
+ if $set2->can( 'as_spanset' );
+ $set2 = $set2->as_set
+ if $set2->can( 'as_set' );
+ $set2 = DateTime::Set->from_datetimes( dates => [ $set2, @_ ] )
+ unless $set2->can( 'union' );
+ $tmp->{set} = $set1->{set}->intersection( $set2->{set} );
+ return $tmp;
+}
+
+sub intersected_spans {
+ my ($set1, $set2) = ( shift, shift );
+ my $class = ref($set1);
+ my $tmp = $class->empty_set();
+ $set2 = $set2->as_spanset
+ if $set2->can( 'as_spanset' );
+ $set2 = $set2->as_set
+ if $set2->can( 'as_set' );
+ $set2 = DateTime::Set->from_datetimes( dates => [ $set2, @_ ] )
+ unless $set2->can( 'union' );
+ $tmp->{set} = $set1->{set}->intersected_spans( $set2->{set} );
+ return $tmp;
+}
+
+sub intersects {
+ my ($set1, $set2) = ( shift, shift );
+
+ unless ( $set2->can( 'union' ) )
+ {
+ for ( $set2, @_ )
+ {
+ return 1 if $set1->contains( $_ );
+ }
+ return 0;
+ }
+
+ my $class = ref($set1);
+ $set2 = $set2->as_spanset
+ if $set2->can( 'as_spanset' );
+ $set2 = $set2->as_set
+ if $set2->can( 'as_set' );
+ $set2 = DateTime::Set->from_datetimes( dates => [ $set2, @_ ] )
+ unless $set2->can( 'union' );
+ return $set1->{set}->intersects( $set2->{set} );
+}
+
+sub contains {
+ my ($set1, $set2) = ( shift, shift );
+
+ unless ( $set2->can( 'union' ) )
+ {
+ if ( exists $set1->{set}{method} &&
+ $set1->{set}{method} eq 'until' )
+ {
+ my $start_set = $set1->start_set;
+ my $end_set = $set1->end_set;
+
+ for ( $set2, @_ )
+ {
+ my $start = $start_set->next( $set2 );
+ my $end = $end_set->next( $set2 );
+
+ goto ABORT unless defined $start && defined $end;
+
+ return 0 if $start < $end;
+ }
+ return 1;
+
+ ABORT: ;
+ # don't know
+ }
+ }
+
+ my $class = ref($set1);
+ $set2 = $set2->as_spanset
+ if $set2->can( 'as_spanset' );
+ $set2 = $set2->as_set
+ if $set2->can( 'as_set' );
+ $set2 = DateTime::Set->from_datetimes( dates => [ $set2, @_ ] )
+ unless $set2->can( 'union' );
+ return $set1->{set}->contains( $set2->{set} );
+}
+
+sub union {
+ my ($set1, $set2) = ( shift, shift );
+ my $class = ref($set1);
+ my $tmp = $class->empty_set();
+ $set2 = $set2->as_spanset
+ if $set2->can( 'as_spanset' );
+ $set2 = $set2->as_set
+ if $set2->can( 'as_set' );
+ $set2 = DateTime::Set->from_datetimes( dates => [ $set2, @_ ] )
+ unless $set2->can( 'union' );
+ $tmp->{set} = $set1->{set}->union( $set2->{set} );
+ return $tmp;
+}
+
+sub complement {
+ my ($set1, $set2) = ( shift, shift );
+ my $class = ref($set1);
+ my $tmp = $class->empty_set();
+ if (defined $set2) {
+ $set2 = $set2->as_spanset
+ if $set2->can( 'as_spanset' );
+ $set2 = $set2->as_set
+ if $set2->can( 'as_set' );
+ $set2 = DateTime::Set->from_datetimes( dates => [ $set2, @_ ] )
+ unless $set2->can( 'union' );
+ $tmp->{set} = $set1->{set}->complement( $set2->{set} );
+ }
+ else {
+ $tmp->{set} = $set1->{set}->complement;
+ }
+ return $tmp;
+}
+
+sub min {
+ return DateTime::Set::_fix_datetime( $_[0]->{set}->min );
+}
+
+sub max {
+ return DateTime::Set::_fix_datetime( $_[0]->{set}->max );
+}
+
+# returns a DateTime::Span
+sub span {
+ my $set = $_[0]->{set}->span;
+ my $self = bless { set => $set }, 'DateTime::Span';
+ return $self;
+}
+
+# returns a DateTime::Duration
+sub duration {
+ my $dur;
+
+ return DateTime::Duration->new( seconds => 0 )
+ if $_[0]->{set}->is_empty;
+
+ local $@;
+ eval {
+ local $SIG{__DIE__}; # don't want to trap this (rt ticket 5434)
+ $dur = $_[0]->{set}->size
+ };
+
+ return $dur if defined $dur && ref( $dur );
+ return DateTime::Infinite::Future->new -
+ DateTime::Infinite::Past->new;
+ # return INFINITY;
+}
+*size = \&duration;
+
+1;
+
+__END__
+
+=head1 NAME
+
+DateTime::SpanSet - set of DateTime spans
+
+=head1 SYNOPSIS
+
+ $spanset = DateTime::SpanSet->from_spans( spans => [ $dt_span, $dt_span ] );
+
+ $set = $spanset->union( $set2 ); # like "OR", "insert", "both"
+ $set = $spanset->complement( $set2 ); # like "delete", "remove"
+ $set = $spanset->intersection( $set2 ); # like "AND", "while"
+ $set = $spanset->complement; # like "NOT", "negate", "invert"
+
+ if ( $spanset->intersects( $set2 ) ) { ... # like "touches", "interferes"
+ if ( $spanset->contains( $set2 ) ) { ... # like "is-fully-inside"
+
+ # data extraction
+ $date = $spanset->min; # first date of the set
+ $date = $spanset->max; # last date of the set
+
+ $iter = $spanset->iterator;
+ while ( $dt = $iter->next ) {
+ # $dt is a DateTime::Span
+ print $dt->start->ymd; # first date of span
+ print $dt->end->ymd; # last date of span
+ };
+
+=head1 DESCRIPTION
+
+C<DateTime::SpanSet> is a class that represents sets of datetime
+spans. An example would be a recurring meeting that occurs from
+13:00-15:00 every Friday.
+
+This is different from a C<DateTime::Set>, which is made of individual
+datetime points as opposed to ranges.
+
+=head1 METHODS
+
+=over 4
+
+=item * from_spans
+
+Creates a new span set from one or more C<DateTime::Span> objects.
+
+ $spanset = DateTime::SpanSet->from_spans( spans => [ $dt_span ] );
+
+=item * from_set_and_duration
+
+Creates a new span set from one or more C<DateTime::Set> objects and a
+duration.
+
+The duration can be a C<DateTime::Duration> object, or the parameters
+to create a new C<DateTime::Duration> object, such as "days",
+"months", etc.
+
+ $spanset =
+ DateTime::SpanSet->from_set_and_duration
+ ( set => $dt_set, days => 1 );
+
+=item * from_sets
+
+Creates a new span set from two C<DateTime::Set> objects.
+
+One set defines the I<starting dates>, and the other defines the I<end
+dates>.
+
+ $spanset =
+ DateTime::SpanSet->from_sets
+ ( start_set => $dt_set1, end_set => $dt_set2 );
+
+The spans have the starting date C<closed>, and the end date C<open>,
+like in C<[$dt1, $dt2)>.
+
+If an end date comes without a starting date before it, then it
+defines a span like C<(-inf, $dt)>.
+
+If a starting date comes without an end date after it, then it defines
+a span like C<[$dt, inf)>.
+
+=item * empty_set
+
+Creates a new empty set.
+
+=item * clone
+
+This object method returns a replica of the given object.
+
+=item * set_time_zone( $tz )
+
+This method accepts either a time zone object or a string that can be
+passed as the "name" parameter to C<< DateTime::TimeZone->new() >>.
+If the new time zone's offset is different from the old time zone,
+then the I<local> time is adjusted accordingly.
+
+If the old time zone was a floating time zone, then no adjustments to
+the local time are made, except to account for leap seconds. If the
+new time zone is floating, then the I<UTC> time is adjusted in order
+to leave the local time untouched.
+
+=item * min
+
+=item * max
+
+First or last dates in the set. These methods may return C<undef> if
+the set is empty. It is also possible that these methods may return a
+scalar containing infinity or negative infinity.
+
+=item * duration
+
+The total size of the set, as a C<DateTime::Duration> object.
+
+The duration may be infinite.
+
+Also available as C<size()>.
+
+=item * span
+
+The total span of the set, as a C<DateTime::Span> object.
+
+=item * next
+
+ my $span = $set->next( $dt );
+
+This method is used to find the next span in the set,
+after a given datetime or span.
+
+The return value is a C<DateTime::Span>, or C<undef> if there is no matching
+span in the set.
+
+=item * previous
+
+ my $span = $set->previous( $dt );
+
+This method is used to find the previous span in the set,
+before a given datetime or span.
+
+The return value is a C<DateTime::Span>, or C<undef> if there is no matching
+span in the set.
+
+
+=item * current
+
+ my $span = $set->current( $dt );
+
+This method is used to find the "current" span in the set,
+that intersects a given datetime or span. If no current span
+is found, then the "previous" span is returned.
+
+The return value is a C<DateTime::SpanSet>, or C<undef> if there is no
+matching span in the set.
+
+If a span parameter is given, it may happen that "current" returns
+more than one span.
+
+See also: C<intersected_spans()> method.
+
+=item * closest
+
+ my $span = $set->closest( $dt );
+
+This method is used to find the "closest" span in the set, given a
+datetime or span.
+
+The return value is a C<DateTime::SpanSet>, or C<undef> if the set is
+empty.
+
+If a span parameter is given, it may happen that "closest" returns
+more than one span.
+
+=item * as_list
+
+Returns a list of C<DateTime::Span> objects.
+
+ my @dt_span = $set->as_list( span => $span );
+
+Just as with the C<iterator()> method, the C<as_list()> method can be
+limited by a span.
+
+Applying C<as_list()> to a large recurring spanset is a very expensive
+operation, both in CPU time and in the memory used.
+
+For this reason, when C<as_list()> operates on large recurrence sets,
+it will return at most approximately 200 spans. For larger sets, and
+for I<infinite> sets, C<as_list()> will return C<undef>.
+
+Please note that this is explicitly not an empty list, since an empty
+list is a valid return value for empty sets!
+
+If you I<really> need to extract spans from a large set, you can:
+
+- limit the set with a shorter span:
+
+ my @short_list = $large_set->as_list( span => $short_span );
+
+- use an iterator:
+
+ my @large_list;
+ my $iter = $large_set->iterator;
+ push @large_list, $dt while $dt = $iter->next;
+
+=item * union
+
+=item * intersection
+
+=item * complement
+
+Set operations may be performed not only with C<DateTime::SpanSet>
+objects, but also with C<DateTime>, C<DateTime::Set> and
+C<DateTime::Span> objects. These set operations always return a
+C<DateTime::SpanSet> object.
+
+ $set = $spanset->union( $set2 ); # like "OR", "insert", "both"
+ $set = $spanset->complement( $set2 ); # like "delete", "remove"
+ $set = $spanset->intersection( $set2 ); # like "AND", "while"
+ $set = $spanset->complement; # like "NOT", "negate", "invert"
+
+=item * intersected_spans
+
+This method can accept a C<DateTime> list, a C<DateTime::Set>, a
+C<DateTime::Span>, or a C<DateTime::SpanSet> object as an argument.
+
+ $set = $set1->intersected_spans( $set2 );
+
+The method always returns a C<DateTime::SpanSet> object, containing
+all spans that are intersected by the given set.
+
+Unlike the C<intersection> method, the spans are not modified. See
+diagram below:
+
+ set1 [....] [....] [....] [....]
+ set2 [................]
+
+ intersection [.] [....] [.]
+
+ intersected_spans [....] [....] [....]
+
+=item * intersects
+
+=item * contains
+
+These set functions return a boolean value.
+
+ if ( $spanset->intersects( $set2 ) ) { ... # like "touches", "interferes"
+ if ( $spanset->contains( $dt ) ) { ... # like "is-fully-inside"
+
+These methods can accept a C<DateTime>, C<DateTime::Set>,
+C<DateTime::Span>, or C<DateTime::SpanSet> object as an argument.
+
+=item * iterator / next / previous
+
+This method can be used to iterate over the spans in a set.
+
+ $iter = $spanset->iterator;
+ while ( $dt = $iter->next ) {
+ # $dt is a DateTime::Span
+ print $dt->min->ymd; # first date of span
+ print $dt->max->ymd; # last date of span
+ }
+
+The boundaries of the iterator can be limited by passing it a C<span>
+parameter. This should be a C<DateTime::Span> object which delimits
+the iterator's boundaries. Optionally, instead of passing an object,
+you can pass any parameters that would work for one of the
+C<DateTime::Span> class's constructors, and an object will be created
+for you.
+
+Obviously, if the span you specify does is not restricted both at the
+start and end, then your iterator may iterate forever, depending on
+the nature of your set. User beware!
+
+The C<next()> or C<previous()> methods will return C<undef> when there
+are no more spans in the iterator.
+
+=item * start_set
+
+=item * end_set
+
+These methods do the inverse of the C<from_sets> method:
+
+C<start_set> retrieves a DateTime::Set with the start datetime of each
+span.
+
+C<end_set> retrieves a DateTime::Set with the end datetime of each
+span.
+
+=item * map ( sub { ... } )
+
+ # example: enlarge the spans
+ $set = $set2->map(
+ sub {
+ my $start = $_->start;
+ my $end = $_->end;
+ return DateTime::Span->from_datetimes(
+ start => $start,
+ before => $end,
+ );
+ }
+ );
+
+This method is the "set" version of Perl "map".
+
+It evaluates a subroutine for each element of the set (locally setting
+"$_" to each DateTime::Span) and returns the set composed of the
+results of each such evaluation.
+
+Like Perl "map", each element of the set may produce zero, one, or
+more elements in the returned value.
+
+Unlike Perl "map", changing "$_" does not change the original
+set. This means that calling map in void context has no effect.
+
+The callback subroutine may not be called immediately. Don't count on
+subroutine side-effects. For example, a C<print> inside the subroutine
+may happen later than you expect.
+
+The callback return value is expected to be within the span of the
+C<previous> and the C<next> element in the original set.
+
+For example: given the set C<[ 2001, 2010, 2015 ]>, the callback
+result for the value C<2010> is expected to be within the span C<[
+2001 .. 2015 ]>.
+
+=item * grep ( sub { ... } )
+
+ # example: filter out all spans happening today
+ my $today = DateTime->today;
+ $set = $set2->grep(
+ sub {
+ return ( ! $_->contains( $today ) );
+ }
+ );
+
+This method is the "set" version of Perl "grep".
+
+It evaluates a subroutine for each element of the set (locally setting
+"$_" to each DateTime::Span) and returns the set consisting of those
+elements for which the expression evaluated to true.
+
+Unlike Perl "grep", changing "$_" does not change the original
+set. This means that calling grep in void context has no effect.
+
+Changing "$_" does change the resulting set.
+
+The callback subroutine may not be called immediately. Don't count on
+subroutine side-effects. For example, a C<print> inside the subroutine
+may happen later than you expect.
+
+=item * iterate
+
+I<Internal method - use "map" or "grep" instead.>
+
+This function apply a callback subroutine to all elements of a set and
+returns the resulting set.
+
+The parameter C<$_[0]> to the callback subroutine is a
+C<DateTime::Span> object.
+
+If the callback returns C<undef>, the datetime is removed from the
+set:
+
+ sub remove_sundays {
+ $_[0] unless $_[0]->start->day_of_week == 7;
+ }
+
+The callback return value is expected to be within the span of the
+C<previous> and the C<next> element in the original set.
+
+For example: given the set C<[ 2001, 2010, 2015 ]>, the callback
+result for the value C<2010> is expected to be within the span C<[
+2001 .. 2015 ]>.
+
+The callback subroutine may not be called immediately. Don't count on
+subroutine side-effects. For example, a C<print> inside the subroutine
+may happen later than you expect.
+
+=back
+
+=head1 SUPPORT
+
+Support is offered through the C<datetime@perl.org> mailing list.
+
+Please report bugs using rt.cpan.org
+
+=head1 AUTHOR
+
+Flavio Soibelmann Glock <fglock@pucrs.br>
+
+The API was developed together with Dave Rolsky and the DateTime Community.
+
+=head1 COPYRIGHT
+
+Copyright (c) 2003 Flavio Soibelmann Glock. All rights reserved.
+This program is free software; you can distribute it and/or
+modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the LICENSE file
+included with this module.
+
+=head1 SEE ALSO
+
+Set::Infinite
+
+For details on the Perl DateTime Suite project please see
+L<http://datetime.perl.org>.
+
+=cut
+