File Coverage

blib/lib/Net/Analysis/Time.pm
Criterion Covered Total %
statement 94 100 94.0
branch 29 42 69.0
condition 5 12 41.6
subroutine 23 23 100.0
pod 4 13 30.7
total 155 190 81.5


line stmt bran cond sub pod time code
1             package Net::Analysis::Time;
2             # $Id: Time.pm 131 2005-10-02 17:24:31Z abworrall $
3              
4 3     3   35613 use 5.008000;
  3         11  
  3         154  
5             our $VERSION = '0.01';
6 3     3   15 use strict;
  3         4  
  3         90  
7 3     3   13 use warnings;
  3         12  
  3         92  
8 3     3   14 use Carp qw(carp croak confess);
  3         6  
  3         190  
9 3     3   810 use POSIX qw(strftime);
  3         5973  
  3         18  
10             use overload
11 3         46 q("") => \&as_string,
12             q(0+) => \&as_number,
13             q(+=) => \&inceq,
14             q(-=) => \&deceq, #)
15             q(+) => \&addition,
16             q(-) => \&subtraction, #)
17 3     3   2519 q(<=>) => \&numerical_cmp;
  3         7  
18              
19             our $Default_format = ''; # No format, raw epoch
20              
21             # {{{ POD
22              
23             =head1 NAME
24              
25             Net::Analysis::Time - value object for [tv_sec, tv_usec] times
26              
27             =head1 SYNOPSIS
28              
29             use Net::Analysis::Time;
30              
31             my $t1 = Net::Analysis::Time->new(10812345, 123456);
32             my $t2 = Net::Analysis::Time->new(10812356, 123456);
33              
34             my $diff = $t2-$t1; # == new Time Object
35              
36             print "$diff\n"; # == "11.000000"
37              
38             $t1->round_usec(10000); # "$t1" == "10812345.120000";
39              
40             =head1 DESCRIPTION
41              
42             Can't believe I've found myself implementing a date/time module. The shame of
43             it.
44              
45             This is a heavily overloaded object, so '+', '-' do what you expect.
46              
47             There is some format stuff to change how it stringfies, and some stuff for
48             rounding off values, used elsewhere for time-boxing.
49              
50             This stuff should probably all be junked as soon as someone wants some
51             efficiency.
52              
53             =cut
54              
55             # }}}
56              
57             #### Public methods
58             #
59             # {{{ new
60              
61             # {{{ POD
62              
63             =head2 new ($sec [, $usec] )
64              
65             If passed a single floating point arg, does what it can, but don't blame me if
66             rounding errors knacker things up.
67              
68             Best to pass two ints, one seconds and one microseconds.
69              
70             =cut
71              
72             # }}}
73              
74             sub new {
75 7     7 1 25 my ($class, $s, $us) = @_;
76              
77             # If it looks like we've been passed floating point seconds, sort it out
78 7 50 33     28 if (!defined $us && ($s - int($s))) {
79 0         0 ($s, $us) = _breakup_float ($s);
80             }
81              
82 7 50       23 $us = 0 if (!defined $us);
83              
84 7         40 return bless ({'s'=>$s, us=>$us}, $class);
85             }
86              
87             # }}}
88             # {{{ clone
89              
90             # {{{ POD
91              
92             =head2 clone ()
93              
94             Returns a new object, holding the same time value as the invocant.
95              
96             =cut
97              
98             # }}}
99              
100             sub clone {
101 3     3 1 319 my ($self) = shift;
102              
103 3         12 my $new = { %$self };
104              
105 3         9 return bless $new => ref($self); # Copy class over
106             }
107              
108             # }}}
109             # {{{ numbers
110              
111             sub numbers {
112 1     1 0 742 my $self = shift;
113             return (wantarray)
114 1 50       46 ? ( $self->{'s'}, $self->{us} )
115             : [ $self->{'s'}, $self->{us} ];
116             }
117              
118             # }}}
119             # {{{ round_usec
120              
121             =head2 round_usec ($usec_step [, $round_up_not_down])
122              
123             Rounds the time down to the nearest usec_step value. Valid values between 10
124             and 1000000. A value of 1000000 will round to the nearest second.
125              
126             Optional argument, if true, causes rounding to go up, not down.
127              
128             =cut
129              
130             sub round_usec {
131 4     4 1 8 my ($self, $val, $up) = @_;
132              
133 4 50 33     23 if ($val < 10 || $val > 1000000) {
134 0         0 croak ("round_usec([10-1000000]), not '$val'\n");
135             }
136              
137 4         10 $self->{rem} = $self->{us} % $val;
138              
139 4         7 $self->{us} -= $self->{rem};
140              
141 4 100 66     19 if ($up && $self->{rem}) {
142 2 100       6 if ($val == 1000000) {
143 1         2 $self->{'s'}++;
144             } else {
145 1         1 $self->{us} += $val ; # round up, not down
146             }
147              
148 2         6 $self->{rem} = $val - $self->{rem}; # Allow for one level of restore
149             }
150             }
151              
152             # }}}
153             # {{{ usec
154              
155             sub usec {
156 1     1 0 2 my $self = shift;
157              
158 1         6 return $self->{'s'} * 1000000 + $self->{us};
159             }
160              
161             # }}}
162              
163             #### Overload methods
164             #
165             # {{{ as_string
166              
167             sub as_string {
168 18     18 0 51 my ($self, $fmt) = @_;
169 18         26 my $ret = '';
170              
171             # If we've been passed an explicit format, override the default for
172             # the scope of this execution.
173 18         27 local $Default_format = $Default_format;
174 18 100       45 Net::Analysis::Time->set_format($fmt) if ($fmt);
175              
176 18 100       27 if ($Default_format) {
177 6         570 $ret = strftime($Default_format, gmtime($self->{'s'}));
178             } else {
179 12         21 $ret = $self->{'s'};
180             }
181              
182 18         134 return $ret . sprintf (".%06d", $self->{us});
183             }
184              
185             # }}}
186             # {{{ as_number
187              
188             sub as_number {
189 6     6 0 10 my ($self) = shift;
190              
191 6         25 return $self->{'s'} + ($self->{us} / 1000000);
192             }
193              
194             # }}}
195             # {{{ numerical_cmp
196              
197             sub numerical_cmp {
198             # If the seconds agree, it's down to the microseconds ...
199 5 50 33 5 0 497 if (ref($_[0]) ne 'Net::Analysis::Time' ||
200             ref($_[1]) ne 'Net::Analysis::Time')
201             {
202 0         0 confess "Time<=> args bad: ".ref($_[0]).", ".ref($_[1])."\n";
203             }
204              
205 5 50       15 if ($_[0]->{'s'} == $_[1]->{'s'}) {
206 5         35 return ($_[0]->{'us'} <=> $_[1]->{'us'});
207             }
208 0         0 return ($_[0]->{'s'} <=> $_[1]->{'s'});
209             }
210              
211             # }}}
212             # {{{ inceq
213              
214             sub inceq {
215 4     4 0 354 my ($arg1, $arg2, $arg3) = @_;
216              
217             # Should really work out what to do here ..
218 4 50       12 die "we have arg3 1 !\n".Data::Dumper::Dumper(\@_) if ($arg3);
219              
220 4         11 $arg1->_add (_arg_to_nums ($arg2));
221              
222 4         11 return $arg1;
223             }
224              
225             # }}}
226             # {{{ deceq
227              
228             sub deceq {
229 4     4 0 317 my ($arg1, $arg2, $arg3) = @_;
230              
231             # Should really work out what to do here ..
232 4 50       13 die "we have arg3 2 !\n".Data::Dumper::Dumper(\@_) if ($arg3);
233              
234 4         9 $arg1->_subtract (_arg_to_nums ($arg2));
235              
236 4         10 return $arg1;
237             }
238              
239             # }}}
240             # {{{ addition
241              
242             sub addition {
243 1     1 0 8 my ($arg1, $arg2, $arg3) = @_;
244              
245             # Should really work out what to do here ..
246 1 50       4 die "we have arg3 3!\n".Data::Dumper::Dumper(\@_) if ($arg3);
247              
248 1         3 my $new = $arg1->clone();
249              
250 1         3 $new->_add (_arg_to_nums ($arg2));
251              
252 1         3 return $new;
253             }
254              
255             # }}}
256             # {{{ subtraction
257              
258             sub subtraction {
259 1     1 0 6 my ($arg1, $arg2, $arg3) = @_;
260              
261             # Should really work out what to do here ..
262 1 50       4 confess "we have arg3 4!\n".Data::Dumper::Dumper(\@_) if ($arg3);
263              
264 1         3 my $new = $arg1->clone();
265              
266 1         3 $new->_subtract (_arg_to_nums ($arg2));
267              
268 1         3 return $new;
269             }
270              
271             # }}}
272              
273             #### Class methods
274             #
275             # {{{ set_format
276              
277             =head1 CLASS METHODS
278              
279             =head2 set_format ($format)
280              
281             Set the default output format for stringification of the date/time.
282             The parameter is either a C compliant string, or a named
283             format:
284              
285             raw - 1100257189.123456
286             time - 10:59:49.123456
287             full - 2004/11/12 10:59:49.123456
288              
289             Returns the old format.
290              
291             =cut
292              
293             sub set_format {
294 7     7 1 12 my ($class, $fmt) = @_;
295 7         27 my (%format_shortcuts) = (raw => '',
296             full => '%Y/%m/%d %T',
297             time => '%T',
298             );
299 7         9 my $old_format = $Default_format;
300              
301 7 50       18 if (exists $format_shortcuts{$fmt}) {
302 7         16 $Default_format = $format_shortcuts{$fmt};
303             } else {
304 0         0 $Default_format = $fmt;
305             }
306              
307 7         17 return $old_format;
308             }
309              
310             # }}}
311              
312             #### Helpers
313             #
314             # {{{ _breakup_float
315              
316             sub _breakup_float {
317 2     2   5 my ($f) = shift;
318              
319             # Break up float with: int (rounds down), and sprintf (rounds closest)
320 2         5 my $s = int($f);
321 2         30 my $us = sprintf ("%6d", ($f - $s) * 1000000);
322              
323 2 50       15 return (wantarray) ? ($s,$us) : [$s,$us];
324             }
325              
326             # }}}
327             # {{{ _arg_to_nums
328              
329             sub _arg_to_nums {
330 10     10   15 my ($arg) = @_;
331              
332 10 100       44 if (! ref($arg)) {
    100          
    50          
333 2         9 return _breakup_float($arg);
334              
335             } elsif (ref ($arg) eq 'ARRAY') {
336 5         17 return (@$arg);
337              
338             } elsif (ref ($arg) eq 'Net::Analysis::Time') {
339 3         12 return ($arg->{'s'}, $arg->{us});
340              
341             } else {
342 0         0 die "could not make arg '$arg' into time: ".Data::Dumper::Dumper($arg);
343             }
344             }
345              
346             # }}}
347              
348             # {{{ _add
349              
350             sub _add {
351 5     5   8 my ($self, $s, $us) = @_;
352              
353 5         8 $self->{'s'} += $s;
354              
355             # Catch overflows
356 5 100       17 if (($self->{'us'} += $us) > 1000000) {
357 1         3 $self->{'s'}++;
358 1         3 $self->{'us'} -= 1000000;
359             }
360             }
361              
362             # }}}
363             # {{{ _subtract
364              
365             sub _subtract {
366 5     5   6 my ($self, $s, $us) = @_;
367              
368 5         9 $self->{'s'} -= $s;
369              
370             # Catch underflows
371 5 100       17 if (($self->{'us'} -= $us) < 0) {
372 2         3 $self->{'s'}--;
373 2         5 $self->{'us'} += 1000000;
374             }
375             }
376              
377             # }}}
378              
379             1;
380             __END__