File Coverage

blib/lib/HTML/CalendarMonth/DateTool.pm
Criterion Covered Total %
statement 560 634 88.3
branch 34 90 37.7
condition 8 51 15.6
subroutine 173 187 92.5
pod 9 9 100.0
total 784 971 80.7


line stmt bran cond sub pod time code
1             package HTML::CalendarMonth::DateTool;
2             {
3             $HTML::CalendarMonth::DateTool::VERSION = '1.26';
4             }
5              
6             # Base class for determining what date calculation package to use.
7              
8 10     10   32 use strict;
  10         11  
  10         222  
9 10     10   28 use warnings;
  10         10  
  10         167  
10 10     10   27 use Carp;
  10         8  
  10         478  
11              
12 10     10   3758 use File::Which qw( which );
  10         6028  
  10         15411  
13              
14             my %Toolmap = (
15             'Time::Local' => 'TimeLocal',
16             'Date::Calc' => 'DateCalc',
17             'DateTime' => 'DateTime',
18             'Date::Manip' => 'DateManip',
19             'ncal' => 'Ncal',
20             'cal' => 'Cal',
21             );
22              
23             my %Classmap;
24             $Classmap{lc $Toolmap{$_}} = $_ foreach keys %Toolmap;
25              
26             my($Cal_Cmd, $Ncal_Cmd);
27              
28             sub _toolmap {
29 461     461   1876 shift;
30 461         471 my $str = shift;
31 461         644 my $tool = $Toolmap{$str};
32 461 100       752 unless ($tool) {
33 308         732 foreach (values %Toolmap) {
34 1218 100       9837 if ($str =~ /^$_$/i) {
35 308         376 $tool = $_;
36 308         1200 last;
37             }
38             }
39             }
40 461 50       874 return unless $tool;
41 461         1275 join('::', __PACKAGE__, $tool);
42             }
43              
44             sub new {
45 310     310 1 1409 my $class = shift;
46 310         386 my $self = {};
47 310         364 bless $self, $class;
48 310         894 my %parms = @_;
49 310         530 $self->{year} = $parms{year};
50 310         420 $self->{month} = $parms{month};
51 310         364 $self->{weeknum} = $parms{weeknum};
52 310         357 $self->{historic} = $parms{historic};
53 310 100       598 if (! $self->{year}) {
54 1         2 my @dmy = $self->_dmy_now;
55 1         1 $self->{year} = $dmy[2];
56 1   33     6 $self->{month} ||= $dmy[1];
57             }
58 310   50     633 $self->{month} ||= 1;
59 310 100       558 if ($parms{datetool}) {
60             $self->{datetool} = $self->_toolmap($parms{datetool})
61 152 50       303 or croak "Sorry, didn't find a tool for datetool '$parms{datetool}'\n";
62             }
63 310         773 my $dc = $self->_summon_date_class;
64 308 50       12556 unless (eval "require $dc") {
65 0         0 croak "Problem loading $dc ($@)\n";
66             }
67             # rebless into new class
68 308         1454 bless $self, $dc;
69             }
70              
71 1538     1538 1 3208 sub year { shift->{year} }
72 1228     1228 1 2982 sub month { shift->{month} }
73 310     310 1 1204 sub weeknum { shift->{weeknum} }
74 0     0 1 0 sub historic { shift->{historic} }
75 310     310 1 650 sub datetool { shift->{datetool} }
76              
77             sub _name {
78 446     446   503 my $class = shift;
79 446   66     1201 $class = ref $class || $class;
80 446         1852 lc((split(/::/, $class))[-1]);
81             }
82              
83             sub _cal_cmd {
84 1     1   326 my $self = shift;
85 1 50       4 if (! defined $Cal_Cmd) {
86 1   50     3 $Cal_Cmd = which('cal') || '';
87 1 50       186 if ($Cal_Cmd) {
88 0         0 my @out = grep { ! /^\s*$/ } `$Cal_Cmd 9 1752`;
  0         0  
89             # September 1752
90             #Su Mo Tu We Th Fr Sa
91             # 1 2 14 15 16
92             #17 18 19 20 21 22 23
93             #24 25 26 27 28 29 30
94 0         0 my @pat = (
95             qr/^\s*\S+\s+\d+$/,
96             qr/^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s*$/,
97             qr/^\s*\d+\s+\d+\s+\d+\s+\d+\s+\d+\s*$/,
98             qr/^\s*\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s*$/,
99             qr/^\s*\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s*$/,
100             );
101 0 0       0 if (@out == @pat) {
102 0         0 for my $i (0 .. $#out) {
103 0 0       0 if ($out[$i] !~ $pat[$i]) {
104 0         0 $Cal_Cmd = '';
105 0         0 last;
106             }
107             }
108             }
109             else {
110 0         0 $Cal_Cmd = '';
111             }
112             }
113             }
114 1         3 $Cal_Cmd;
115             }
116              
117             sub _ncal_cmd {
118 1     1   326 my $self = shift;
119 1 50       3 if (! defined $Ncal_Cmd) {
120 1   50     4 $Ncal_Cmd = which('ncal') || '';
121 1 50       168 if ($Ncal_Cmd) {
122 0         0 my @out = grep { ! /^\s*$/ } map { s/^\s*//; $_ } `$Ncal_Cmd 9 1752`;
  0         0  
  0         0  
  0         0  
123             # September 1752
124             #Mo 18 25
125             #Tu 1 19 26
126             #We 2 20 27
127             #Th 14 21 28
128             #Fr 15 22 29
129             #Sa 16 23 30
130             #Su 17 24
131 0         0 my @pat = (
132             qr/^\s*\S+\s+\d+$/,
133             qr/^\s*\S+\s+\d+\s+\d+\s*$/,
134             qr/^\s*\S+\s+\d+\s+\d+\s+\d+\s*$/,
135             qr/^\s*\S+\s+\d+\s+\d+\s+\d+\s*$/,
136             qr/^\s*\S+\s+\d+\s+\d+\s+\d+\s*$/,
137             qr/^\s*\S+\s+\d+\s+\d+\s+\d+\s*$/,
138             qr/^\s*\S+\s+\d+\s+\d+\s+\d+\s*$/,
139             qr/^\s*\S+\s+\d+\s+\d+\s*$/,
140             );
141 0 0       0 if (@out == @pat) {
142 0         0 for my $i (0 .. $#out) {
143 0 0       0 if ($out[$i] !~ $pat[$i]) {
144 0         0 $Ncal_Cmd = '';
145 0         0 last;
146             }
147             }
148             }
149             else {
150 0         0 $Ncal_Cmd = '';
151             }
152             }
153             }
154 1         2 $Ncal_Cmd;
155             }
156              
157             sub day_epoch {
158             # in case our subclasses are lazy
159 0     0 1 0 my($self, $day, $month, $year) = @_;
160 0   0     0 $month ||= $self->month;
161 0   0     0 $year ||= $self->year;
162 0         0 Time::Local::timegm(0,0,0,1,$month,$year);
163             }
164              
165             sub _skips {
166 307     307   306 my $self = shift;
167 307 50       775 @_ ? $self->{skips} = shift : $self->{skips};
168             }
169              
170 307     307 1 661 sub dow1st { (shift->dow1st_and_lastday)[0] }
171              
172 614     614 1 1488 sub lastday { (shift->dow1st_and_lastday)[1] }
173              
174             sub _dmy_now {
175 308     308   351 my $self = shift;
176 308 100       540 my $ts = @_ ? shift : time;
177 308         11139 my($d, $m, $y) = (localtime($ts))[3,4,5];
178 308         405 ++$m; $y += 1900;
  308         358  
179 308         795 ($d, $m, $y);
180             }
181              
182             sub _dom_now {
183 307     307   328 my $self = shift;
184 307 50       625 my $ts = @_ ? shift : time;
185 307         333 my($d, $m, $y);
186 307 50       1149 if ($ts =~ /^\d+$/) {
187 307 50       534 if (length $ts <= 2) {
188 0         0 ($d, $m, $y) = ($ts, $self->month, $self->year);
189 0 0 0     0 croak "invalid day of month (1 .. " . $self->lastday . ") '$ts'"
190             unless $ts >= 1 && $ts <= $self->lastday;
191             }
192             else {
193 307         666 ($d, $m, $y) = $self->_dmy_now($ts);
194             }
195             }
196             else {
197 0         0 ($y, $m, $d) = $ts =~ m{^(\d+)/(\d\d)/(\d\d)$};
198 0 0       0 croak "invalid yyyy/mm/dd date string '$ts'" unless defined $d;
199             }
200 307         706 my($cy, $cm) = ($self->year, $self->month);
201 307         1680 my $first = sprintf("%04d/%02d/%02d", $cy, $cm, 1);
202 307         685 my $last = sprintf("%04d/%02d/%02d", $cy, $cm, $self->lastday);
203 307         600 my $pivot = sprintf("%04d/%02d/%02d", $y, $m, $d);
204 307 50       1008 return -1 if $pivot gt $last;
205 0 0       0 return 0 if $pivot lt $first;
206 0         0 $d;
207             }
208              
209             sub _summon_date_class {
210 310     310   281 my $self = shift;
211 310         293 my @tools;
212 310 100       591 if (my $c = $self->datetool) {
213 152     1   11243 eval "use $c";
  1     1   5  
  1     1   0  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   2  
  1     1   11  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   13  
  1     1   6  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   11  
  1     1   6  
  1     1   1  
  1     1   12  
  1     1   6  
  1     1   2  
  1     1   12  
  1     1   5  
  1     1   2  
  1     1   12  
  1     1   6  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   13  
  1     1   6  
  1     1   1  
  1     1   12  
  1     1   6  
  1     1   2  
  1     1   12  
  1     1   5  
  1     1   2  
  1     1   12  
  1     1   6  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   13  
  1     1   6  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   2  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   11  
  1     1   5  
  1     1   1  
  1     1   14  
  1     1   5  
  1     1   1  
  1     1   13  
  1     1   5  
  1     1   2  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   27  
  1     1   4  
  1     1   2  
  1     1   11  
  1     1   5  
  1     1   1  
  1     1   13  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   2  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   4  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   2  
  1     1   11  
  1     1   5  
  1     1   2  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   13  
  1     1   6  
  1     1   2  
  1     1   12  
  1     1   7  
  1     1   2  
  1     1   12  
  1     1   6  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   0  
  1     1   13  
  1     1   6  
  1     1   1  
  1     1   12  
  1     1   7  
  1     1   1  
  1     1   12  
  1     1   6  
  1     1   1  
  1     1   12  
  1     1   5  
  1     1   1  
  1     1   11  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   4  
  1         1  
  1         11  
  1         4  
  1         2  
  1         11  
  1         5  
  1         1  
  1         13  
  1         5  
  1         1  
  1         12  
  1         4  
  1         1  
  1         12  
  1         4  
  1         1  
  1         12  
  1         6  
  1         1  
  1         12  
  1         5  
  1         1  
  1         13  
  1         5  
  1         2  
  1         12  
  1         5  
  1         1  
  1         12  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         11  
  1         4  
  1         2  
  1         11  
  1         5  
  1         1  
  1         12  
  1         5  
  1         2  
  1         11  
  1         4  
  1         1  
  1         12  
  1         4  
  1         1  
  1         12  
  1         4  
  1         1  
  1         11  
  1         5  
  1         1  
  1         13  
  1         4  
  1         2  
  1         11  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         11  
  1         4  
  1         1  
  1         11  
  1         4  
  1         1  
  1         12  
  1         4  
  1         2  
  1         11  
  1         5  
  1         1  
  1         12  
  1         4  
  1         1  
  1         12  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         11  
  1         4  
  1         1  
  1         12  
  1         4  
  1         2  
  1         11  
  1         5  
  1         1  
  1         11  
  1         4  
  1         2  
  1         11  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         12  
  1         4  
  1         1  
  1         12  
  1         4  
  1         1  
  1         12  
  1         4  
  1         1  
  1         11  
  1         5  
  1         1  
  1         11  
  1         4  
  1         1  
  1         11  
  1         4  
  1         2  
  1         11  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         12  
  1         5  
  1         1  
  1         13  
  1         5  
  1         1  
  1         12  
  1         5  
  1         1  
  1         11  
  1         4  
  1         1  
  1         20  
  1         5  
  1         1  
  1         13  
  1         5  
  1         1  
  1         13  
  1         6  
  1         1  
  1         12  
  1         4  
  1         1  
  1         12  
  1         7  
  1         1  
  1         13  
  1         6  
  1         2  
  1         12  
  1         6  
  1         2  
  1         12  
  1         5  
  1         1  
  1         12  
  1         5  
  1         1  
  1         12  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         12  
  1         5  
  1         1  
  1         12  
  1         6  
  1         2  
  1         13  
  1         4  
  1         2  
  1         11  
  1         5  
  1         1  
  1         12  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         12  
  1         5  
  1         1  
  1         12  
  1         5  
  1         1  
  1         12  
  1         5  
  1         103  
  1         15  
  1         5  
  1         1  
  1         11  
  1         4  
  1         2  
  1         12  
  1         5  
  1         2  
  1         11  
  1         5  
  1         1  
  1         11  
  1         5  
  1         2  
  1         12  
  1         4  
  1         2  
  1         11  
  1         6  
  1         1  
  1         12  
  1         7  
  1         2  
  1         13  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         13  
  1         5  
  1         1  
  1         11  
  1         7  
  1         2  
  1         16  
  1         5  
  1         2  
  1         11  
  1         5  
  1         2  
  1         12  
  1         6  
  1         2  
  1         12  
  1         5  
  1         1  
  1         12  
  1         6  
  1         2  
  1         13  
  1         5  
  1         1  
  1         13  
  1         4  
  1         1  
  1         13  
  1         5  
  1         1  
  1         12  
  1         5  
  1         2  
  1         12  
  1         5  
  1         1  
  1         12  
  1         4  
  1         2  
  1         10  
  1         5  
  1         1  
  1         10  
  1         5  
  1         1  
  1         11  
  1         5  
  1         1  
  1         12  
  1         5  
  1         2  
  1         12  
  1         5  
  1         2  
  1         12  
  1         5  
  1         1  
  1         11  
  1         6  
  1         1  
  1         12  
  1         5  
  1         2  
  1         12  
  1         4  
  1         2  
  1         11  
214 152 50       377 die "invalid date tool $c : $@" if $@;
215 152         387 @tools = $c->_name;
216             }
217             else {
218 158         481 @tools = qw( timelocal datecalc datetime datemanip ncal cal );
219             }
220 310         341 my($dc, @fails);
221 310         443 for my $tool (@tools) {
222 310         693 my $method = join('_', '', lc($tool), 'fails');
223 310 100       902 if (my $f = $self->$method) {
224 2         6 push(@fails, [$tool, $f]);
225             }
226             else {
227 308         698 $dc = $self->_toolmap($tool);
228 308         439 last;
229             }
230             }
231 310 100       813 return $dc if $dc;
232 2 50       4 if (@tools == 1) {
233 2         3 croak "invalid date tool " . join(': ', @{$fails[0]});
  2         379  
234             }
235             else {
236 0         0 croak join("\n",
237             "no valid date tool found:",
238             map(sprintf("%11s: %s", @$_), @fails),
239             "\n"
240             );
241             }
242             }
243              
244             sub _dump_tests {
245 0     0   0 my $self = shift;
246 0   0     0 print "Time::Local : ", $self->_timelocal_fails || 1, "\n";
247 0   0     0 print " Date::Calc : ", $self->_datecalc_fails || 1, "\n";
248 0   0     0 print " DateTime : ", $self->_datetime_fails || 1, "\n";
249 0   0     0 print "Date::Manip : ", $self->_datemanip_fails || 1, "\n";
250 0   0     0 print " ncal : ", $self->_ncal_fails || 1, "\n";
251 0   0     0 print " cal : ", $self->_cal_fails || 1, "\n";
252             }
253              
254             sub _is_julian {
255 0     0   0 my $self = shift;
256 0         0 my $y = $self->year;
257 0 0 0     0 $y < 1752 || ($y == 1752 && $self->month <= 9);
258             }
259              
260             sub _timelocal_fails {
261 310     310   417 my $self = shift;
262 310 50       630 return "not installed" unless $self->_timelocal_present;
263 310 50       754 return "week-of-year numbering unsupported" if $self->weeknum;
264 310         629 my $y = $self->year;
265 310 100 66     1595 return "only years between 1970 and 2038 supported"
266             if $y < 1970 || $y >= 2038;
267 308         598 return;
268             }
269              
270             sub _ncal_fails {
271 0     0   0 my $self = shift;
272 0 0       0 return "command not found" unless $self->_ncal_present;
273 0 0 0     0 return "week-of-year numbering not supported prior to 1752/09"
274             if $self->weeknum && $self->_is_julian;
275 0         0 return;
276             }
277              
278             sub _cal_fails {
279 0     0   0 my $self = shift;
280 0 0       0 return "command not found" unless $self->_cal_present;
281 0 0       0 return "week-of-year numbering not supported" if $self->weeknum;
282 0         0 return;
283             }
284              
285             sub _datecalc_fails {
286 0     0   0 my $self = shift;
287 0 0       0 return "not installed" unless $self->_datecalc_present;
288 0 0 0     0 return "historic mode prior to 1752/09 not supported"
289             if $self->historic && $self->_is_julian;
290 0         0 return;
291             }
292              
293             sub _datetime_fails {
294 0     0   0 my $self = shift;
295 0 0       0 return "not installed" unless $self->_datetime_present;
296 0 0 0     0 return "historic mode prior to 1752/09 not supported"
297             if $self->historic && $self->_is_julian;
298 0         0 return;
299             }
300              
301             sub _datemanip_fails {
302 0     0   0 my $self = shift;
303 0 0       0 return "not installed" unless $self->_datemanip_present;
304 0 0 0     0 return "historic mode prior to 1752/09 not supported"
305             if $self->historic && $self->_is_julian;
306 0 0       0 eval { require Date::Manip && Date::Manip::Date_Init() };
  0         0  
307 0 0       0 return "init failure: $@" if $@;
308 0         0 return;
309             }
310              
311 310     310   15833 sub _timelocal_present { eval "require Time::Local"; return !$@ }
  310         4222  
312 0     0   0 sub _datecalc_present { eval "require Date::Calc"; return !$@ }
  0         0  
313 0     0   0 sub _datetime_present { eval "require DateTime"; return !$@ }
  0         0  
314 0     0   0 sub _datemanip_present { eval "require Date::Manip"; return !$@ }
  0         0  
315 0     0   0 sub _ncal_present { shift->_ncal_cmd }
316 0     0   0 sub _cal_present { shift->_cal_cmd };
317              
318              
319             1;
320              
321             __END__