Filename | /usr/share/perl5/DateTime/Set.pm |
Statements | Executed 35 statements in 4.52ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 7.96ms | 18.1ms | BEGIN@10 | DateTime::Set::
1 | 1 | 1 | 1.68ms | 5.39ms | BEGIN@9 | DateTime::Set::
1 | 1 | 1 | 1.49ms | 1.84ms | BEGIN@11 | DateTime::Set::
1 | 1 | 1 | 22µs | 26µs | BEGIN@4 | DateTime::Set::
1 | 1 | 1 | 17µs | 36µs | BEGIN@7 | DateTime::Set::
1 | 1 | 1 | 16µs | 85µs | BEGIN@6 | DateTime::Set::
1 | 1 | 1 | 14µs | 15µs | BEGIN@8 | DateTime::Set::
1 | 1 | 1 | 12µs | 76µs | BEGIN@16 | DateTime::Set::
1 | 1 | 1 | 10µs | 37µs | BEGIN@13 | DateTime::Set::
1 | 1 | 1 | 10µs | 67µs | BEGIN@15 | DateTime::Set::
1 | 1 | 1 | 9µs | 51µs | BEGIN@5 | DateTime::Set::
1 | 1 | 1 | 7µs | 7µs | BEGIN@18 | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:110] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:129] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:143] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:155] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:169] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:185] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:216] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:224] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:233] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:241] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:71] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | __ANON__[:91] | DateTime::Set::
0 | 0 | 0 | 0s | 0s | _callback_next | DateTime::Set::
0 | 0 | 0 | 0s | 0s | _callback_previous | DateTime::Set::
0 | 0 | 0 | 0s | 0s | _fix_datetime | DateTime::Set::
0 | 0 | 0 | 0s | 0s | _fix_return_datetime | DateTime::Set::
0 | 0 | 0 | 0s | 0s | add | DateTime::Set::
0 | 0 | 0 | 0s | 0s | add_duration | DateTime::Set::
0 | 0 | 0 | 0s | 0s | as_list | DateTime::Set::
0 | 0 | 0 | 0s | 0s | clone | DateTime::Set::
0 | 0 | 0 | 0s | 0s | closest | DateTime::Set::
0 | 0 | 0 | 0s | 0s | complement | DateTime::Set::
0 | 0 | 0 | 0s | 0s | contains | DateTime::Set::
0 | 0 | 0 | 0s | 0s | count | DateTime::Set::
0 | 0 | 0 | 0s | 0s | current | DateTime::Set::
0 | 0 | 0 | 0s | 0s | empty_set | DateTime::Set::
0 | 0 | 0 | 0s | 0s | from_datetimes | DateTime::Set::
0 | 0 | 0 | 0s | 0s | from_recurrence | DateTime::Set::
0 | 0 | 0 | 0s | 0s | grep | DateTime::Set::
0 | 0 | 0 | 0s | 0s | intersection | DateTime::Set::
0 | 0 | 0 | 0s | 0s | intersects | DateTime::Set::
0 | 0 | 0 | 0s | 0s | iterate | DateTime::Set::
0 | 0 | 0 | 0s | 0s | iterator | DateTime::Set::
0 | 0 | 0 | 0s | 0s | map | DateTime::Set::
0 | 0 | 0 | 0s | 0s | max | DateTime::Set::
0 | 0 | 0 | 0s | 0s | min | DateTime::Set::
0 | 0 | 0 | 0s | 0s | next | DateTime::Set::
0 | 0 | 0 | 0s | 0s | previous | DateTime::Set::
0 | 0 | 0 | 0s | 0s | set | DateTime::Set::
0 | 0 | 0 | 0s | 0s | set_time_zone | DateTime::Set::
0 | 0 | 0 | 0s | 0s | span | DateTime::Set::
0 | 0 | 0 | 0s | 0s | subtract | DateTime::Set::
0 | 0 | 0 | 0s | 0s | subtract_duration | DateTime::Set::
0 | 0 | 0 | 0s | 0s | union | DateTime::Set::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | |||||
2 | package DateTime::Set; | ||||
3 | |||||
4 | 3 | 26µs | 2 | 31µs | # spent 26µs (22+4) within DateTime::Set::BEGIN@4 which was called:
# once (22µs+4µs) by Koha::Calendar::BEGIN@7 at line 4 # spent 26µs making 1 call to DateTime::Set::BEGIN@4
# spent 4µs making 1 call to strict::import |
5 | 3 | 32µs | 2 | 93µ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 | 42µs | 2 | 153µs | # spent 85µs (16+68) within DateTime::Set::BEGIN@6 which was called:
# once (16µs+68µs) by Koha::Calendar::BEGIN@7 at line 6 # spent 85µs making 1 call to DateTime::Set::BEGIN@6
# spent 68µs making 1 call to Exporter::import |
7 | 3 | 53µs | 3 | 55µs | # spent 36µs (17+19) within DateTime::Set::BEGIN@7 which was called:
# once (17µs+19µs) by Koha::Calendar::BEGIN@7 at line 7 # spent 36µs making 1 call to DateTime::Set::BEGIN@7
# spent 17µs making 1 call to UNIVERSAL::VERSION
# spent 2µs making 1 call to UNIVERSAL::import |
8 | 3 | 27µs | 2 | 17µs | # spent 15µs (14+2) within DateTime::Set::BEGIN@8 which was called:
# once (14µs+2µs) by Koha::Calendar::BEGIN@7 at line 8 # spent 15µs making 1 call to DateTime::Set::BEGIN@8
# spent 2µs making 1 call to UNIVERSAL::import |
9 | 3 | 140µs | 2 | 5.40ms | # spent 5.39ms (1.68+3.71) within DateTime::Set::BEGIN@9 which was called:
# once (1.68ms+3.71ms) by Koha::Calendar::BEGIN@7 at line 9 # spent 5.39ms making 1 call to DateTime::Set::BEGIN@9
# spent 3µs making 1 call to UNIVERSAL::import |
10 | 3 | 212µs | 3 | 18.2ms | # spent 18.1ms (7.96+10.1) within DateTime::Set::BEGIN@10 which was called:
# once (7.96ms+10.1ms) by Koha::Calendar::BEGIN@7 at line 10 # spent 18.1ms making 1 call to DateTime::Set::BEGIN@10
# spent 56µs making 1 call to UNIVERSAL::VERSION
# spent 41µs making 1 call to Exporter::import |
11 | 3 | 203µs | 2 | 1.87ms | # spent 1.84ms (1.49+346µs) within DateTime::Set::BEGIN@11 which was called:
# once (1.49ms+346µs) by Koha::Calendar::BEGIN@7 at line 11 # spent 1.84ms making 1 call to DateTime::Set::BEGIN@11
# spent 35µs making 1 call to Exporter::import |
12 | |||||
13 | 3 | 39µs | 2 | 63µs | # spent 37µs (10+26) within DateTime::Set::BEGIN@13 which was called:
# once (10µs+26µs) by Koha::Calendar::BEGIN@7 at line 13 # spent 37µs making 1 call to DateTime::Set::BEGIN@13
# spent 26µs making 1 call to vars::import |
14 | |||||
15 | 3 | 45µs | 2 | 123µs | # spent 67µs (10+56) within DateTime::Set::BEGIN@15 which was called:
# once (10µs+56µs) by Koha::Calendar::BEGIN@7 at line 15 # spent 67µs making 1 call to DateTime::Set::BEGIN@15
# spent 56µs making 1 call to constant::import |
16 | 3 | 45µs | 2 | 139µs | # spent 76µs (12+63) within DateTime::Set::BEGIN@16 which was called:
# once (12µs+63µs) by Koha::Calendar::BEGIN@7 at line 16 # spent 76µs making 1 call to DateTime::Set::BEGIN@16
# spent 63µs making 1 call to constant::import |
17 | |||||
18 | # spent 7µs within DateTime::Set::BEGIN@18 which was called:
# once (7µs+0s) by Koha::Calendar::BEGIN@7 at line 20 | ||||
19 | 1 | 8µs | $VERSION = '0.28'; | ||
20 | 1 | 3.64ms | 1 | 7µs | } # spent 7µ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 | 6µs | 1; | ||
660 | |||||
661 | __END__ |