| Filename | /usr/share/perl5/DateTime/Set.pm |
| Statements | Executed 35 statements in 4.62ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 1 | 1 | 1 | 8.95ms | 19.0ms | DateTime::Set::BEGIN@10 |
| 1 | 1 | 1 | 1.83ms | 6.37ms | DateTime::Set::BEGIN@9 |
| 1 | 1 | 1 | 1.71ms | 2.09ms | DateTime::Set::BEGIN@11 |
| 1 | 1 | 1 | 59µs | 84µs | DateTime::Set::BEGIN@7 |
| 1 | 1 | 1 | 18µs | 82µs | DateTime::Set::BEGIN@6 |
| 1 | 1 | 1 | 17µs | 21µs | DateTime::Set::BEGIN@4 |
| 1 | 1 | 1 | 12µs | 41µs | DateTime::Set::BEGIN@13 |
| 1 | 1 | 1 | 12µs | 83µs | DateTime::Set::BEGIN@15 |
| 1 | 1 | 1 | 11µs | 11µs | DateTime::Set::BEGIN@8 |
| 1 | 1 | 1 | 9µs | 51µs | DateTime::Set::BEGIN@5 |
| 1 | 1 | 1 | 9µs | 42µs | DateTime::Set::BEGIN@16 |
| 1 | 1 | 1 | 6µs | 6µs | DateTime::Set::BEGIN@18 |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:110] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:129] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:143] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:155] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:169] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:185] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:216] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:224] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:233] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:241] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:71] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::__ANON__[:91] |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::_callback_next |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::_callback_previous |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::_fix_datetime |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::_fix_return_datetime |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::add |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::add_duration |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::as_list |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::clone |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::closest |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::complement |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::contains |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::count |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::current |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::empty_set |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::from_datetimes |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::from_recurrence |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::grep |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::intersection |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::intersects |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::iterate |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::iterator |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::map |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::max |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::min |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::next |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::previous |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::set |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::set_time_zone |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::span |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::subtract |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::subtract_duration |
| 0 | 0 | 0 | 0s | 0s | DateTime::Set::union |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | |||||
| 2 | package DateTime::Set; | ||||
| 3 | |||||
| 4 | 3 | 25µs | 2 | 24µs | # spent 21µs (17+4) within DateTime::Set::BEGIN@4 which was called:
# once (17µs+4µs) by Koha::Calendar::BEGIN@7 at line 4 # spent 21µs making 1 call to DateTime::Set::BEGIN@4
# spent 4µs making 1 call to strict::import |
| 5 | 3 | 33µs | 2 | 94µs | # spent 51µs (9+42) within DateTime::Set::BEGIN@5 which was called:
# once (9µs+42µs) by Koha::Calendar::BEGIN@7 at line 5 # spent 51µs making 1 call to DateTime::Set::BEGIN@5
# spent 42µs making 1 call to Exporter::import |
| 6 | 3 | 78µs | 2 | 145µs | # spent 82µs (18+64) within DateTime::Set::BEGIN@6 which was called:
# once (18µs+64µs) by Koha::Calendar::BEGIN@7 at line 6 # spent 82µs making 1 call to DateTime::Set::BEGIN@6
# spent 64µs making 1 call to Exporter::import |
| 7 | 3 | 71µs | 2 | 109µs | # spent 84µs (59+25) within DateTime::Set::BEGIN@7 which was called:
# once (59µs+25µs) by Koha::Calendar::BEGIN@7 at line 7 # spent 84µs making 1 call to DateTime::Set::BEGIN@7
# spent 25µs making 1 call to UNIVERSAL::VERSION |
| 8 | 3 | 30µs | 1 | 11µs | # spent 11µs within DateTime::Set::BEGIN@8 which was called:
# once (11µs+0s) by Koha::Calendar::BEGIN@7 at line 8 # spent 11µs making 1 call to DateTime::Set::BEGIN@8 |
| 9 | 3 | 137µs | 1 | 6.37ms | # spent 6.37ms (1.83+4.55) within DateTime::Set::BEGIN@9 which was called:
# once (1.83ms+4.55ms) by Koha::Calendar::BEGIN@7 at line 9 # spent 6.37ms making 1 call to DateTime::Set::BEGIN@9 |
| 10 | 3 | 240µs | 3 | 19.1ms | # spent 19.0ms (8.95+10.0) within DateTime::Set::BEGIN@10 which was called:
# once (8.95ms+10.0ms) by Koha::Calendar::BEGIN@7 at line 10 # spent 19.0ms making 1 call to DateTime::Set::BEGIN@10
# spent 72µs making 1 call to UNIVERSAL::VERSION
# spent 48µs making 1 call to Exporter::import |
| 11 | 3 | 227µs | 2 | 2.13ms | # spent 2.09ms (1.71+380µs) within DateTime::Set::BEGIN@11 which was called:
# once (1.71ms+380µs) by Koha::Calendar::BEGIN@7 at line 11 # spent 2.09ms making 1 call to DateTime::Set::BEGIN@11
# spent 41µs making 1 call to Exporter::import |
| 12 | |||||
| 13 | 3 | 41µs | 2 | 70µs | # spent 41µs (12+29) within DateTime::Set::BEGIN@13 which was called:
# once (12µs+29µs) by Koha::Calendar::BEGIN@7 at line 13 # spent 41µs making 1 call to DateTime::Set::BEGIN@13
# spent 29µs making 1 call to vars::import |
| 14 | |||||
| 15 | 3 | 42µs | 2 | 154µs | # spent 83µs (12+71) within DateTime::Set::BEGIN@15 which was called:
# once (12µs+71µs) by Koha::Calendar::BEGIN@7 at line 15 # spent 83µs making 1 call to DateTime::Set::BEGIN@15
# spent 71µs making 1 call to constant::import |
| 16 | 3 | 37µs | 2 | 75µs | # spent 42µs (9+33) within DateTime::Set::BEGIN@16 which was called:
# once (9µs+33µs) by Koha::Calendar::BEGIN@7 at line 16 # spent 42µs making 1 call to DateTime::Set::BEGIN@16
# spent 33µs making 1 call to constant::import |
| 17 | |||||
| 18 | # spent 6µs within DateTime::Set::BEGIN@18 which was called:
# once (6µs+0s) by Koha::Calendar::BEGIN@7 at line 20 | ||||
| 19 | 1 | 10µs | $VERSION = '0.28'; | ||
| 20 | 1 | 3.64ms | 1 | 6µs | } # spent 6µs making 1 call to DateTime::Set::BEGIN@18 |
| 21 | |||||
| 22 | |||||
| 23 | sub _fix_datetime { | ||||
| 24 | # internal function - | ||||
| 25 | # (not a class method) | ||||
| 26 | # | ||||
| 27 | # checks that the parameter is an object, and | ||||
| 28 | # also protects the object against mutation | ||||
| 29 | |||||
| 30 | return $_[0] | ||||
| 31 | unless defined $_[0]; # error | ||||
| 32 | return $_[0]->clone | ||||
| 33 | if ref( $_[0] ); # "immutable" datetime | ||||
| 34 | return DateTime::Infinite::Future->new | ||||
| 35 | if $_[0] == INFINITY; # Inf | ||||
| 36 | return DateTime::Infinite::Past->new | ||||
| 37 | if $_[0] == NEG_INFINITY; # -Inf | ||||
| 38 | return $_[0]; # error | ||||
| 39 | } | ||||
| 40 | |||||
| 41 | sub _fix_return_datetime { | ||||
| 42 | my ( $dt, $dt_arg ) = @_; | ||||
| 43 | |||||
| 44 | # internal function - | ||||
| 45 | # (not a class method) | ||||
| 46 | # | ||||
| 47 | # checks that the returned datetime has the same | ||||
| 48 | # time zone as the parameter | ||||
| 49 | |||||
| 50 | # TODO: set locale | ||||
| 51 | |||||
| 52 | return unless $dt; | ||||
| 53 | return unless $dt_arg; | ||||
| 54 | if ( $dt_arg->can('time_zone_long_name') && | ||||
| 55 | !( $dt_arg->time_zone_long_name eq 'floating' ) ) | ||||
| 56 | { | ||||
| 57 | $dt->set_time_zone( $dt_arg->time_zone ); | ||||
| 58 | } | ||||
| 59 | return $dt; | ||||
| 60 | } | ||||
| 61 | |||||
| 62 | sub iterate { | ||||
| 63 | # deprecated method - use map() or grep() instead | ||||
| 64 | my ( $self, $callback ) = @_; | ||||
| 65 | my $class = ref( $self ); | ||||
| 66 | my $return = $class->empty_set; | ||||
| 67 | $return->{set} = $self->{set}->iterate( | ||||
| 68 | sub { | ||||
| 69 | my $min = $_[0]->min; | ||||
| 70 | $callback->( $min->clone ) if ref($min); | ||||
| 71 | } | ||||
| 72 | ); | ||||
| 73 | $return; | ||||
| 74 | } | ||||
| 75 | |||||
| 76 | sub map { | ||||
| 77 | my ( $self, $callback ) = @_; | ||||
| 78 | my $class = ref( $self ); | ||||
| 79 | die "The callback parameter to map() must be a subroutine reference" | ||||
| 80 | unless ref( $callback ) eq 'CODE'; | ||||
| 81 | my $return = $class->empty_set; | ||||
| 82 | $return->{set} = $self->{set}->iterate( | ||||
| 83 | sub { | ||||
| 84 | local $_ = $_[0]->min; | ||||
| 85 | next unless ref( $_ ); | ||||
| 86 | $_ = $_->clone; | ||||
| 87 | my @list = $callback->(); | ||||
| 88 | my $set = Set::Infinite::_recurrence->new(); | ||||
| 89 | $set = $set->union( $_ ) for @list; | ||||
| 90 | return $set; | ||||
| 91 | } | ||||
| 92 | ); | ||||
| 93 | $return; | ||||
| 94 | } | ||||
| 95 | |||||
| 96 | sub grep { | ||||
| 97 | my ( $self, $callback ) = @_; | ||||
| 98 | my $class = ref( $self ); | ||||
| 99 | die "The callback parameter to grep() must be a subroutine reference" | ||||
| 100 | unless ref( $callback ) eq 'CODE'; | ||||
| 101 | my $return = $class->empty_set; | ||||
| 102 | $return->{set} = $self->{set}->iterate( | ||||
| 103 | sub { | ||||
| 104 | local $_ = $_[0]->min; | ||||
| 105 | next unless ref( $_ ); | ||||
| 106 | $_ = $_->clone; | ||||
| 107 | my $result = $callback->(); | ||||
| 108 | return $_ if $result; | ||||
| 109 | return; | ||||
| 110 | } | ||||
| 111 | ); | ||||
| 112 | $return; | ||||
| 113 | } | ||||
| 114 | |||||
| 115 | sub add { return shift->add_duration( DateTime::Duration->new(@_) ) } | ||||
| 116 | |||||
| 117 | sub subtract { return shift->subtract_duration( DateTime::Duration->new(@_) ) } | ||||
| 118 | |||||
| 119 | sub subtract_duration { return $_[0]->add_duration( $_[1]->inverse ) } | ||||
| 120 | |||||
| 121 | sub add_duration { | ||||
| 122 | my ( $self, $dur ) = @_; | ||||
| 123 | $dur = $dur->clone; # $dur must be "immutable" | ||||
| 124 | |||||
| 125 | $self->{set} = $self->{set}->iterate( | ||||
| 126 | sub { | ||||
| 127 | my $min = $_[0]->min; | ||||
| 128 | $min->clone->add_duration( $dur ) if ref($min); | ||||
| 129 | }, | ||||
| 130 | backtrack_callback => sub { | ||||
| 131 | my ( $min, $max ) = ( $_[0]->min, $_[0]->max ); | ||||
| 132 | if ( ref($min) ) | ||||
| 133 | { | ||||
| 134 | $min = $min->clone; | ||||
| 135 | $min->subtract_duration( $dur ); | ||||
| 136 | } | ||||
| 137 | if ( ref($max) ) | ||||
| 138 | { | ||||
| 139 | $max = $max->clone; | ||||
| 140 | $max->subtract_duration( $dur ); | ||||
| 141 | } | ||||
| 142 | return Set::Infinite::_recurrence->new( $min, $max ); | ||||
| 143 | }, | ||||
| 144 | ); | ||||
| 145 | $self; | ||||
| 146 | } | ||||
| 147 | |||||
| 148 | sub set_time_zone { | ||||
| 149 | my ( $self, $tz ) = @_; | ||||
| 150 | |||||
| 151 | $self->{set} = $self->{set}->iterate( | ||||
| 152 | sub { | ||||
| 153 | my $min = $_[0]->min; | ||||
| 154 | $min->clone->set_time_zone( $tz ) if ref($min); | ||||
| 155 | }, | ||||
| 156 | backtrack_callback => sub { | ||||
| 157 | my ( $min, $max ) = ( $_[0]->min, $_[0]->max ); | ||||
| 158 | if ( ref($min) ) | ||||
| 159 | { | ||||
| 160 | $min = $min->clone; | ||||
| 161 | $min->set_time_zone( $tz ); | ||||
| 162 | } | ||||
| 163 | if ( ref($max) ) | ||||
| 164 | { | ||||
| 165 | $max = $max->clone; | ||||
| 166 | $max->set_time_zone( $tz ); | ||||
| 167 | } | ||||
| 168 | return Set::Infinite::_recurrence->new( $min, $max ); | ||||
| 169 | }, | ||||
| 170 | ); | ||||
| 171 | $self; | ||||
| 172 | } | ||||
| 173 | |||||
| 174 | sub set { | ||||
| 175 | my $self = shift; | ||||
| 176 | my %args = validate( @_, | ||||
| 177 | { locale => { type => SCALAR | OBJECT, | ||||
| 178 | default => undef }, | ||||
| 179 | } | ||||
| 180 | ); | ||||
| 181 | $self->{set} = $self->{set}->iterate( | ||||
| 182 | sub { | ||||
| 183 | my $min = $_[0]->min; | ||||
| 184 | $min->clone->set( %args ) if ref($min); | ||||
| 185 | }, | ||||
| 186 | ); | ||||
| 187 | $self; | ||||
| 188 | } | ||||
| 189 | |||||
| 190 | sub from_recurrence { | ||||
| 191 | my $class = shift; | ||||
| 192 | |||||
| 193 | my %args = @_; | ||||
| 194 | my %param; | ||||
| 195 | |||||
| 196 | # Parameter renaming, such that we can use either | ||||
| 197 | # recurrence => xxx or next => xxx, previous => xxx | ||||
| 198 | $param{next} = delete $args{recurrence} || delete $args{next}; | ||||
| 199 | $param{previous} = delete $args{previous}; | ||||
| 200 | |||||
| 201 | $param{span} = delete $args{span}; | ||||
| 202 | # they might be specifying a span using begin / end | ||||
| 203 | $param{span} = DateTime::Span->new( %args ) if keys %args; | ||||
| 204 | |||||
| 205 | my $self = {}; | ||||
| 206 | |||||
| 207 | die "Not enough arguments in from_recurrence()" | ||||
| 208 | unless $param{next} || $param{previous}; | ||||
| 209 | |||||
| 210 | if ( ! $param{previous} ) | ||||
| 211 | { | ||||
| 212 | my $data = {}; | ||||
| 213 | $param{previous} = | ||||
| 214 | sub { | ||||
| 215 | _callback_previous ( _fix_datetime( $_[0] ), $param{next}, $data ); | ||||
| 216 | } | ||||
| 217 | } | ||||
| 218 | else | ||||
| 219 | { | ||||
| 220 | my $previous = $param{previous}; | ||||
| 221 | $param{previous} = | ||||
| 222 | sub { | ||||
| 223 | $previous->( _fix_datetime( $_[0] ) ); | ||||
| 224 | } | ||||
| 225 | } | ||||
| 226 | |||||
| 227 | if ( ! $param{next} ) | ||||
| 228 | { | ||||
| 229 | my $data = {}; | ||||
| 230 | $param{next} = | ||||
| 231 | sub { | ||||
| 232 | _callback_next ( _fix_datetime( $_[0] ), $param{previous}, $data ); | ||||
| 233 | } | ||||
| 234 | } | ||||
| 235 | else | ||||
| 236 | { | ||||
| 237 | my $next = $param{next}; | ||||
| 238 | $param{next} = | ||||
| 239 | sub { | ||||
| 240 | $next->( _fix_datetime( $_[0] ) ); | ||||
| 241 | } | ||||
| 242 | } | ||||
| 243 | |||||
| 244 | my ( $min, $max ); | ||||
| 245 | $max = $param{previous}->( DateTime::Infinite::Future->new ); | ||||
| 246 | $min = $param{next}->( DateTime::Infinite::Past->new ); | ||||
| 247 | $max = INFINITY if $max->is_infinite; | ||||
| 248 | $min = NEG_INFINITY if $min->is_infinite; | ||||
| 249 | |||||
| 250 | my $base_set = Set::Infinite::_recurrence->new( $min, $max ); | ||||
| 251 | $base_set = $base_set->intersection( $param{span}->{set} ) | ||||
| 252 | if $param{span}; | ||||
| 253 | |||||
| 254 | # warn "base set is $base_set\n"; | ||||
| 255 | |||||
| 256 | my $data = {}; | ||||
| 257 | $self->{set} = | ||||
| 258 | $base_set->_recurrence( | ||||
| 259 | $param{next}, | ||||
| 260 | $param{previous}, | ||||
| 261 | $data, | ||||
| 262 | ); | ||||
| 263 | bless $self, $class; | ||||
| 264 | |||||
| 265 | return $self; | ||||
| 266 | } | ||||
| 267 | |||||
| 268 | sub from_datetimes { | ||||
| 269 | my $class = shift; | ||||
| 270 | my %args = validate( @_, | ||||
| 271 | { dates => | ||||
| 272 | { type => ARRAYREF, | ||||
| 273 | }, | ||||
| 274 | } | ||||
| 275 | ); | ||||
| 276 | my $self = {}; | ||||
| 277 | $self->{set} = Set::Infinite::_recurrence->new; | ||||
| 278 | # possible optimization: sort datetimes and use "push" | ||||
| 279 | for( @{ $args{dates} } ) | ||||
| 280 | { | ||||
| 281 | # DateTime::Infinite objects are not welcome here, | ||||
| 282 | # but this is not enforced (it does't hurt) | ||||
| 283 | |||||
| 284 | carp "The 'dates' argument to from_datetimes() must only contain ". | ||||
| 285 | "datetime objects" | ||||
| 286 | unless UNIVERSAL::can( $_, 'utc_rd_values' ); | ||||
| 287 | |||||
| 288 | $self->{set} = $self->{set}->union( $_->clone ); | ||||
| 289 | } | ||||
| 290 | |||||
| 291 | bless $self, $class; | ||||
| 292 | return $self; | ||||
| 293 | } | ||||
| 294 | |||||
| 295 | sub empty_set { | ||||
| 296 | my $class = shift; | ||||
| 297 | |||||
| 298 | return bless { set => Set::Infinite::_recurrence->new }, $class; | ||||
| 299 | } | ||||
| 300 | |||||
| 301 | sub clone { | ||||
| 302 | my $self = bless { %{ $_[0] } }, ref $_[0]; | ||||
| 303 | $self->{set} = $_[0]->{set}->copy; | ||||
| 304 | return $self; | ||||
| 305 | } | ||||
| 306 | |||||
| 307 | # default callback that returns the | ||||
| 308 | # "previous" value in a callback recurrence. | ||||
| 309 | # | ||||
| 310 | # This is used to simulate a 'previous' callback, | ||||
| 311 | # when then 'previous' argument in 'from_recurrence' is missing. | ||||
| 312 | # | ||||
| 313 | sub _callback_previous { | ||||
| 314 | my ($value, $callback_next, $callback_info) = @_; | ||||
| 315 | my $previous = $value->clone; | ||||
| 316 | |||||
| 317 | return $value if $value->is_infinite; | ||||
| 318 | |||||
| 319 | my $freq = $callback_info->{freq}; | ||||
| 320 | unless (defined $freq) | ||||
| 321 | { | ||||
| 322 | # This is called just once, to setup the recurrence frequency | ||||
| 323 | my $previous = $callback_next->( $value ); | ||||
| 324 | my $next = $callback_next->( $previous ); | ||||
| 325 | $freq = 2 * ( $previous - $next ); | ||||
| 326 | # save it for future use with this same recurrence | ||||
| 327 | $callback_info->{freq} = $freq; | ||||
| 328 | } | ||||
| 329 | |||||
| 330 | $previous->add_duration( $freq ); | ||||
| 331 | $previous = $callback_next->( $previous ); | ||||
| 332 | if ($previous >= $value) | ||||
| 333 | { | ||||
| 334 | # This error happens if the event frequency oscilates widely | ||||
| 335 | # (more than 100% of difference from one interval to next) | ||||
| 336 | my @freq = $freq->deltas; | ||||
| 337 | print STDERR "_callback_previous: Delta components are: @freq\n"; | ||||
| 338 | warn "_callback_previous: iterator can't find a previous value, got ". | ||||
| 339 | $previous->ymd." after ".$value->ymd; | ||||
| 340 | } | ||||
| 341 | my $previous1; | ||||
| 342 | while (1) | ||||
| 343 | { | ||||
| 344 | $previous1 = $previous->clone; | ||||
| 345 | $previous = $callback_next->( $previous ); | ||||
| 346 | return $previous1 if $previous >= $value; | ||||
| 347 | } | ||||
| 348 | } | ||||
| 349 | |||||
| 350 | # default callback that returns the | ||||
| 351 | # "next" value in a callback recurrence. | ||||
| 352 | # | ||||
| 353 | # This is used to simulate a 'next' callback, | ||||
| 354 | # when then 'next' argument in 'from_recurrence' is missing. | ||||
| 355 | # | ||||
| 356 | sub _callback_next { | ||||
| 357 | my ($value, $callback_previous, $callback_info) = @_; | ||||
| 358 | my $next = $value->clone; | ||||
| 359 | |||||
| 360 | return $value if $value->is_infinite; | ||||
| 361 | |||||
| 362 | my $freq = $callback_info->{freq}; | ||||
| 363 | unless (defined $freq) | ||||
| 364 | { | ||||
| 365 | # This is called just once, to setup the recurrence frequency | ||||
| 366 | my $next = $callback_previous->( $value ); | ||||
| 367 | my $previous = $callback_previous->( $next ); | ||||
| 368 | $freq = 2 * ( $next - $previous ); | ||||
| 369 | # save it for future use with this same recurrence | ||||
| 370 | $callback_info->{freq} = $freq; | ||||
| 371 | } | ||||
| 372 | |||||
| 373 | $next->add_duration( $freq ); | ||||
| 374 | $next = $callback_previous->( $next ); | ||||
| 375 | if ($next <= $value) | ||||
| 376 | { | ||||
| 377 | # This error happens if the event frequency oscilates widely | ||||
| 378 | # (more than 100% of difference from one interval to next) | ||||
| 379 | my @freq = $freq->deltas; | ||||
| 380 | print STDERR "_callback_next: Delta components are: @freq\n"; | ||||
| 381 | warn "_callback_next: iterator can't find a previous value, got ". | ||||
| 382 | $next->ymd." before ".$value->ymd; | ||||
| 383 | } | ||||
| 384 | my $next1; | ||||
| 385 | while (1) | ||||
| 386 | { | ||||
| 387 | $next1 = $next->clone; | ||||
| 388 | $next = $callback_previous->( $next ); | ||||
| 389 | return $next1 if $next >= $value; | ||||
| 390 | } | ||||
| 391 | } | ||||
| 392 | |||||
| 393 | sub iterator { | ||||
| 394 | my $self = shift; | ||||
| 395 | |||||
| 396 | my %args = @_; | ||||
| 397 | my $span; | ||||
| 398 | $span = delete $args{span}; | ||||
| 399 | $span = DateTime::Span->new( %args ) if %args; | ||||
| 400 | |||||
| 401 | return $self->intersection( $span ) if $span; | ||||
| 402 | return $self->clone; | ||||
| 403 | } | ||||
| 404 | |||||
| 405 | |||||
| 406 | # next() gets the next element from an iterator() | ||||
| 407 | # next( $dt ) returns the next element after a datetime. | ||||
| 408 | sub next { | ||||
| 409 | my $self = shift; | ||||
| 410 | return undef unless ref( $self->{set} ); | ||||
| 411 | |||||
| 412 | if ( @_ ) | ||||
| 413 | { | ||||
| 414 | if ( $self->{set}->_is_recurrence ) | ||||
| 415 | { | ||||
| 416 | return _fix_return_datetime( | ||||
| 417 | $self->{set}->{param}[0]->( $_[0] ), $_[0] ); | ||||
| 418 | } | ||||
| 419 | else | ||||
| 420 | { | ||||
| 421 | my $span = DateTime::Span->from_datetimes( after => $_[0] ); | ||||
| 422 | return _fix_return_datetime( | ||||
| 423 | $self->intersection( $span )->next, $_[0] ); | ||||
| 424 | } | ||||
| 425 | } | ||||
| 426 | |||||
| 427 | my ($head, $tail) = $self->{set}->first; | ||||
| 428 | $self->{set} = $tail; | ||||
| 429 | return $head->min if defined $head; | ||||
| 430 | return $head; | ||||
| 431 | } | ||||
| 432 | |||||
| 433 | # previous() gets the last element from an iterator() | ||||
| 434 | # previous( $dt ) returns the previous element before a datetime. | ||||
| 435 | sub previous { | ||||
| 436 | my $self = shift; | ||||
| 437 | return undef unless ref( $self->{set} ); | ||||
| 438 | |||||
| 439 | if ( @_ ) | ||||
| 440 | { | ||||
| 441 | if ( $self->{set}->_is_recurrence ) | ||||
| 442 | { | ||||
| 443 | return _fix_return_datetime( | ||||
| 444 | $self->{set}->{param}[1]->( $_[0] ), $_[0] ); | ||||
| 445 | } | ||||
| 446 | else | ||||
| 447 | { | ||||
| 448 | my $span = DateTime::Span->from_datetimes( before => $_[0] ); | ||||
| 449 | return _fix_return_datetime( | ||||
| 450 | $self->intersection( $span )->previous, $_[0] ); | ||||
| 451 | } | ||||
| 452 | } | ||||
| 453 | |||||
| 454 | my ($head, $tail) = $self->{set}->last; | ||||
| 455 | $self->{set} = $tail; | ||||
| 456 | return $head->max if defined $head; | ||||
| 457 | return $head; | ||||
| 458 | } | ||||
| 459 | |||||
| 460 | # "current" means less-or-equal to a datetime | ||||
| 461 | sub current { | ||||
| 462 | my $self = shift; | ||||
| 463 | |||||
| 464 | return undef unless ref( $self->{set} ); | ||||
| 465 | |||||
| 466 | if ( $self->{set}->_is_recurrence ) | ||||
| 467 | { | ||||
| 468 | my $tmp = $self->next( $_[0] ); | ||||
| 469 | return $self->previous( $tmp ); | ||||
| 470 | } | ||||
| 471 | |||||
| 472 | return $_[0] if $self->contains( $_[0] ); | ||||
| 473 | $self->previous( $_[0] ); | ||||
| 474 | } | ||||
| 475 | |||||
| 476 | sub closest { | ||||
| 477 | my $self = shift; | ||||
| 478 | # return $_[0] if $self->contains( $_[0] ); | ||||
| 479 | my $dt1 = $self->current( $_[0] ); | ||||
| 480 | my $dt2 = $self->next( $_[0] ); | ||||
| 481 | |||||
| 482 | return $dt2 unless defined $dt1; | ||||
| 483 | return $dt1 unless defined $dt2; | ||||
| 484 | |||||
| 485 | my $delta = $_[0] - $dt1; | ||||
| 486 | return $dt1 if ( $dt2 - $delta ) >= $_[0]; | ||||
| 487 | |||||
| 488 | return $dt2; | ||||
| 489 | } | ||||
| 490 | |||||
| 491 | sub as_list { | ||||
| 492 | my $self = shift; | ||||
| 493 | return undef unless ref( $self->{set} ); | ||||
| 494 | |||||
| 495 | my %args = @_; | ||||
| 496 | my $span; | ||||
| 497 | $span = delete $args{span}; | ||||
| 498 | $span = DateTime::Span->new( %args ) if %args; | ||||
| 499 | |||||
| 500 | my $set = $self->clone; | ||||
| 501 | $set = $set->intersection( $span ) if $span; | ||||
| 502 | |||||
| 503 | return if $set->{set}->is_null; # nothing = empty | ||||
| 504 | |||||
| 505 | # Note: removing this line means we may end up in an infinite loop! | ||||
| 506 | ## return undef if $set->{set}->is_too_complex; # undef = no begin/end | ||||
| 507 | |||||
| 508 | return undef | ||||
| 509 | if $set->max->is_infinite || | ||||
| 510 | $set->min->is_infinite; | ||||
| 511 | |||||
| 512 | my @result; | ||||
| 513 | my $next = $self->min; | ||||
| 514 | if ( $span ) { | ||||
| 515 | my $next1 = $span->min; | ||||
| 516 | $next = $next1 if $next1 && $next1 > $next; | ||||
| 517 | $next = $self->current( $next ); | ||||
| 518 | } | ||||
| 519 | my $last = $self->max; | ||||
| 520 | if ( $span ) { | ||||
| 521 | my $last1 = $span->max; | ||||
| 522 | $last = $last1 if $last1 && $last1 < $last; | ||||
| 523 | } | ||||
| 524 | do { | ||||
| 525 | push @result, $next if !$span || $span->contains($next); | ||||
| 526 | $next = $self->next( $next ); | ||||
| 527 | } | ||||
| 528 | while $next && $next <= $last; | ||||
| 529 | return @result; | ||||
| 530 | } | ||||
| 531 | |||||
| 532 | sub intersection { | ||||
| 533 | my ($set1, $set2) = ( shift, shift ); | ||||
| 534 | my $class = ref($set1); | ||||
| 535 | my $tmp = $class->empty_set(); | ||||
| 536 | $set2 = $set2->as_set | ||||
| 537 | if $set2->can( 'as_set' ); | ||||
| 538 | $set2 = $class->from_datetimes( dates => [ $set2, @_ ] ) | ||||
| 539 | unless $set2->can( 'union' ); | ||||
| 540 | $tmp->{set} = $set1->{set}->intersection( $set2->{set} ); | ||||
| 541 | return $tmp; | ||||
| 542 | } | ||||
| 543 | |||||
| 544 | sub intersects { | ||||
| 545 | my ($set1, $set2) = ( shift, shift ); | ||||
| 546 | my $class = ref($set1); | ||||
| 547 | $set2 = $set2->as_set | ||||
| 548 | if $set2->can( 'as_set' ); | ||||
| 549 | unless ( $set2->can( 'union' ) ) | ||||
| 550 | { | ||||
| 551 | if ( $set1->{set}->_is_recurrence ) | ||||
| 552 | { | ||||
| 553 | for ( $set2, @_ ) | ||||
| 554 | { | ||||
| 555 | return 1 if $set1->current( $_ ) == $_; | ||||
| 556 | } | ||||
| 557 | return 0; | ||||
| 558 | } | ||||
| 559 | $set2 = $class->from_datetimes( dates => [ $set2, @_ ] ) | ||||
| 560 | } | ||||
| 561 | return $set1->{set}->intersects( $set2->{set} ); | ||||
| 562 | } | ||||
| 563 | |||||
| 564 | sub contains { | ||||
| 565 | my ($set1, $set2) = ( shift, shift ); | ||||
| 566 | my $class = ref($set1); | ||||
| 567 | $set2 = $set2->as_set | ||||
| 568 | if $set2->can( 'as_set' ); | ||||
| 569 | unless ( $set2->can( 'union' ) ) | ||||
| 570 | { | ||||
| 571 | if ( $set1->{set}->_is_recurrence ) | ||||
| 572 | { | ||||
| 573 | for ( $set2, @_ ) | ||||
| 574 | { | ||||
| 575 | return 0 unless $set1->current( $_ ) == $_; | ||||
| 576 | } | ||||
| 577 | return 1; | ||||
| 578 | } | ||||
| 579 | $set2 = $class->from_datetimes( dates => [ $set2, @_ ] ) | ||||
| 580 | } | ||||
| 581 | return $set1->{set}->contains( $set2->{set} ); | ||||
| 582 | } | ||||
| 583 | |||||
| 584 | sub union { | ||||
| 585 | my ($set1, $set2) = ( shift, shift ); | ||||
| 586 | my $class = ref($set1); | ||||
| 587 | my $tmp = $class->empty_set(); | ||||
| 588 | $set2 = $set2->as_set | ||||
| 589 | if $set2->can( 'as_set' ); | ||||
| 590 | $set2 = $class->from_datetimes( dates => [ $set2, @_ ] ) | ||||
| 591 | unless $set2->can( 'union' ); | ||||
| 592 | $tmp->{set} = $set1->{set}->union( $set2->{set} ); | ||||
| 593 | bless $tmp, 'DateTime::SpanSet' | ||||
| 594 | if $set2->isa('DateTime::Span') or $set2->isa('DateTime::SpanSet'); | ||||
| 595 | return $tmp; | ||||
| 596 | } | ||||
| 597 | |||||
| 598 | sub complement { | ||||
| 599 | my ($set1, $set2) = ( shift, shift ); | ||||
| 600 | my $class = ref($set1); | ||||
| 601 | my $tmp = $class->empty_set(); | ||||
| 602 | if (defined $set2) | ||||
| 603 | { | ||||
| 604 | $set2 = $set2->as_set | ||||
| 605 | if $set2->can( 'as_set' ); | ||||
| 606 | $set2 = $class->from_datetimes( dates => [ $set2, @_ ] ) | ||||
| 607 | unless $set2->can( 'union' ); | ||||
| 608 | # TODO: "compose complement"; | ||||
| 609 | $tmp->{set} = $set1->{set}->complement( $set2->{set} ); | ||||
| 610 | } | ||||
| 611 | else | ||||
| 612 | { | ||||
| 613 | $tmp->{set} = $set1->{set}->complement; | ||||
| 614 | bless $tmp, 'DateTime::SpanSet'; | ||||
| 615 | } | ||||
| 616 | return $tmp; | ||||
| 617 | } | ||||
| 618 | |||||
| 619 | sub min { | ||||
| 620 | return _fix_datetime( $_[0]->{set}->min ); | ||||
| 621 | } | ||||
| 622 | |||||
| 623 | sub max { | ||||
| 624 | return _fix_datetime( $_[0]->{set}->max ); | ||||
| 625 | } | ||||
| 626 | |||||
| 627 | # returns a DateTime::Span | ||||
| 628 | sub span { | ||||
| 629 | my $set = $_[0]->{set}->span; | ||||
| 630 | my $self = bless { set => $set }, 'DateTime::Span'; | ||||
| 631 | return $self; | ||||
| 632 | } | ||||
| 633 | |||||
| 634 | sub count { | ||||
| 635 | my ($self) = shift; | ||||
| 636 | return undef unless ref( $self->{set} ); | ||||
| 637 | |||||
| 638 | my %args = @_; | ||||
| 639 | my $span; | ||||
| 640 | $span = delete $args{span}; | ||||
| 641 | $span = DateTime::Span->new( %args ) if %args; | ||||
| 642 | |||||
| 643 | my $set = $self->clone; | ||||
| 644 | $set = $set->intersection( $span ) if $span; | ||||
| 645 | |||||
| 646 | return $set->{set}->count | ||||
| 647 | unless $set->{set}->is_too_complex; | ||||
| 648 | |||||
| 649 | return undef | ||||
| 650 | if $set->max->is_infinite || | ||||
| 651 | $set->min->is_infinite; | ||||
| 652 | |||||
| 653 | my $count = 0; | ||||
| 654 | my $iter = $set->iterator; | ||||
| 655 | $count++ while $iter->next; | ||||
| 656 | return $count; | ||||
| 657 | } | ||||
| 658 | |||||
| 659 | 1 | 7µs | 1; | ||
| 660 | |||||
| 661 | __END__ |