File Coverage

blib/lib/Time/Simple.pm
Criterion Covered Total %
statement 125 153 81.7
branch 40 72 55.5
condition 15 24 62.5
subroutine 26 30 86.6
pod 6 12 50.0
total 212 291 72.8


line stmt bran cond sub pod time code
1             package Time::Simple;
2              
3 1     1   1060 use 5.008003;
  1         34  
  1         82  
4             our $VERSION = '0.06';
5             our $FATALS = 1;
6              
7             =head1 NAME
8              
9             Time::Simple - A simple, light-weight ISO 8601 time object.
10              
11             =head1 SYNOPSIS
12              
13             use Time::Simple;
14             my $time = Time::Simple->new('23:24:59');
15             my $hour = $time->hours;
16             my $minute = $time->minutes;
17             my $second = $time->seconds;
18              
19             my $time2 = Time::Simple->new($hour, $minute, $second);
20              
21             my $now = Time::Simple->new;
22             my $nexthour = $now + (60*60);
23             print "An hour from now is $nexthour.\n";
24              
25             if ($nexthour->hour > 23) {
26             print "It'll be tomorrow within the next hour!\n";
27             }
28              
29             # You can also do this:
30             ($time cmp "23:24:25")
31             # ...and this:
32             ($time <=> [23, 24, 25])
33              
34             $time++; # Add a second
35             $time--; # Subtract a second
36              
37             # Seconds of difference:
38             $seconds = Time::Simple->new("00:00:02")
39             - Time::Simple->new("00:00:01");
40              
41             my $now = Time::Simple->new;
42             # A minute from now:
43             my $then = Time::Simple->new( $now + 60 );
44             # Or:
45             my $soon = Time::Simple->new( '00:01:00' );
46              
47             =head1 DESCRIPTION
48              
49             A simple, light-weight time object.
50              
51             B.
52              
53             How do you think this moudle should handle return values of multiplacation, where the
54             return value would be greater than 23:59:59?
55              
56             =head2 FATAL ERRORS
57              
58             Attempting to create an invalid time with this module will return C rather than an object.
59              
60             Some operations can produce fatal errors: these can be replaced by warnings and the
61             return of C by switching the value of C<$FATALS>:
62              
63             $Time::Simple::FATALS = undef;
64              
65             You will then only get warnings to C, and even then only if you asked perl for
66             warnings with C or by setting C<$^W> either directly or with the C<-w>
67             command-line switch.
68              
69             =head2 EXPORT
70              
71             None by default.
72              
73             =cut
74              
75 1     1   7 use strict;
  1         2  
  1         40  
76 1     1   20 use warnings;
  1         3  
  1         30  
77 1     1   5 use Carp;
  1         2  
  1         75  
78 1     1   848 use POSIX qw(strftime mktime);
  1         8340  
  1         7  
79              
80             use overload
81 1         12 '=' => '_copy',
82             '+=' => '_increment',
83             '++' => '_increment_mod',
84             '-=' => '_decrement',
85             '--' => '_decrement_mod',
86             '+' => '_add',
87             '-' => '_subtract',
88             '<=>' => '_compare',
89             'cmp' => '_compare',
90             '""' => '_stringify',
91             '*' => '_multiply',
92             '/' => '_divide',
93             # fallback=>1
94 1     1   1241 ;
  1         2  
