File Coverage

blib/lib/Data/ICal/DateTime.pm
Criterion Covered Total %
statement 243 290 83.7
branch 66 86 76.7
condition 33 50 66.0
subroutine 31 34 91.1
pod 20 20 100.0
total 393 480 81.8


line stmt bran cond sub pod time code
1             package Data::ICal::DateTime;
2              
3 8     8   395954 use strict;
  8         21  
  8         362  
4 8     8   9254 use Clone;
  8         51729  
  8         1069  
5 8     8   10444 use Data::ICal;
  8         558556  
  8         204  
6 8     8   12492 use DateTime::Set;
  8         3395253  
  8         327  
7 8     8   12166 use DateTime::Format::ICal;
  8         196918  
  8         563  
8              
9             our $VERSION = '0.7';
10              
11             # mmm, mixin goodness
12             sub import {
13 8     8   137 my $class = shift;
14 8     8   99 no strict 'refs';
  8         20  
  8         272  
15 8     8   47 no warnings 'redefine';
  8         16  
  8         47003  
16 8         42 *Data::ICal::events = \&events;
17 8         28 *Data::ICal::collapse = \&collapse;
18 8         40 foreach my $sub (qw(start end duration period summary description original
19             all_day floating recurrence recurrence_id rdate exrule exdate uid
20             _rule_set _date_set explode is_in _normalise split_up _escape _unescape))
21             {
22 184         394 *{"Data::ICal::Entry::Event::$sub"} = \&$sub;
  184         919  
23             }
24 8         325 push @Data::ICal::Entry::Event::ISA, 'Clone';
25             }
26              
27              
28              
29             =head1 NAME
30              
31             Data::ICal::DateTime - convenience methods for using Data::ICal with DateTime
32              
33             =head1 SYNPOSIS
34              
35             # performs mixin voodoo
36             use Data::ICal::DateTime;
37             my $cal = Data::ICal->new( filename => 'example.ics');
38              
39              
40             my $date1 = DateTime->new( year => 2005, month => 7, day => 01 );
41             my $date2 = DateTime->new( year => 2005, month => 7, day => 07 );
42             my $span = DateTime::Span->from_datetimes( start => $date1, end => $date2 );
43              
44             my @events = $cal->events(); # all VEVENTS
45             my @week = $cal->events($span); # just in that week
46             my @week = $cal->events($span,'day');# explode long events into days
47              
48             my $event = Data::ICal::Entry::Event->new();
49            
50             $event->start($start); # $start is a DateTime object
51             $event->end($end); # so is $end
52              
53             $event->all_day # is this an all day event
54              
55             $event->duration($duration); # $duration is DateTime::Duration
56             $event->recurrence($recurrence); # $reccurence is a DateTime list,
57             # a DateTime::Span list,
58             # a DateTime::Set,
59             # or a DateTime::SpanSet
60            
61             $event->start; # returns a DateTime object
62             $event->end; # ditto
63             $event->duration; # returns a DateTime::Duration
64             $event->recurrence; # returns a DateTime::Set
65             $event->period; # returns a DateTime::Span object
66             $event->rdate; # returns a DateTime::Set
67             $event->exrule; # returns a DateTime::Set
68             $event->exdate; # returns a DateTime::Set
69             $event->explode($span); # returns an array of sub events
70             # (if this is recurring);
71             $event->explode($span,'week'); # if any events are longer than a
72             # week then split them up
73             $event->is_in($span); # whether this event falls within a
74             # Set, Span, or SetSpan
75              
76              
77             $cal->add($event);
78              
79             methods
80              
81              
82             =head1 DESCRIPTION
83              
84             =head1 METHODS
85              
86             =cut
87              
88             =head2 events [span] [period]
89              
90             Provides a L object with a method to return all events.
91              
92             If a L, L or L object
93             is passed then only the events that occur within that set will be
94             returned including expansion of all recurring events. All events will be
95             normalised to have a dtstart and dtend rather than any other method of
96             determining their start and stop time.
97              
98             Additionally you can pass a period string which can be one of the
99             following
100              
101             year month week day hour minute second
102              
103             This will explode an event into as many sub events as needed e.g a
104             period of 'day' will explode a 2-day event into 2 one day events with
105             the second starting just after the first
106              
107             =cut
108              
109             sub events {
110 13     13 1 786640 my $self = shift;
111 13         29 my $set = shift;
112 13         28 my $period = shift;
113              
114              
115 13         27 my @events = grep { $_->ical_entry_type eq 'VEVENT' } @{$self->entries};
  402         1768  
  13         108  
116              
117             # NOTE: this won't normalise events
118 13 100       344 return @events if (!$set);
119 8         25 @events = map { $_->explode($set) } @events;
  264         772  
120 8         51 @events = $self->collapse(@events);
121              
122 8 100       68 return @events unless defined $period;
123 2         6 return map { $_->split_up($period) } @events;
  22         103  
124              
125             }
126              
127             =head2 collapse
128              
129             Provides a L object with a method to collapse Cs.
130              
131             Given a list of events, some of which might have Cs,
132             return a list of events with all recurrences within C and all
133             Cs handled correctly.
134              
135             Used internally by C.
136              
137             =cut
138              
139             sub collapse {
140 8     8 1 26 my ($self, @events) = @_;
141              
142 8         15 my %rid;
143              
144             my @recurs;
145 8         27 for (@events) {
146 55         143 my $uid = $_->uid;
147             # TODO: this feels very hacky
148 55 50       523 $uid = rand().{}.time unless defined $uid;
149 55         123 $_->uid($uid);
150 55 100       510 if ($_->recurrence_id) {
151 1         46 push @recurs, $_;
152             } else {
153 54         467 push @{$rid{$uid}}, $_;
  54         206  
154             }
155             }
156              
157 8         25 foreach my $e (@recurs) {
158 1         4 my $uid = $e->uid;
159 1         12 for (@{$rid{$uid}}) {
  1         5  
160 4 100       121 next unless $_->start == $e->recurrence_id;
161             # TODO: does this need to merge fields?
162 1         73 $_ = $e;
163             }
164             }
165 8         144 @events = ();
166 8         33 push @events, @{$rid{$_}} for keys %rid;
  26         62  
167 8         90 return @events;
168              
169              
170             }
171              
172              
173             =head2 start [new]
174              
175             Returns a L object representing the start time of this event.
176              
177             May return undef.
178              
179             If passed a L object will set that to be the new start time.
180              
181             =cut
182            
183             sub start {
184 1797     1797 1 13887 my $self = shift;
185 1797         2144 my $new = shift;
186              
187 1797 100       4067 if ($new) {
188 540         23791 delete $self->{properties}->{dtstart};
189 540         2692 $self->add_property(dtstart => DateTime::Format::ICal->format_datetime($new));
190             }
191              
192              
193 1797   50     125083 my $dtstart = $self->property('dtstart') || return undef;
194 1797         17002 my $ret = DateTime::Format::ICal->parse_datetime($dtstart->[0]->value);
195              
196             # $ret->set_time_zone($dtstart->[0]->parameters->{TZID}) if $dtstart->[0]->parameters->{TZID};
197              
198 1797         498562 return $ret;
199              
200             }
201              
202             =head2 end
203              
204             Returns a L object representing the end time of this event.
205              
206             May return undef.
207              
208             If passed a L object will set that to be the new end time.
209              
210             =cut
211              
212              
213             sub end {
214 1632     1632 1 9383 my $self = shift;
215 1632         2747 my $new = shift;
216              
217             # iCal represents all-day events by using ;VALUE=DATE
218             # and setting DTEND=end_date + 1
219 1632         3626 my $all_day = $self->all_day;
220              
221 1632 100       4021 if ($new) {
222 609         26083 delete $self->{properties}->{dtend};
223 609         1819 my $update = $new->clone;
224 609 100       11310 if ($all_day) {
225 14         67 $update->add( days => 1);
226 14         8787 $update->set( hour => 0, minute => 0, second => 0 );
227             }
228 609         10512 $self->add_property( dtend => DateTime::Format::ICal->format_datetime($update) );
229 609 100       148870 $self->property('dtend')->[0]->parameters->{VALUE} = 'DATE' if $all_day;
230              
231             }
232              
233              
234 1632   100     6274 my $dtend = $self->property('dtend') || return undef;
235 1464         11641 my $ret = DateTime::Format::ICal->parse_datetime($dtend->[0]->value);
236              
237             # $ret->set_time_zone($dtend->[0]->parameters->{TZID}) if ($dtend->[0]->parameters->{TZID});
238 1464 100       444386 $ret->truncate(to => 'day' )->subtract( nanoseconds => 1 ) if $all_day;
239              
240 1464         376218 return $ret;
241             }
242              
243             =head2 all_day
244              
245             Returns 1 if event is all day or 0 if not.
246              
247             If no end has been set and 1 is passed then will set end to be a
248             nanosecond before midnight the next day.
249              
250             =cut
251              
252             sub all_day {
253 2208     2208 1 10356 my $self = shift;
254 2208         2676 my $new = shift;
255              
256             # TODO - should be able to make all day with just the start
257 2208         6456 my $dtend = $self->property('dtend');
258              
259 2208 100       14752 if (!$dtend) {
260 213 100       688 return 0 unless $new;
261 1         6 $dtend = $self->start->clone->add( days => 1 )->truncate(to => 'day' )->subtract( nanoseconds => 1 );
262 1         6339 $self->end($dtend);
263 1         6 $dtend = $self->property('dtend');
264             }
265            
266 1996   100     16348 my $cur = (defined $dtend && defined $dtend->[0]->parameters->{VALUE} && $dtend->[0]->parameters->{VALUE} eq 'DATE') || 0;
267              
268 1996 100 100     46651 if (defined $new && $new != $cur) {
269 41         120 my $end = $self->end;
270 41 100       167 if ($new == 0) {
271 39         197 delete $self->property('dtend')->[0]->parameters->{VALUE};
272             } else {
273 2         12 $self->property('dtend')->[0]->parameters->{VALUE} = 'DATE';
274             }
275 41         962 $self->end($end);
276 41         219 $cur = $new;
277             }
278              
279 1996         5917 return $cur;
280             }
281              
282             =head2 floating
283              
284             An event is considered floating if it has a start but no end. It is intended
285             to represent an event that is associated with a given calendar date and time
286             of day, such as an anniversary and should not be considered as taking up any
287             amount of time.
288              
289             Returns 1 if the evnt is floating and 0 if it isn't.
290              
291             If passed a 1 then will set the event to be floating by deleting the end time.
292              
293             If passed a 0 and no end is currently set then it will set end to be a
294             nanosecond before midnight the next day.
295              
296             =cut
297              
298             sub floating {
299 34     34 1 11082 my $self = shift;
300 34         62 my $new = shift;
301              
302 34         104 my $end = $self->end;
303 34 100       457 my $cur = (defined $end)? 0 : 1;
304 34 100 66     176 if (defined $new && $new != $cur) {
305             # it is floating - delete the end
306 4 100       15 if ($new) {
307 2         13 delete $self->{properties}->{dtend};
308             # it's not floating - simulate end as 1 nanosecond before midnight after the start
309             } else {
310 2         8 my $dtend = $self->start->clone->add( days => 1 )->truncate(to => 'day' )->subtract( nanoseconds => 1 );
311 2         3462 $self->end($dtend);
312             }
313 4         8 $cur = $new;
314             }
315              
316 34         229 return $cur;
317              
318             }
319              
320             =head2 duration
321              
322             Returns a L object representing the duration of this
323             event.
324              
325             May return undef.
326              
327             If passed a L object will set that to be the new
328             duration.
329              
330             =cut
331              
332             sub duration {
333 394     394 1 2659 my $self = shift;
334 394         461 my $new = shift;
335              
336 394 50       837 if ($new) {
337 0         0 delete $self->{properties}->{duration};
338 0         0 $self->add_property( duration => DateTime::Format::ICal->format_duration($new) );
339             }
340              
341 394   100     1150 my $duration = $self->property('duration') || return undef;
342 155         1348 return DateTime::Format::ICal->parse_duration($duration->[0]->value);
343             }
344              
345              
346             =head2 period
347              
348             Returns a L object representing the period of this
349             event.
350              
351             May return undef.
352              
353             If passed a L object will set that to be the new
354             period.
355              
356             =cut
357              
358             sub period {
359 392     392 1 487 my $self = shift;
360 392         506 my $new = shift;
361              
362 392 50       774 if ($new) {
363 0         0 delete $self->{properties}->{period};
364 0         0 $self->add_property( period => DateTime::Format::ICal->format_period($new) );
365             }
366              
367 392   50     1298 my $period = $self->property('period') || return undef;
368 0         0 my $ret = DateTime::Format::ICal->parse_period($period->[0]->value);
369              
370             # $ret->set_time_zone($period->[0]->parameters->{TZID}) if ($period->[0]->parameters->{TZID});
371 0         0 return $ret;
372             }
373              
374              
375             =head2 recurrence
376              
377             Returns a L object representing the union of all the
378             Cs in this object.
379              
380             May return undef.
381              
382             If passed one or more L lists, L lists, Ls,
383             or Ls then set the recurrence rules to be those.
384              
385             =cut
386              
387             sub recurrence {
388 394     394 1 557 my $self = shift;
389            
390              
391 394         1034 return $self->_rule_set('rrule', @_);
392             }
393              
394             =head2 rdate
395              
396             Returns a L object representing the set of all Cs in the object.
397              
398             May return undef.
399              
400             =cut
401              
402             sub rdate {
403 392     392 1 528 my $self = shift;
404              
405 392         1130 return $self->_date_set('rdate', @_);
406             }
407              
408              
409             =head2 exrule
410              
411             Returns a L object representing the union of all the
412             Cs in this object.
413              
414             May return undef.
415              
416             If passed one or more L lists, L lists, Ls,
417             or Ls then set the recurrence exclusion rules to be those.
418              
419             =cut
420              
421              
422             sub exrule {
423 392     392 1 573 my $self = shift;
424              
425 392         940 return $self->_rule_set('exrule', @_);
426              
427             }
428              
429             =head2 exdate
430              
431             Returns a L object representing the set of all Cs in the object.
432              
433             May return undef.
434              
435             =cut
436              
437             sub exdate {
438 392     392 1 520 my $self = shift;
439              
440 392         742 return $self->_date_set('exdate', @_);
441             }
442              
443              
444              
445             sub _date_set {
446 784     784   887 my $self = shift;
447 784         1175 my $name = shift;
448              
449              
450 784 50       2262 $self->property($name) || return undef;
451 0         0 my @dates;
452 0         0 for (@{ $self->property($name) }) {
  0         0  
453 0         0 foreach my $bit (split /,/, $_->value) {
454 0         0 my $date = DateTime::Format::ICal->parse_datetime($bit);
455             # $date->set_time_zone($_->parameters->{TZID}) if $_->parameters->{TZID};
456 0         0 push @dates, $date;
457             }
458             }
459 0         0 return DateTime::Set->from_datetimes( dates => \@dates );
460              
461             }
462              
463            
464             sub _rule_set {
465 786     786   863 my $self = shift;
466 786         1008 my $name = shift;
467              
468 786 50       1723 if (@_) {
469 0         0 delete $self->{properties}->{$name};
470 0         0 foreach my $rule (DateTime::Format::ICal->format_recurrence(@_)) {
471             #$rule =~ s!^$name:!!i;
472 0         0 $rule =~ s!^[^:]+:!!;
473 0         0 $self->add_properties( $name => $rule );
474             }
475             }
476              
477              
478 786         933 my @recurrence;
479 786   50     1797 my $start = $self->start || return undef;
480 786         35839 my $tz = $start->time_zone;
481              
482 786         4582 $start = $start->clone;
483 786         12175 $start->set_time_zone("floating");
484              
485 786         9566 my $set = DateTime::Set->empty_set;
486 786 100       18851 $self->property($name) || return undef;
487 17         144 for (@{ $self->property($name) }) {
  17         82  
488 18         255 my $recur = DateTime::Format::ICal->parse_recurrence(recurrence => $_->value, dtstart => $start);
489             # $recur->set_time_zone($_->parameters->{TZID}) if $_->parameters->{TZID};
490 18         385945 $set = $set->union($recur);
491             }
492             # $set->set_time_zone($tz);
493 17         1534 return $set;
494              
495              
496             }
497              
498             =head2 recurrence_id
499              
500             Returns a L object representing the recurrence-id of this event.
501              
502             May return undef.
503              
504             If passed a L object will set that to be the new recurrence-id.
505              
506             =cut
507              
508             sub recurrence_id {
509 451     451 1 756 my $self = shift;
510 451         696 my $new = shift;
511              
512 451 50       1155 if ($new) {
513 0         0 delete $self->{properties}->{'recurrence-id'};
514 0         0 $self->add_property('recurrence-id' => DateTime::Format::ICal->format_datetime($new));
515             }
516              
517              
518 451   100     1204 my $rid = $self->property('recurrence-id') || return undef;
519 6         53 my $ret = DateTime::Format::ICal->parse_datetime($rid->[0]->value);
520              
521             # $ret->set_time_zone($rid->[0]->parameters->{TZID}) if $rid->[0]->parameters->{TZID};
522              
523 6         1446 return $ret;
524              
525             }
526              
527             =head2 uid
528              
529             Returns the uid of this event.
530              
531             If passed a new value then sets that to be the new uid value.
532              
533             =cut
534              
535             sub uid {
536 505     505 1 1069 my $self = shift;
537 505         608 my $uid = shift;
538              
539 505 100       1031 if ($uid) {
540 55         255 delete $self->{properties}->{uid};
541 55         154 $self->add_property( uid => $uid );
542             }
543              
544 505   50     11170 $uid = $self->property('uid') || return undef;
545 505         8855 return $uid->[0]->value;
546              
547             }
548              
549             =head2 summary
550              
551             Returns a string representing the summary of this event.
552              
553             May return undef.
554              
555             If passed a new value then sets that to be the new summary (and will escape all relevant characters).
556              
557             =cut
558              
559             sub summary {
560 294     294 1 9698 my $self = shift;
561 294         300 my $summ = shift;
562              
563 294 50       502 if ($summ) {
564 0         0 delete $self->{properties}->{summary};
565 0         0 $self->add_property( summary => $summ );
566             }
567              
568 294   50     595 $summ = $self->property('summary') || return undef;
569 294         1946 return $summ->[0]->value;
570             }
571              
572             =head2 description
573              
574             Returns a string representing the summary of this event.
575              
576             May return undef.
577              
578             If passed a new value then sets that to be the new description (and will escape all relevant characters).
579              
580             =cut
581              
582              
583             sub description {
584 0     0 1 0 my $self = shift;
585 0         0 my $desc = shift;
586              
587 0 0       0 if ($desc) {
588 0         0 delete $self->{properties}->{description};
589 0         0 $self->add_property( description => $desc );
590             }
591            
592 0   0     0 $desc = $self->property('description') || return undef;
593 0         0 return $desc->[0]->value;
594              
595             }
596              
597              
598             sub _escape {
599 0     0   0 my $string = shift;
600 0         0 $string =~ s!(\\|,|;)!\\$1!mg;
601 0         0 $string =~ s!\x0a!\\n!mg;
602 0         0 return $string;
603             }
604              
605             sub _unescape {
606 0     0   0 my $string = shift;
607 0         0 $string =~ s!\\n!\x0a!gm;
608 0         0 $string =~ s!(\\\\|\\,|\\;)!substr($1,-1)!gem;
  0         0  
609 0         0 return $string;
610             }
611              
612              
613             =head2 explode [period]
614              
615             Takes L, L or L and
616             returns an array of events.
617              
618             If this is not a recurring event, and it falls with the span, then it
619             will return one event with the dtstart and dtend properties set and no
620             other time information.
621              
622             If this is a recurring event then it will return all times that this
623             recurs within the span. All returned events will have the dtstart and
624             dtend properties set and no other time information.
625              
626             If C is optionally passed then events longer than C will
627             be exploded into multiple events.
628              
629             C can be any of the following
630              
631             year month week day hour minute second
632              
633             =cut
634              
635             # this is quite heavily based on 'wgo' in the bin/ directory of Text::vFile::asData
636             sub explode {
637 264     264 1 370 my $self = shift;
638 264         329 my $span = shift;
639 264         326 my $period = shift;
640 264         650 my %e = $self->_normalise;
641              
642              
643            
644              
645 264         649 my @events;
646              
647              
648              
649 264 100 100     1489 if (! $e{recur} && $e{span}->intersects($span) ) {
650 16         35381 my $event = $self->clone();
651 16         212 delete $event->{properties}->{$_} for qw(rrule exrule rdate exdate duration period);
652 16         63 $event->start($e{start});
653 16         63 $event->end($e{end});
654 16         48 push @events, $event;
655             }
656              
657              
658 264 100 66     28511 if($e{recur} && $e{recur}->intersects($span)) {
659 12         2013974 my $int_set = $e{recur}->intersection($span);
660              
661            
662             # Change the event's recurrence details so that only the events
663             # inside the time span we're interested in are listed.
664 12         2030261 $e{recur} = $int_set;
665 12         922 my $it = $e{recur}->iterator;
666 12         915 while(my $dt = $it->next()) {
667 40 100 100     222788 next if $e{exrule} && $e{exrule}->contains($dt);
668 39 50 33     525973 next if $e{exdate} && $e{exdate}->contains($dt);
669 39         3715 my $event = $self->clone();
670 39         525 delete $event->{properties}->{$_} for qw(rrule exrule rdate exdate duration period);
671              
672 39         184 $event->start($dt);
673 39 100       147 if (defined $e{duration}) {
674 38         148 my $end = $dt + $e{duration};
675 38         18756 $event->end($end);
676             }
677 39         100 $event->all_day($self->all_day);
678 39         116 $event->original($self);
679 39         240 push @events, $event;
680            
681             }
682             }
683 264 50       4171 return @events if (!defined $period);
684 0         0 my @new;
685 0         0 push @new, $_->split_up($period) for @events;
686 0         0 return @new;
687             }
688              
689             =head2 original
690              
691             Store or fetch a reference to the original event this was derived from.
692              
693             =cut
694              
695             sub original {
696 525     525 1 1241 my $self = shift;
697              
698 525 100       1700 $self->{_original} = $_[0] if @_;
699              
700 525         4339 return $self->{_original};
701             }
702              
703             =head2 split_up
704              
705             Split an n-period event into n 1-period events.
706              
707             =cut
708              
709             sub split_up {
710 24     24 1 64 my $event = shift;
711 24         53 my $period = shift;
712              
713 24 50       90 return ($event) if $event->floating;
714              
715 24         52 my @new;
716 24         97 my $span = DateTime::Span->from_datetimes( start => $event->start, end => $event->end );
717 24         8692 my $dur = DateTime::Duration->new("${period}s" => 1)->subtract( "nanoseconds" => 1 );
718             my $r = DateTime::Set->from_recurrence(
719             recurrence => sub {
720 623     623   660747 $_[0]->truncate(to => $period )->add("${period}s" => 1);
721             },
722 24         7096 span => $span);
723 24         22942 $r = $r->union(DateTime::Set->from_datetimes(dates => [$event->start]));
724              
725 24         17207 my $i = $r->iterator;
726 24         1428 while (my $dt = $i->next) {
727 491 100       80119 last if $dt >= $event->end; # && !$event->all_day;
728 484         115167 my $e = $event->clone;
729 484         3098 $e->start($dt);
730 484         1314 $e->all_day(0);
731 484         1418 $e->original($event);
732             # $e->all_day($event->all_day) if $period ne 'second' && $period ne 'minute' && $period ne 'day';
733              
734 484         1824 my $end = $dt->truncate( to => $period )->add( "${period}s" => 1 )->subtract( nanoseconds => 1 );
735 484         797321 $e->end($end);
736 484         4820 push @new, $e;
737             }
738             # If, say we have a one week and 1 day event and period is
739             # 'week' then need to truncate to one 1 week event and one
740             # day event.
741             # $end = $e{end} if ( defined $period && $e{end} < $end);
742 24         671 $new[-1]->end($event->end); # if !$event->all_day;
743 24         3569 return @new;
744             }
745              
746             =head2 is_in
747              
748             Takes L, L or L and
749             returns whether this event can fall within that time frame.
750              
751             =cut
752              
753             sub is_in {
754 128     128 1 262271 my $self = shift;
755 128         154 my $span = shift;
756              
757 128         411 my %e = $self->_normalise;
758              
759              
760 128   66     904 return ( ( !$e{recur} && $e{span}->intersects($span) ) ||
761             ( $e{recur} && $e{recur}->intersection($span) ) );
762              
763             }
764              
765             # return normalised information about this event
766             sub _normalise {
767 392     392   515 my $self = shift;
768              
769 392         598 my %e = ();
770              
771 392         820 $e{period} = $self->period;
772 392         3513 $e{start} = $self->start;
773 392         1006 $e{end} = $self->end;
774 392         2103 $e{duration} = $self->duration;
775 392         24301 $e{recur} = $self->recurrence;
776 392         6035 $e{exrule} = $self->exrule;
777 392         5755 $e{rdate} = $self->rdate;
778 392         3461 $e{exdate} = $self->exdate;
779 392         2903 $e{rid} = $self->recurrence_id;
780 392         3725 $e{uid} = $self->uid;
781              
782            
783 392 50       5292 if (defined $e{period}) {
784 0 0 0     0 if (defined $e{start} || defined $e{end}) {
785 0         0 die "Found a period *and* a start or end:\n".$self->as_string;
786             }
787            
788 0         0 $e{start} = $e{period}->start;
789 0         0 $e{end} = $e{period}->end;
790              
791             }
792              
793              
794              
795 392 50       1092 if (!defined $e{start}) {
796 0         0 die "Couldn't find start\n".$self->as_string;
797             }
798              
799 392 50 66     1650 if (defined $e{end} && defined $e{duration}) {
800 0         0 die "Found both end *and* duration:\n".$self->as_string;
801             }
802            
803              
804             # events can be floating
805             #if (!defined $e{end} && !defined $e{duration}) {
806             # die "Couldn't find end *or* duration:\n".$self->as_string;
807             #}
808              
809 392 100       1220 if (defined $e{duration}) {
810 154         617 $e{end} = $e{start} + $e{duration};
811             }
812              
813 392 50       86638 if ($e{rdate}) {
814 0 0       0 $e{recur} = (defined $e{recur}) ? $e{recur}->union($e{rdate}) : $e{rdate};
815             }
816              
817 392   66     1251 my $end = $e{end} || $e{start}->clone->add(seconds => 1 );
818 392         18450 $e{span} = DateTime::Span->from_datetimes( start => $e{start}, end => $end );
819              
820 392 100       132142 $e{duration} = $e{span}->duration if $e{end};
821              
822 392         121014 return %e;
823             }
824              
825              
826             =head1 AUTHOR
827              
828             Simon Wistow
829              
830             =head1 COPYING
831              
832             Copyright, 2005 Simon Wistow
833              
834             Distributed under the same terms as Perl itself.
835              
836             =head1 BUGS
837              
838             Potential timezone problems?
839              
840             =head1 SEE ALSO
841              
842             L, L, L, L, L
843              
844             =cut
845              
846             1;