File Coverage

blib/lib/Date/Calc/Endpoints.pm
Criterion Covered Total %
statement 247 253 97.6
branch 83 102 81.3
condition 22 33 66.6
subroutine 43 43 100.0
pod 20 23 86.9
total 415 454 91.4


line stmt bran cond sub pod time code
1             package Date::Calc::Endpoints;
2 6     6   74915 use base qw(Class::Accessor);
  6         15  
  6         2569  
3 6     6   8121 use strict;
  6         12  
  6         111  
4 6     6   30 use vars qw($VERSION);
  6         13  
  6         266  
5              
6 6         12199 use Date::Calc qw(
7             Today
8             Add_Delta_YMD
9             check_date
10             Day_of_Week
11             Monday_of_Week
12             Week_of_Year
13 6     6   1752 );
  6         29356  
14              
15             __PACKAGE__->mk_accessors(qw(
16             type intervals direction span sliding_window
17             start_dow start_dow_name start_dom start_moy
18             today_date error print_format
19             ));
20              
21             $VERSION = 1.01;
22              
23             sub new {
24 5     5 1 79 my $class = shift;
25 5         60 my $self = bless {}, $class;
26 5         22 my %args = @_;
27 5         30 $self->_set_default_parameters();
28 5         66 $self->_set_passed_parameters(\%args);
29 5         18 return $self;
30             }
31              
32             sub get_dates {
33 714     714 1 5781 my $self = shift;
34 714         1741 $self->clear_error;
35 714         7166 my %args = @_;
36 714 50       1881 if (scalar keys %args) {
37 0         0 $self->_set_passed_parameters(\%args);
38             }
39 714 50       1612 if (!$self->type) {
40 0         0 $self->set_error("Cannot get dates - no range type specified");
41 0         0 return ();
42             }
43 714         8224 my @start = $self->_get_start_date;
44 714 50       1749 unless (scalar @start) {
45 0         0 return ();
46             }
47 714         1532 my @end = $self->_get_end_date(@start);
48 714 50       1659 unless (scalar @end) {
49 0         0 return ();
50             }
51 714         1466 my @last = $self->_get_last_date(@end);
52 714 50       1669 unless (scalar @last) {
53 0         0 return ();
54             }
55              
56 714         1464 my $start_date = $self->_array_to_date(@start);
57 714         1601 my $end_date = $self->_array_to_date(@end);
58 714         1443 my $last_date = $self->_array_to_date(@last);
59 714         2745 return ($start_date,$end_date,$last_date);
60             }
61              
62             sub set_type {
63 430     430 0 598311 my ($self, $type) = @_;
64 430 50       1237 return 0 unless defined $type;
65 430         863 $type = uc($type);
66 430         1695 my %valid_types = ('DAY' => 1 , 'WEEK' => 1 , 'MONTH' => 1 , 'QUARTER' => 1 , 'YEAR' => 1);
67 430 100       1109 unless ($valid_types{$type}) {
68 1         6 $self->set_error("Invalid type $type");
69 1         18 $self->type('');
70 1         11 return 0;
71             }
72 429         1468 $self->type($type);
73 429         5451 return 1;
74             }
75              
76             sub get_type {
77 2153     2153 0 5343 my $self = shift;
78 2153         4675 return $self->type;
79             }
80              
81             sub set_intervals {
82 733     733 1 7133 my ($self, $intervals) = @_;
83 733 50       1816 return 0 unless defined $intervals;
84 733 100       2841 if ($intervals =~ /^(?:-)?\d+$/) {
85 732         2301 $self->intervals($intervals);
86 732         7247 return 1;
87             }
88             else {
89 1         6 $self->set_error("Invalid intervals, \"$intervals\"");
90 1         12 return 0;
91             }
92             }
93              
94             sub get_intervals {
95 726     726 1 5179 my $self = shift;
96 726         1566 return $self->intervals;
97             }
98              
99             sub set_span {
100 729     729 1 4742 my ($self, $span) = @_;
101 729 50       1970 return 0 unless defined $span;
102 729 100 66     4633 if ($span =~ /^\d+$/ and $span > 0) {
103 728         2244 $self->span($span);
104 728         7034 return 1;
105             }
106             else {
107 1         5 $self->set_error("Invalid span, \"$span\"");
108 1         12 return 0;
109             }
110             }
111              
112             sub get_span {
113 1437     1437 1 3948 my $self = shift;
114 1437         3103 return $self->span;
115             }
116              
117             sub set_start_day_of_week {
118 311     311 1 402011 my ($self, $start_dow) = @_;
119 311 50       846 return 0 unless defined $start_dow;
120 311         653 $start_dow = uc($start_dow);
121 311         1458 my %valid_dow = (
122             'MONDAY' => 1,
123             'TUESDAY' => 2,
124             'WEDNESDAY' => 3,
125             'THURSDAY' => 4,
126             'FRIDAY' => 5,
127             'SATURDAY' => 6,
128             'SUNDAY' => 7,
129             );
130 311 100       774 if (exists $valid_dow{$start_dow}) {
131 310         1102 $self->start_dow($valid_dow{$start_dow});
132 310         4020 $self->_set_start_dow_name($start_dow);
133 310         3374 return 1;
134             }
135             else {
136 1         7 $self->set_error("Invalid start day of week, \"$start_dow\"");
137 1         12 return 0;
138             }
139             }
140              
141             sub get_start_day_of_week {
142 301     301 1 2967 my $self = shift;
143 301         646 return $self->start_dow;
144             }
145              
146             sub set_start_day_of_month {
147 163     163 1 11710 my ($self, $start_dom) = @_;
148 163 50       390 return 0 unless defined $start_dom;
149 163 100 66     1421 if ($start_dom =~ /^\d+$/ and $start_dom >= 1 and $start_dom <= 28) {
      100        
150 161         483 $self->start_dom($start_dom);
151             } else {
152 2         10 $self->set_error("Invalid start day of month, \"$start_dom\"");
153 2         22 return 0;
154             }
155 161         1639 return 1;
156             }
157              
158             sub get_start_day_of_month {
159 198     198 1 10456 my $self = shift;
160 198         436 return $self->start_dom;
161             }
162              
163             sub set_start_month_of_year {
164 147     147 1 5817 my ($self, $start_moy) = @_;
165 147 50       376 return 0 unless defined $start_moy;
166 147 100 66     1342 if ($start_moy =~ /^\d+$/ and $start_moy >= 1 and $start_moy <= 12) {
      100        
167 145         456 $self->start_moy($start_moy);
168             } else {
169 2         10 $self->set_error("Invalid start month of year, \"$start_moy\"");
170 2         23 return 0;
171             }
172 145         1514 return 1;
173             }
174              
175             sub get_start_month_of_year {
176 432     432 1 5330 my $self = shift;
177 432         923 return $self->start_moy;
178             }
179              
180             sub set_today_date {
181 15     15 1 900 my ($self, @today) = @_;
182 15 100       41 if (scalar @today) {
183 8         39 my @verified_date = $self->_date_to_array(@today);
184 8 100       31 if (@verified_date) {
185 7         30 $self->today_date(@verified_date);
186 7         91 return 1;
187             }
188 1         5 my $temp = join(":",@today);
189 1         5 $self->set_error("Today override failed validation, \"$temp\"");
190 1         12 return 0;
191             }
192             else {
193 7         648 $self->today_date(Today);
194 7         108 return 1;
195             }
196             }
197              
198             sub get_today_date {
199 719     719 1 2475 my $self = shift;
200 719         1019 return @{$self->today_date};
  719         1613  
201             }
202              
203             sub set_sliding_window {
204 724     724 1 3584 my ($self, $sliding_window) = @_;
205 724 50       1693 return 0 unless defined $sliding_window;
206 724 100 100     2495 if ($sliding_window == 0 or $sliding_window == 1) {
207 723         2022 $self->sliding_window($sliding_window);
208 723         6840 return 1;
209             }
210             else {
211 1         6 $self->set_error("Invalid sliding window, \"$sliding_window\"");
212 1         12 return 0;
213             }
214             }
215              
216             sub get_sliding_window {
217 717     717 1 1858 my $self = shift;
218 717         1524 return $self->sliding_window;
219             }
220              
221             sub set_direction {
222 724     724 1 3601 my ($self,$direction) = @_;
223 724 50       1600 return 0 unless defined $direction;
224 724 100       2188 if ($direction =~ /^[\+-]$/) {
225 723         1997 $self->direction($direction);
226 723         6691 return 1;
227             }
228 1         6 $self->set_error("Invalid direction argument, \"$direction\"");
229 1         11 return 0;
230             }
231              
232             sub get_direction {
233 717     717 1 1770 my $self = shift;
234 717         1617 return $self->direction;
235             }
236              
237             sub set_error {
238 15     15 0 404 my ($self, $msg) = @_;
239 15         22 my @existing = @{$self->error};
  15         40  
240 15         174 push @existing, $msg;
241 15         40 $self->error(\@existing);
242             }
243              
244             sub get_error {
245 2     2 1 27 my $self = shift;
246 2         5 return $self->error;
247             }
248              
249             sub clear_error {
250 722     722 1 1469 my $self = shift;
251 722         1923 $self->error([]);
252             }
253              
254             ################################################################################
255             sub _set_default_parameters {
256 7     7   391 my $self = shift;
257 7         41 $self->set_intervals(1);
258 7         27 $self->set_span(1);
259 7         26 $self->set_start_day_of_week('MONDAY');
260 7         27 $self->set_start_day_of_month(1);
261 7         31 $self->set_start_month_of_year(1);
262 7         38 $self->_set_print_format('%04d-%02d-%02d');
263 7         23 $self->set_today_date();
264 7         26 $self->set_sliding_window(0);
265 7         26 $self->set_direction('-');
266 7         25 $self->clear_error();
267             }
268              
269             sub _set_passed_parameters {
270 7     7   796 my $self = shift;
271 7         16 my $hash = shift;
272 7 100       69 $self->set_type($hash->{type}) if exists $hash->{type};
273 7 50       28 $self->set_intervals($hash->{intervals}) if exists $hash->{intervals};
274 7 100       27 $self->set_span($hash->{span}) if exists $hash->{span};
275 7 100       38 $self->set_today_date($hash->{today_date}) if exists $hash->{today_date};
276 7 50       24 $self->set_direction($hash->{direction}) if exists $hash->{direction};
277             $self->set_start_day_of_week($hash->{start_day_of_week})
278 7 100       23 if exists $hash->{start_day_of_week};
279             $self->set_sliding_window($hash->{sliding_window})
280 7 50       26 if exists $hash->{sliding_window};
281             $self->set_start_day_of_month($hash->{start_day_of_month})
282 7 50       24 if exists $hash->{start_day_of_month};
283             $self->set_start_month_of_year($hash->{start_month_of_year})
284 7 50       22 if exists $hash->{start_month_of_year};
285             }
286              
287             sub _get_start_date {
288 715     715   1159 my $self = shift;
289 715         1374 my $direction = $self->get_direction;
290 715         7020 my @start = $self->_start_reference;
291 715         1505 my $span = $self->get_span;
292 715         6987 my $intervals = $self->get_intervals;
293 715         6683 my @delta = $self->_delta_per_period;
294 715 100       1830 if ($direction eq '-') {
295 358         729 @delta = _negate(@delta);
296             }
297 715         1083 my $map_factor;
298 715 100       1371 if ($self->get_sliding_window) {
299 238 100       2634 $map_factor = ($direction eq '+') ? $intervals
300             : ($span + $intervals - 1)
301             ;
302             } else {
303 477         5131 $map_factor = $span * $intervals;
304             }
305 715         1443 @delta = map { $_ * $map_factor } @delta;
  2145         4109  
306 715         1613 @start = $self->_add_delta_ymd(@start, @delta);
307 715         1639 return @start;
308             }
309              
310             sub _get_end_date {
311 715     715   1549 my $self = shift;
312 715         1353 my @start = @_;
313 715         1392 my @delta = $self->_delta_ymd;
314 715         1531 my @end = $self->_add_delta_ymd(@start,@delta);
315 715         1551 return @end;
316             }
317              
318             sub _get_last_date {
319 715     715   1450 my $self = shift;
320 715         1282 my @end = @_;
321 715         1462 @end = $self->_add_delta_ymd(@end,(0,0,-1));
322 715         1421 return @end;
323             }
324              
325             sub _start_reference {
326 716     716   1085 my $self = shift;
327 716         1406 my @start = $self->get_today_date;
328 716         7923 my $type = $self->get_type;
329 716 100       7731 if ($type eq 'YEAR') {
    100          
    100          
    100          
    50          
330 168         330 my $start_moy = $self->get_start_month_of_year;
331 168 100       1651 if ($start_moy > $start[1]) {
332 42         96 @start = $self->_add_delta_ymd(@start,(-1,0,0));
333             }
334 168         322 $start[1] = $start_moy;
335 168         301 $start[2] = 1;
336             } elsif ($type eq 'QUARTER') {
337 42         89 $start[1] -= ( ( $start[1] - 1 ) % 3 );
338 42         76 $start[2] = 1;
339             } elsif ($type eq 'MONTH') {
340 170         338 my $start_dom = $self->get_start_day_of_month;
341 170 100       1714 if ($start_dom > $start[2]) {
342 42         91 @start = $self->_add_delta_ymd(@start,(0,-1,0));
343             }
344 170         325 $start[2] = $start_dom;
345             } elsif ($type eq 'WEEK') {
346             ## Calculate the "Monday" of the current week, and add the number of days to get to
347             ## desired start date. If that start day-of-week is "after" the "current" day-of-week,
348             ## that start date will be in the future. Will need to subtract a week.
349 294         587 my $start_dow = $self->get_start_day_of_week;
350 294         3034 my $today_dow = Day_of_Week(@start);
351 294         1272 @start = $self->_add_delta_ymd(Monday_of_Week(Week_of_Year(@start)),(0,0,$start_dow - 1));
352             ## NEED MORE HERE _ this is just "monday" at this point
353 294 100       763 if ($today_dow < $start_dow) {
354 168         339 @start = $self->_add_delta_ymd(@start,(0,0,-7));
355             }
356             } elsif ($type eq 'DAY') {
357             ## No change
358             }
359 716         1799 return @start;
360             }
361              
362             sub _set_start_dow_name {
363 310     310   567 my ($self,$start_dow_name) = @_;
364 310         806 $self->start_dow_name($start_dow_name);
365             }
366              
367             sub _get_start_dow_name {
368 2     2   396 my $self = shift;
369 2         7 return $self->start_dow_name;
370             }
371              
372             sub _set_print_format {
373 9     9   780 my ($self, $format) = @_;
374             ## valid: %s, %d, '/', '-', ' ', ':'
375 9         16 my $validate = $format;
376 9         87 $validate =~ s/[\/\- :]//g;
377 9         52 $validate =~ s/%[0-9]*d//g;
378 9 100       32 if ($validate) {
379 1         6 $self->set_error("Suspect output format: \"$format\"");
380 1         11 return 0;
381             }
382 8         36 $self->print_format($format);
383 8         84 return 1;
384             }
385              
386             sub _get_print_format {
387 2144     2144   3417 my $self = shift;
388 2144         4337 return $self->print_format;
389             }
390              
391             sub _delta_ymd {
392 716     716   1066 my $self = shift;
393 716         1299 my $span = $self->get_span;
394 716         7257 my @single_delta = $self->_delta_per_period;
395 716         1308 my @total_delta = map { $span * $_ } @single_delta;
  2148         3681  
396 716         1560 return @total_delta;
397             }
398              
399             sub _delta_per_period {
400 1432     1432   2170 my $self = shift;
401 1432         2526 my $type = $self->get_type;
402 1432 100       15569 return $type eq 'YEAR' ? (1,0,0)
    100          
    100          
    100          
403             : $type eq 'QUARTER' ? (0,3,0)
404             : $type eq 'MONTH' ? (0,1,0)
405             : $type eq 'WEEK' ? (0,0,7)
406             : (0,0,1)
407             }
408              
409             sub _negate {
410 359     359   1080 my @negatives = map { -1 * $_ } @_;
  1077         2127  
411 359         842 return @negatives;
412             }
413              
414             sub _date_to_array {
415 10     10   1464 my ($self,@date) = @_;
416 10 100 66     84 if (scalar(@date) == 1 and $date[0] =~ /^(\d+)-(\d+)-(\d+)$/) {
417 7         40 @date = ($1,$2,$3);
418             }
419 10 100 33     205 if ((scalar(@date) == 3) and
      33        
      33        
      66        
420             ($date[0] =~ /^\d+$/) and
421             ($date[1] =~ /^\d+$/) and
422             ($date[2] =~ /^\d+$/) and
423             (check_date(@date))) {
424 9         44 return (@date);
425             }
426             else {
427 1         7 $self->set_error("Invalid \"today\": " . join("-",@date));
428             }
429 1         13 return ();
430             }
431              
432             sub _array_to_date {
433 2143     2143   4635 my ($self, @date) = @_;
434 2143         3853 my $format = $self->_get_print_format();
435 2143         23226 return sprintf $format, @date;
436             }
437              
438             sub _add_delta_ymd {
439 2693     2693   6291 my ($self,@date_info) = @_;
440 2693         4040 my @new_date = ();
441 2693         3963 eval {
442 2693         7112 @new_date = Add_Delta_YMD(@date_info);
443             };
444 2693 100       6061 if ($@) {
445 1         7 my $errstring = sprintf "Cannot calculate date diff: (%d,%d,%d) + (%d,%d,%d)", @date_info;
446 1         13 $self->set_error($errstring);
447             }
448 2693         6040 return @new_date;
449             }
450              
451             1;
452              
453             __END__