95              
96             =head1 METHODS
97              
98             =cut
99              
100             # http://rt.cpan.org/Public/Bug/Display.html?id=34710 :-
101             # Log: Make the isdst argument to asctime and mktime default to -1
102             # instead of 0, as suggested by Mike Schilli.
103             # Branch: perl
104             # ! ext/POSIX/POSIX.pm ext/POSIX/POSIX.pod ext/POSIX/POSIX.xs
105             my $DST = $^V lt v5.10.0? 0 : 0;
106              
107             =head2 CONSTRUCTOR (new)
108              
109             $_ = Time::Simple->new('21:10:09');
110             $_ = Time::Simple->new( 11,10, 9 );
111             Time::Simple->new() == Time::Simple->new( time() );
112              
113             The constructor C returns a C object if the supplied
114             values specify a valid time, otherwise returns C.
115              
116             Valid times are either as supplied by the L, or in ISO 8601
117             format. In the latter case, the values may be supplied as a colon-delimited scalar,
118             as a list, or as an anonymous array.
119              
120             If nothing is supplied to the constructor, the current local time will be used.
121              
122             =cut
123              
124             sub new {
125 75     75 0 6981 my ($that, @hms) = (@_);
126 75         87 my $time;
127              
128 75   33     303 my $class = ref($that) || $that;
129              
130             # From time()
131 75 100 66     643 if (scalar(@hms) and
    100 66        
132             ($hms[0] =~ /^\d{10}$/g or scalar(@hms) == 10 or scalar(@hms) == 9)
133             ) {
134 2         5 $time = join'',@hms;
135 2 50       6 if ($time =~ /\D/g){
136 0 0       0 if ($FATALS){
137 0         0 croak "Could not make a time from $time - please read the documentation";
138             } else {
139 0 0       0 Carp::cluck("Could not make a time from $time - please read the documentation") if $^W;
140 0         0 return undef;
141             }
142             }
143             }
144              
145             elsif (@hms == 1) {
146 62 100       134 if(ref $hms[0] eq 'ARRAY') {
147 8         10 @hms = join':',@{$hms[0]};
  8         36  
148             }
149 62         400 @hms = $hms[0] =~ /^(\d{1,2})(:\d{1,2})?(:\d{1,2})?$/;
150 62   100     169 $hms[1] ||= '00';
151 62   100     137 $hms[2] ||= '00';
152 62         422 s/^:// foreach @hms[1..2];
153 62 100       156 if (not defined $hms[0]){
154 1 50       3 if ($FATALS){
155 1         115 croak"'$_[1]' is not a valid ISO 8601 formated time" ;
156             } else {
157 0 0       0 Carp::cluck("'$_[1]' is not a valid ISO 8601 formated time") if $^W;
158 0         0 return undef;
159             }
160             }
161             }
162              
163 74 100       153 if (not defined $time){
164 72 100       138 if (@hms == 3) {
    50          
    0          
165 67 100       118 unless (validate(@hms)){
166 1 50       4 if ($FATALS){
167 1         197 croak "Could not make a time - please read the documentation";
168             } else {
169 0 0       0 Carp::cluck("Could not make a time - please read the documentation") if $^W;
170 0         0 return undef;
171             }
172             }
173              
174             # mktime(sec, min, hour, mday, mon, year, wday = 0, yday = 0, isdst = 0/-1)
175 66         1448 my @localtime = localtime;
176 66 50       2163 $time = mktime (
    50          
177             $hms[2],
178             $hms[1],
179             $hms[0] - ($localtime[8]? 1 : 0), # Daylight saving time xxx
180             $localtime[3],
181             $localtime[4],
182             $localtime[5],
183             0,0,
184             $localtime[8]? $DST : 0
185             );
186 66 50       212 confess 'Can not mktime' if not $time;
187             }
188              
189             elsif (@hms == 0) {
190 5         13 $time = time;
191             }
192              
193             elsif ($FATALS){
194 0         0 croak "Could not make a time - please read the documentation";
195             }
196              
197             else {
198 0 0       0 Carp::cluck("Could not make a time - please read the documentation") if $^W;
199 0         0 return undef;
200             }
201             }
202 73         333 return bless \$time, $class;
203             }
204              
205 1     1 1 11 sub next { return $_[0] + 1 }
206 1     1 1 7 sub prev { return $_[0] - 1 }
207              
208             sub _mktime_seconds($) {
209 5     5   8 my $t = shift;
210 5         9 my $h = int( $t / (60*60));
211 5         7 my $m = int(($t % (60*60)) / 60);
212 5         8 my $s = int( $t % (60));
213 5         12 return $h, $m, $s;
214             }
215              
216             # Return the number of seoncds in time
217             sub total_seconds($){
218 5     5 0 7 my $self = shift;
219 5         10 my ($sh, $sm, $ss) = $self =~ /^0?(\d+?).0?(\d+?).0?(\d+?)$/;
220 5         18 $ss += ($sm * 60) + ($sh * 60 * 60);
221             }
222              
223 8     8 1 1985 sub hour { return (localtime ${$_[0]})[2] }
  8         196  
224 0     0 0 0 sub hours { return (localtime ${$_[0]})[2] }
  0         0  
225              
226 8     8 1 13 sub minute { return (localtime ${$_[0]})[1] }
  8         182  
227 0     0 0 0 sub minutes { return (localtime ${$_[0]})[1] }
  0         0  
228              
229 6     6 1 10 sub second { return (localtime ${$_[0]})[0] }
  6         151  
230 2     2 0 3 sub seconds { return (localtime ${$_[0]})[0] }
  2         33  
