File Coverage

blib/lib/Time/Interval.pm
Criterion Covered Total %
statement 103 117 88.0
branch 33 46 71.7
condition 13 29 44.8
subroutine 6 6 100.0
pod 0 4 0.0
total 155 202 76.7


line stmt bran cond sub pod time code
1             ###################################################
2             ## Interval.pm (Time::Interval)
3             ## Andrew N. Hicox
4             ## http://www.hicox.com
5             ##
6             ## a module for dealing with time intervals
7             ###################################################
8              
9              
10             ## Global Stuff ###################################
11             package Time::Interval;
12 1     1   4237 use strict;
  1         2  
  1         32  
13             require Exporter;
14              
15             #class global vars ...
16 1     1   5 use vars qw($VERSION @EXPORT @ISA %intervals);
  1         2  
  1         1129  
17             @ISA = qw(Exporter);
18             @EXPORT = qw(&parseInterval &convertInterval &getInterval &coalesce);
19             $VERSION = 1.234;
20             #what everything is worth in seconds
21             %intervals = (
22             'days' => ((60**2) * 24),
23             'hours' => (60 **2),
24             'minutes' => 60,
25             'seconds' => 1
26             );
27              
28              
29             ## getInterval ####################################
30             sub getInterval {
31 1     1 0 101 my $date1 = shift();
32 1         2 my $date2 = shift();
33 1   50     6 my $string = shift() || "";
34 1 50 33     7 if ( (! $date1) || (! $date2) ){
35 0         0 warn ("two dates are required for the getInterval method");
36 0         0 return (undef);
37             }
38 1         351 require Date::Parse;
39 1         5537 foreach ($date1, $date2){
40 2   33     362 $_ = Date::Parse::str2time($_) || do {
41             warn ("failed to parse date: $!\n");
42             return (undef);
43             };
44             }
45              
46 1         199 my %args = ( seconds => abs($date1 - $date2) );
47 1 50       8 if ($string =~/^small/i){
    50          
48 0         0 $args{'Small'} = 1;
49             }elsif($string !~/^\s*$/){
50 0         0 $args{'String'} = 1;
51             }
52 1         7 my $data = parseInterval(%args);
53 1         4 return ($data);
54             }
55              
56              
57             ## convertInterval ################################
58             #'days' => $num,
59             #'hours' => $num,
60             #'minutes' => $num,
61             #'seconds' => $num,
62             #'ConvertTo' => 'days'|'hours'|'minutes'|'seconds'
63             sub convertInterval {
64 14     14 0 315 my %p = @_;
65             #ConvertTo, default is seconds
66 14 100       41 exists($p{'ConvertTo'}) || do {
67 10         23 $p{'ConvertTo'} = "seconds";
68 10 50       28 warn ("convertInterval: using default ConvertTo (seconds)") if $p{'Debug'};
69             };
70             #convert everything to seconds
71 14         22 my $seconds = 0;
72 14         31 foreach ("days","hours","minutes","seconds"){
73              
74             #new 1.233 hotness: quantize seconds.
75 56 100       96 if ($_ eq "seconds"){ $p{$_} = int($p{$_}); }
  14         30  
76              
77             #as it were
78 56 100       102 if (exists($p{$_})){ $seconds += ($intervals{$_} * $p{$_}); }
  32         64  
79             }
80             #send it back out into the desired output
81 14         50 return (($seconds/$intervals{$p{'ConvertTo'}}));
82             }
83              
84              
85             ## parseInterval ##################################
86             #'days' => $num,
87             #'hours' => $num,
88             #'minutes' => $num,
89             #'seconds' => $num,
90             sub parseInterval {
91 10     10 0 882 my %p = @_;
92             #convert everything to seconds
93 10         39 my $seconds = convertInterval(%p);
94              
95             #new 1.233 hotness: quantize seconds.
96 10         21 $seconds = int($seconds);
97              
98             #do the thang
99 10         33 my %time = (
100             'days' => 0,
101             'hours' => 0,
102             'minutes' => 0,
103             'seconds' => 0
104             );
105 10         26 while ($seconds > 0){
106 26213         31539 foreach ("days","hours","minutes","seconds"){
107 27020 100       39578 if ($seconds >= $intervals{$_}){
108 26213         28949 $time{$_} ++;
109 26213         29270 $seconds -= $intervals{$_};
110 26213         36885 last;
111             }
112             }
113             }
114             #return data
115 10 100 66     80 if ($p{'Small'} && $p{'Small'} != 0) {
    100 66        
116             #return a string?
117 5         14 my @temp = ();
118 5         8 foreach ("days","hours","minutes","seconds"){
119 20 100       42 if ($time{$_} > 0){
120 10         37 push (@temp, "$time{$_}".substr($_,0,1));
121             }
122             }
123 5   50     46 return join (" ", @temp) || "0s";
124             }elsif ($p{'String'} && $p{'String'} != 0){
125             #return a string?
126 4         14 my @temp = ();
127 4         11 foreach ("days","hours","minutes","seconds"){
128 16 100       43 if ($time{$_} > 0){
129 9 50       22 if ($time{$_} == 1) {
130 0         0 push (@temp, "$time{$_} ".substr($_,0,-1));
131             } else {
132 9         38 push (@temp, "$time{$_} $_");
133             }
134             }
135             }
136 4   50     44 return (join (", ", @temp)) || "0 seconds";
137             }else{
138             #return a data structure
139 1         4 return (\%time);
140             }
141             }
142              
143              
144             ## coalesce #######################################
145             #coalesce([ [$start1, $end1], [$start2, $end2] ... ])
146             sub coalesce {
147 1     1 0 93 require Date::Parse;
148 1   50     8 my $intervals = shift() || [];
149 1         6 my %epoch_map = ();
150 1         4 my ($flag, $repeat) = (0,1);
151              
152             #convert each start / end to an epoch pair and stash 'em in epoch_map
153 1         3 foreach my $int (@{$intervals}) {
  1         5  
154 6         11 foreach (@{$int}){
  6         11  
155              
156             ## only convert if it's not already epoch time
157 12         17 my $epoch = "";
158 12 50       59 if ($_ =~/^(\d{10})$/){
159 0         0 $epoch = $1;
160             }else{
161 12         32 $epoch = Date::Parse::str2time($_);
162             }
163 12         1813 $epoch_map{$epoch} = $_;
164 12         26 $_ = $epoch;
165             }
166             }
167              
168             #sort 'em by start time
169 1         4 @{$intervals} = sort { $a->[0] <=> $b->[0] } @{$intervals};
  1         3  
  9         14  
  1         9  
170              
171             #flatten 'em
172 1         17 while ($repeat == 1) {
173 4         7 @{$intervals} = sort {
174             #if it's not an array ref, it's been destructo'd
175 10 100 66     33 if ( (ref($a) eq "ARRAY") && (ref($b) eq "ARRAY") ){
176              
177             #if b is inside a
178 5 50 33     15 if (($b->[0] >= $a->[0]) && ($b->[0] <= $a->[1])){
    0 0        
179             #if b's end time is greater than a's, update a's end time
180 5 100       11 if ($b->[1] > $a->[1]){ $a->[1] = $b->[1]; }
  4         7  
181             #destructo b
182 5         8 $b = ();
183 5         5 $flag = 1;
184 5         8 return (1);
185             #if a is inside b
186             }elsif (($a->[0] >= $b->[0]) && ($a->[0] <= $b->[1])){
187             #if a's end time is greater than b's, update b's end time
188 0 0       0 if ($a->[1] > $b->[1]){ $b->[1] = $a->[1]; }
  0         0  
189             #destructo a
190 0         0 $a = ();
191 0         0 $flag = 1;
192 0         0 return (0);
193             }else{
194 0         0 return (0);
195             }
196              
197             }else{
198 5         9 return (1);
199             }
200              
201 4         10 } @{$intervals};
  4         7  
202              
203             #weed out null elements
204 4         5 my $i = 0;
205 4         5 foreach (@{$intervals}){
  4         7  
206 7 100       13 if ( ref($_) ne "ARRAY" ){ splice (@{$intervals}, $i, 1); }
  5         5  
  5         8  
207 7         8 $i ++;
208             }
209              
210             #decide wether or not to repeat
211 4 100       8 if ($flag == 1){
212 3         4 $repeat = 1;
213 3         5 $flag = 0;
214             }else{
215 1         3 $repeat = 0;
216             }
217             }
218              
219             #replace the epoch's with their time string equivalents
220 1         1 foreach my $pair (@{$intervals}){
  1         2  
221 1 50       4 if ( ref($pair) eq "ARRAY"){
222 1         1 foreach (@{$pair}){ $_ = $epoch_map{$_}; }
  1         2  
  2         4  
223             }
224             }
225              
226             #weed out any remaining bum elements
227 1         2 my $i = 0;
228 1         1 foreach (@{$intervals}){
  1         3  
229 1 50       3 unless (ref($_) eq "ARRAY"){ splice (@{$intervals}, $i, 1); }
  0         0  
  0         0  
230 1         1 $i ++;
231             }
232 1         5 return ($intervals);
233             }