File Coverage

blib/lib/Net/SDP/Time.pm
Criterion Covered Total %
statement 182 237 76.7
branch 70 128 54.6
condition 3 6 50.0
subroutine 28 31 90.3
pod 16 17 94.1
total 299 419 71.3


line stmt bran cond sub pod time code
1             package Net::SDP::Time;
2              
3             ################
4             #
5             # Net::SDP - Session Description Protocol (rfc2327)
6             #
7             # Nicholas J Humfrey
8             # njh@cpan.org
9             #
10             # See the bottom of this file for the POD documentation.
11             #
12              
13 5     5   27 use strict;
  5         10  
  5         189  
14 5     5   26 use vars qw/$VERSION/;
  5         9  
  5         311  
15 5     5   44 use constant NTPOFFSET => 2208988800;
  5         10  
  5         455  
16 5     5   26 use Carp;
  5         8  
  5         20236  
17              
18             $VERSION="0.07";
19              
20              
21              
22             sub new {
23 4     4 0 740 my $class = shift;
24 4         23 my $self = {
25             't_start' => '0',
26             't_end' => '0',
27             'r' => [],
28             };
29 4         15 bless $self, $class;
30              
31             # Initial value provided ?
32 4         8 my ($t) = @_;
33 4 100       24 $self->_parse_t($t) if (defined $t);
34              
35 4         14 return $self;
36             }
37              
38             sub _ntptime {
39 8     8   110 return time() + NTPOFFSET;
40             }
41              
42             #sub remove {
43             # my $self=shift;
44             #
45             # ### Delete ourselves from our parent's array
46             #
47             # undef $self;
48             #}
49              
50              
51             sub _parse_t {
52 1     1   2 my $self = shift;
53 1         2 my ($t) = @_;
54            
55             # we need two positive integers
56             # t=
57 1 50       9 if ( $t !~ /^([0-9]+) ([0-9]+)$/ ) {
58 0         0 warn "Invalid 't' passed: $t";
59 0         0 return 0;
60             }
61              
62 1         8 ($self->{'t_start'}, $self->{'t_end'}) = split(/ /, $t);
63            
64             # Success
65 1         3 return 1;
66             }
67              
68             sub _generate_t {
69 1     1   2 my $self = shift;
70              
71 1         6 return "t=".$self->{'t_start'}.' '.$self->{'t_end'}."\n";
72             }
73              
74             sub _parse_r {
75 0     0   0 my $self = shift;
76 0         0 my ($r) = @_;
77            
78 0 0       0 if ( $self->is_permanent ) {
79 0         0 warn "corrupt packet, you cannot have a repeat field for a permanent session";
80 0         0 return 0;
81             }
82            
83             # we need at least three
84             # r=
85 0 0       0 if ( $r !~ /^([0-9]+[dhms]?)( [0-9]+[dhms]?){2,}$/ ) {
86 0         0 warn "Invalid 'r' passed: $r";
87 0         0 return 0;
88             }
89              
90 0         0 my @values = split / /, $r;
91 0 0       0 if ( $values[0] == 0 ) {
92 0         0 warn "you cannot have a repeat interval of zero";
93 0         0 return 0;
94             }
95            
96 0         0 _repeat_push($self, \@values);
97            
98             # Success
99 0         0 return 1;
100             }
101              
102             sub _generate_r {
103 2     2   4 my $self = shift;
104            
105 2         6 my $result = '';
106 2         3 foreach my $item ( @{$self->{'r'}} )
  2         6  
107             {
108 2         5 my $element = _rollup_seconds($item->{'interval'}) . ' '
109             . _rollup_seconds($item->{'duration'});
110              
111 2         3 foreach my $offset ( @{$item->{'offsets'}} ) {
  2         11  
112 3         5 $element .= ' ' . _rollup_seconds( $offset );
113             }
114            
115 2         6 $result .= 'r=' . $element . "\n";
116             }
117              
118 2         19 return $result;
119             }
120              
121              
122             sub start_time_ntp {
123 48     48 1 59 my $self=shift;
124 48         58 my ($start_time) = @_;
125 48 100       111 if ( defined $start_time ) {
126 3         12 $self->{'t_start'} = $start_time;
127             # you cannot have a permanent session with repeat interval
128 3 50       10 $self->repeat_delete_all if ( $start_time == 0 );
129             }
130 48         394 return $self->{'t_start'};
131             }
132              
133             sub end_time_ntp {
134 18     18 1 21 my $self=shift;
135 18         21 my ($end_time) = @_;
136 18 100       49 $self->{'t_end'} = $end_time if defined $end_time;
137 18         209 return $self->{'t_end'};
138             }
139              
140             sub start_time_unix {
141 13     13 1 24 my $self=shift;
142 13         17 my ($start_time) = @_;
143 13 100       36 $self->start_time_ntp( $start_time+NTPOFFSET ) if (defined $start_time);
144 13 50       24 return 0 if ($self->start_time_ntp()==0);
145 13         25 return $self->start_time_ntp() - NTPOFFSET;
146             }
147              
148             sub end_time_unix {
149 5     5 1 14 my $self=shift;
150 5         7 my ($end_time) = @_;
151 5 100       21 $self->end_time_ntp( $end_time+NTPOFFSET ) if (defined $end_time);
152 5 50       10 return 0 if ($self->end_time_ntp()==0);
153 5         104 return $self->end_time_ntp() - NTPOFFSET;
154             }
155              
156             sub start_time {
157 4     4 1 5 my $self=shift;
158 4 50       9 return "Permanent" if ($self->is_permanent());
159 4         9 return scalar(localtime($self->start_time_unix()))
160             }
161              
162             sub end_time {
163 3     3 1 4 my $self=shift;
164 3 50       7 return "Permanent" if ($self->is_permanent());
165 3 100       9 return "Unbounded" if ($self->end_time_ntp()==0);
166 2         5 return scalar(localtime($self->end_time_unix()))
167             }
168              
169             sub is_permanent {
170 17     17 1 211 my $self=shift;
171            
172 17 100       81 if ($self->start_time_ntp()==0)
173 2         11 { return 1; }
174 15         51 else { return 0; }
175             }
176              
177             sub make_permanent {
178 2     2 1 11 my $self=shift;
179 2         15 $self->{'t_start'} = 0;
180 2         6 $self->{'t_end'} = 0;
181              
182             # you cannot have a permanent session with repeat intervals
183 2         35 $self->repeat_delete_all;
184             }
185              
186             sub is_unbounded {
187 0     0 1 0 my $self=shift;
188            
189 0 0       0 if ($self->end_time_ntp()==0)
190 0         0 { return 1; }
191 0         0 else { return 0; }
192             }
193              
194             sub make_unbounded {
195 2     2 1 4 my $self=shift;
196 2         4 $self->{'t_end'} = 0;
197              
198             # you cannot have a permanent session with repeat intervals
199 2         6 $self->repeat_delete_all();
200             }
201              
202             sub as_string {
203 5     5 1 11 my $self=shift;
204              
205             # Permanent
206 5 100       12 if ( $self->is_permanent() ) {
207 1         6 return "Broadcasts permanently.";
208             }
209              
210              
211             # Repeat elements present
212 4 100       7 if ( @{$self->{'r'}} ) {
  4         12  
213 2         3 my $text;
214            
215 2 100       6 if ( $self->end_time_ntp() == 0 ) {
216 1         2 $text = 'Broadcasts ';
217             }
218             else {
219 1         4 $text = 'Until ' . $self->end_time() . ', broadcasts ';
220             }
221              
222 2         5 my @repeatSlots = ();
223 2         3 foreach my $repeat ( @{$self->{'r'}} ) {
  2         5  
224 3         10 my $interval = _summariseTime($repeat->{interval});
225            
226 3         10 my %results = (
227             interval => $interval,
228             times => [ ]
229             );
230            
231 3         9 my @abbr = qw( Sun Mon Tue Wed Thu Fri Sat );
232             # the results output depends on the interval
233 3         11 foreach my $offset ( @{$repeat->{offsets}} ) {
  3         7  
234 3         8 my @startTime = localtime($self->start_time_unix() + $offset);
235 3         14 my @endTime = localtime($self->start_time_unix() + $offset + $repeat->{duration} );
236            
237 3         6 my $time;
238             # weekly: display which day
239 3 50       16 if ( $repeat->{interval} == 604800 ) {
    50          
    50          
240 0         0 $time = 'every ' . $abbr[$startTime[6]] . ', from ';
241 0         0 $time .= _buildHourlyTime(\@startTime, \@endTime);
242             }
243             # daily: display which hour
244             elsif ( $repeat->{interval} == 86400 ) {
245 0         0 $time = _buildHourlyTime(\@startTime, \@endTime);
246             }
247             # hourly: display minutely times
248             elsif ( $repeat->{interval} == 3600 ) {
249 3         11 $time = 'from ' . $startTime[1] . 'mins until ' . $endTime[1] . 'mins past';
250             }
251             # we fall back to the best we can do which is a more direct
252             # textual description of the 'r' field
253             # anyone being caught here might want to consider improving
254             # the above common cases and/or adding their common cases
255             # that I could not think of
256             else {
257 0         0 my $friendlierOffset = _rollup_seconds($offset);
258 0 0       0 $friendlierOffset .= 's'
259             if ( $friendlierOffset =~ /^[0-9]+$/ );
260 0         0 my $friendlierDuration = _rollup_seconds($repeat->{duration});
261 0 0       0 $friendlierDuration .= 's'
262             if ( $friendlierDuration =~ /^[0-9]+$/ );
263            
264 0         0 $time = " starting $friendlierOffset past the interval and lasting $friendlierDuration";
265             }
266            
267 3         4 push @{$results{times}}, $time;
  3         15  
268             }
269            
270 3         11 push @repeatSlots, \%results;
271             }
272            
273 2         9 while ( my $repeater = shift @repeatSlots ) {
274 3 50       9 if ( $repeater->{interval} !~ /^[0-9]+$/ ) {
275 3         9 $text .= 'every ' . $repeater->{interval} . ' ';
276             } else {
277 0         0 my $friendlierInterval = _rollup_seconds($repeater->{interval});
278 0 0       0 $friendlierInterval .= 's' if ( $friendlierInterval =~ /^[0-9]+$/ );
279 0         0 $text .= "every $friendlierInterval interval ";
280             }
281            
282 3         4 while ( my $repeaterTimes = shift @{$repeater->{times}} ) {
  6         29  
283 3         5 $text .= $repeaterTimes;
284            
285 3 50       4 if ( scalar(@{$repeater->{times}}) > 1 ) {
  3 50       8  
  3         19  
286 0         0 $text .= ', ';
287             }
288             elsif ( scalar(@{$repeater->{times}}) == 1 ) {
289 0         0 $text .= ' and ';
290             }
291             }
292            
293 3 50       16 if ( scalar(@repeatSlots) > 1 ) {
    100          
294 0         0 $text .= ', again';
295             }
296             elsif ( scalar(@repeatSlots) == 1 ) {
297 1         5 $text .= ', and again ';
298             }
299             }
300            
301 2         7 $text .= ' starting ' . $self->start_time() . '.';
302            
303 2         63 return $text;
304             }
305            
306             # no repeat elements so nice and simple
307             else {
308 2 50       4 if ( $self->start_time_ntp() == 0 ) {
309 0         0 return 'Broadcasts until ' . $self->end_time().'.' ;
310             }
311             else {
312 2         7 return 'Broadcasts from ' . $self->start_time() . ' until ' . $self->end_time().'.' ;
313             }
314             }
315             }
316              
317             sub _buildHourlyTime {
318 0     0   0 my $startTime = shift;
319 0         0 my $endTime = shift;
320            
321 0         0 my @times = ( $startTime->[2], $startTime->[1], $endTime->[2], $endTime->[1] );
322            
323 0         0 foreach my $item ( 0..(scalar(@times) - 1 ) ) {
324 0 0       0 $times[$item] = ( length($times[$item]) == 1 )
325             ? '0' . $times[$item]
326             : $times[$item];
327             }
328            
329 0         0 return $times[0] . ':' . $times[1] . ' until ' . $times[2] . ':' . $times[3];
330             }
331              
332             sub _summariseTime {
333 3     3   4 my $value = shift;
334            
335             # we can only do from minutes to weeks as after that how many
336             # days are there in a month, what about a year, etc etc?
337 3 50       22 if ( ( $value % 604800 ) == 0 ) {
    50          
    50          
    0          
338 0         0 $value /= 604800;
339 0 0       0 $value = ( $value == 1 ) ? 'week' : $value . ' weeks';
340             }
341             elsif ( ( $value % 86400 ) == 0 ) {
342 0         0 $value /= 86400;
343 0 0       0 $value = ( $value == 1 ) ? 'day' : $value . ' days';
344             }
345             elsif ( ( $value % 3600 ) == 0 ) {
346 3         6 $value /= 3600;
347 3 50       8 $value = ( $value == 1 ) ? 'hour' : $value . ' hours';
348             }
349             elsif ( ( $value % 60 ) == 0 ) {
350 0         0 $value /= 60;
351 0 0       0 $value = ( $value == 1 ) ? 'minute' : $value . ' minutes';
352             }
353             else {
354 0 0       0 $value = ( $value == 1 ) ? 'second' : $value . ' seconds';
355             }
356            
357 3         7 return $value;
358             }
359              
360             sub repeat_add {
361 4     4 1 229 my $self=shift;
362 4         8 my ($interval, $duration, $offsets) = @_;
363 4 50       12 carp "Missing interval parameter" unless (defined $interval);
364 4 50       11 carp "Missing duration parameter" unless (defined $duration);
365 4 50       11 carp "Missing offsets parameter" unless (defined $offsets);
366 4 50 66     34 carp "Interval parameter cannot be zero" if ( $interval =~ /^\d+$/ and $interval == 0 );
367            
368 4 50       11 if ( $self->is_permanent ) {
369 0         0 carp "repeat_add failed, you cannot have a repeat field for a permanent session";
370 0         0 return;
371             }
372            
373             # Make it is hashref if only one offset passed
374 4 100       26 $offsets = [ $offsets ] if ( ref($offsets) ne 'ARRAY' );
375            
376 4         18 my @values = ( $interval, $duration, ( @$offsets ) );
377 4         14 _repeat_push($self, \@values);
378              
379 4         18 return $self->{'r'}->[-1];
380             }
381              
382             sub repeat_delete {
383 1     1 1 2 my $self=shift;
384 1         3 my ($num) = @_;
385            
386 1 50 33     7 return 1 if ( !defined($num) || !defined($self->{'r'}->[$num]) );
387            
388 1         2 my $results = [ ];
389 1         3 for my $loop ( 0...(scalar(@{$self->{'r'}}) - 1) ) {
  1         4  
390 2 100       6 next if ( $loop == $num );
391            
392 1         3 push @$results, $self->{'r'}->[$loop];
393             }
394 1         3 $self->{'r'} = $results;
395            
396 1         6 return 0;
397             }
398              
399             sub repeat_delete_all {
400 5     5 1 674 my $self=shift;
401              
402 5         11 $self->{'r'} = [ ];
403            
404 5         31 return 0;
405             }
406              
407             sub repeat_desc {
408 1     1 1 2 my $self=shift;
409            
410 1         1 my $num = shift;
411            
412 1 50       5 $num = 0 if ( !defined($num) );
413            
414 1 50       4 return undef if ( !defined($self->{'r'}->[$num]) );
415            
416 1         3 return $self->{'r'}->[$num];
417             }
418              
419             sub repeat_desc_arrayref {
420 4     4 1 8 my $self=shift;
421            
422 4 50       83 if ( defined($self->{'r'}) ) {
423 4         23 return $self->{'r'};
424             }
425 0         0 return undef;
426             }
427              
428             sub _rollup_seconds {
429 7     7   15 my $value = shift;
430            
431 7 50       24 if ( $value !~ /^[0-9]+[dhms]?$/ ) {
432 0         0 carp "Invalid value parsed to _rollup_seconds: $value";
433 0         0 return;
434             }
435            
436             # if its already partially rolled up we should unroll it all first
437 7 50       33 $value = _rollout_seconds($value)
438             if ( $value !~ /^[0-9]+[dhms]$/ );
439            
440             # if its zero do nothing with it
441 7 100       19 return 0 if ( $value == 0 );
442            
443             # try reducing to days
444 6 100       26 if ( ( $value % 86400 ) == 0 ) {
    100          
    50          
445 1         26 $value = ( $value / 86400 ) . 'd';
446             }
447             # try reducing to hours
448             elsif ( ( $value % 3600 ) == 0 ) {
449 3         10 $value = ( $value / 3600 ) . 'h';
450             }
451             # and finally try to minutes
452             elsif ( ( $value % 60 ) == 0 ) {
453 2         9 $value = ( $value / 60 ) . 'm';
454             }
455            
456 6         19 return $value;
457             }
458              
459             sub _rollout_seconds {
460 20     20   30 my $value = shift;
461              
462 20 50       68 if ( $value !~ /^[0-9]+[dhms]?$/ ) {
463 0         0 carp "Invalid value parsed to _rollout_seconds: $value";
464 0         0 return;
465             }
466            
467             # test for a NOOP
468 20 100       68 if ( $value =~ /^[0-9]+s?$/ ) {
469 17 50       39 $value = substr($value, 0, -1) if ( substr($value, -1, 1) eq 's' );
470 17         40 return int($value);
471             }
472            
473 3         6 my $unit = substr($value, -1, 1);
474 3         7 $value = substr($value, 0, -1);
475            
476 3 100       10 if ( $unit eq 'd' ) {
    100          
477 1         3 $value *= 86400;
478             }
479             elsif ( $unit eq 'h' ) {
480 1         1 $value *= 3600;
481             }
482             # must be 'm' (minutes)
483             else {
484 1         3 $value *= 60;
485             }
486            
487 3         10 return int($value);
488             }
489              
490             sub _repeat_push {
491 4     4   14 my $self=shift;
492            
493 4         7 my $values = shift;
494            
495 4         18 foreach my $item ( 0...(scalar(@$values) - 1) ) {
496 13         78 $values->[$item] = _rollout_seconds($values->[$item]);
497             }
498            
499 4         22 my $rProcessed = {
500             interval => shift @$values,
501             duration => shift @$values,
502             offsets => [ ]
503             };
504 4         9 foreach my $offset ( @$values ) {
505 5         7 push @{$rProcessed->{'offsets'}}, $offset;
  5         23  
506             }
507            
508 4         7 push @{$self->{'r'}}, $rProcessed;
  4         11  
509             }
510              
511             1;
512              
513             __END__