231              
232             sub format {
233 31     31 1 47 my $self = shift;
234 31   50     109 my $format = shift || '%H:%M:%S';
235             # strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
236 31         32 my $test;
237 31         47 eval { $test = scalar localtime $$self };
  31         736  
238 31 50       79 Carp::confess("Invalid time ($$self)") unless defined $test;
239 31         33 my $return = eval {strftime($format, localtime($$self));};
  31         1451  
240 31 50       83 Carp::confess "You supplied $$self: ".$@ if $@;
241 31         199 return $return;
242             }
243              
244             sub validate ($$$) {
245 67     67 0 109 my ($h, $m, $s)= @_;
246 67         106 foreach my $i (@_){
247 201 50 33     908 return 0 if $i != abs int $i or $i < 0;
248             }
249 67 50 66     420 return 0 if $h > 23
      66        
250             or $m > 59
251             or $s > 59;
252 66         164 return 1;
253             }
254              
255              
256             #------------------------------------------------------------------------------
257             # the following methods are called by the overloaded operators, so they should
258             # not normally be called directly.
259             #------------------------------------------------------------------------------
260 31     31   3931 sub _stringify { $_[0]->format }
261              
262             sub _copy {
263 14     14   23 my $self = shift;
264 14         18 my $v = $$self;
265 14         23 my $copy = \$v;
266 14         28 bless $copy, ref $self;
267 14         27 return $copy;
268             }
269              
270             sub _increment {
271 0     0   0 my ($self, $n) = @_;
272 0 0       0 if (UNIVERSAL::isa($n, ref($self))) {
273 0         0 $n = $$n;
274             }
275 0         0 my $copy = $self->_copy;
276 0         0 $$copy+= $n;
277 0         0 return $copy;
278             }
279              
280             sub _increment_mod {
281 3     3   1234 my ($self, $n) = @_;
282 3 50       13 $n = $$n if UNIVERSAL::isa($n, ref($self));
283 3         6 $$self ++;
284 3         29 return $self;
285             }
286              
287             sub _decrement {
288 0     0   0 my ($self, $n, $reverse) = @_;
289 0 0       0 $n = $$n if UNIVERSAL::isa($n, ref($self));
290 0         0 my $copy = $self->_copy;
291 0         0 $$copy -= $n;
292 0         0 return $copy;
293             }
294              
295             sub _decrement_mod {
296 3     3   659 my ($self, $n, $reverse) = @_;
297 3 50       14 $n = $$n if UNIVERSAL::isa($n, ref($self));
298 3         6 $$self --;
299 3         10 return $self;
300             }
301              
302             sub _add {
303 6     6   317 my ($self, $n, $reverse) = @_;
304 6 100       28 if (UNIVERSAL::isa($n, ref($self))) {
305 2         8 my $s = ($n->hour * 60 * 60)
306             + ($n->minute * 60)
307             + $n->seconds;
308 2         7 $n = $s;
309             }
310 6         18 my $copy = $self->_copy;
311 6         12 $$copy += $n;
312 6         28 return $copy;
313             }
314              
315             sub _subtract {
316 8     8   851 my ($self, $n, $reverse) = @_;
317 8 100       27 if (UNIVERSAL::isa($n, ref($self))) {
318 5         13 my $copy = $self->_copy;
319 5         11 my $diff = $$copy - $$n;
320             # $diff /= 86400;
321             # $reverse should probably always be false here, but...
322 5 100       17 $diff = -$diff if $diff < 0;
323 5 50       28 return $reverse ? -$diff : $diff;
324             } else {
325 3         8 my $copy = $self->_copy;
326 3         4 $$copy -= $n;
327 3         16 return $copy;
328             }
329             }
330              
331             sub _compare {
332 36     36   2050 my ($self, $x, $reverse) = @_;
333 36 100       250 $x = ref($self)->new($x) unless UNIVERSAL::isa($x, ref($self));
334 36         56 my $c = (int(${$self}) <=> int(${$x}));
  36         60  
  36         59  
335 36 100       262 return $reverse ? -$c : $c;
336             }
337              
338              
339              
340             sub _multiply {
341 3     3   12 my ($self, $n, $reverse) = @_;
342              
343 3 50       14 if (UNIVERSAL::isa($n, ref($self))) {
344 0         0 Carp::cluck "Cannot multiply a time by a time, only a time by a number.";
345             }
346              
347             # Convert time to seconds
348 3         9 my $ss = $self->total_seconds;
349 3         4 $ss *= $n;
350 3         8 my @hms = _mktime_seconds($ss);
351 3         11 return ref($self)->new( @hms );
352             }
353              
354             sub _divide {
355 2     2   9 my ($self, $n, $reverse) = @_;
356              
357 2 50       9 if (UNIVERSAL::isa($n, ref($self) )) {
358 0         0 Carp::cluck "Cannot multiply a time by a time, only a time by a number.";
359             }
360              
361             # Convert time to seconds
362 2         6 my $ss = $self->total_seconds;
363 2         6 my $return = $ss /= $n;
364              
365             # Convert return value to time
366 2         4 my @hms = _mktime_seconds($return);
367 2         7 return ref($self)->new( @hms );
368             }
369              
370             1;
371              
372             __END__