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   75502 use base qw(Class::Accessor);
  6         15  
  6         2695  
3 6     6   8444 use strict;
  6         14  
  6         113  
4 6     6   31 use vars qw($VERSION);
  6         13  
  6         305  
5              
6 6         12446 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   1848 );
  6         31201  
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.03;
22              
23             sub new {
24 5     5 1 76 my $class = shift;
25 5         58 my $self = bless {}, $class;
26 5         27 my %args = @_;
27 5         26 $self->_set_default_parameters();
28 5         62 $self->_set_passed_parameters(\%args);
29 5         17 return $self;
30             }
31              
32             sub get_dates {
33 714     714 1 5609 my $self = shift;
34 714         1716 $self->clear_error;
35 714         7007 my %args = @_;
36 714 50       1884 if (scalar keys %args) {
37 0         0 $self->_set_passed_parameters(\%args);
38             }
39 714 50       1650 if (!$self->type) {
40 0         0 $self->set_error("Cannot get dates - no range type specified");
41 0         0 return ();
42             }
43 714         7829 my @start = $self->_get_start_date;
44 714 50       1691 unless (scalar @start) {
45 0         0 return ();
46             }
47 714         1462 my @end = $self->_get_end_date(@start);
48 714 50       1612 unless (scalar @end) {
49 0         0 return ();
50             }
51 714         1409 my @last = $self->_get_last_date(@end);
52 714 50       1635 unless (scalar @last) {
53 0         0 return ();
54             }
55              
56 714         1468 my $start_date = $self->_array_to_date(@start);
57 714         1532 my $end_date = $self->_array_to_date(@end);
58 714         1428 my $last_date = $self->_array_to_date(@last);
59 714         2668 return ($start_date,$end_date,$last_date);
60             }
61              
62             sub set_type {
63 430     430 0 533301 my ($self, $type) = @_;
64 430 50       1262 return 0 unless defined $type;
65 430         911 $type = uc($type);
66 430         1869 my %valid_types = ('DAY' => 1 , 'WEEK' => 1 , 'MONTH' => 1 , 'QUARTER' => 1 , 'YEAR' => 1);
67 430 100       1485 unless ($valid_types{$type}) {
68 1         6 $self->set_error("Invalid type $type");
69 1         13 $self->type('');
70 1         11 return 0;
71             }
72 429         1573 $self->type($type);
73 429         6059 return 1;
74             }
75              
76             sub get_type {
77 2153     2153 0 5127 my $self = shift;
78 2153         4222 return $self->type;
79             }
80              
81             sub set_intervals {
82 733     733 1 7630 my ($self, $intervals) = @_;
83 733 50       2234 return 0 unless defined $intervals;
84 733 100       3287 if ($intervals =~ /^(?:-)?\d+$/) {
85 732         2694 $self->intervals($intervals);
86 732         8602 return 1;
87             }
88             else {
89 1         5 $self->set_error("Invalid intervals, \"$intervals\"");
90 1         11 return 0;
91             }
92             }
93              
94             sub get_intervals {
95 726     726 1 5198 my $self = shift;
96 726         1562 return $self->intervals;
97             }
98              
99             sub set_span {
100 729     729 1 5235 my ($self, $span) = @_;
101 729 50       1852 return 0 unless defined $span;
102 729 100 66     4823 if ($span =~ /^\d+$/ and $span > 0) {
103 728         2582 $self->span($span);
104 728         8368 return 1;
105             }
106             else {
107 1         6 $self->set_error("Invalid span, \"$span\"");
108 1         11 return 0;
109             }
110             }
111              
112             sub get_span {
113 1437     1437 1 3973 my $self = shift;
114 1437         2960 return $self->span;
115             }
116              
117             sub set_start_day_of_week {
118 311     311 1 401906 my ($self, $start_dow) = @_;
119 311 50       967 return 0 unless defined $start_dow;
120 311         764 $start_dow = uc($start_dow);
121 311         1571 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       892 if (exists $valid_dow{$start_dow}) {
131 310         1245 $self->start_dow($valid_dow{$start_dow});
132 310         4516 $self->_set_start_dow_name($start_dow);
133 310         3998 return 1;
134             }
135             else {
136 1         5 $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 3096 my $self = shift;
143 301         616 return $self->start_dow;
144             }
145              
146             sub set_start_day_of_month {
147 163     163 1 13691 my ($self, $start_dom) = @_;
148 163 50       576 return 0 unless defined $start_dom;
149 163 100 66     1845 if ($start_dom =~ /^\d+$/ and $start_dom >= 1 and $start_dom <= 28) {
      100        
150 161         696 $self->start_dom($start_dom);
151             } else {
152 2         12 $self->set_error("Invalid start day of month, \"$start_dom\"");
153 2         23 return 0;
154             }
155 161         2488 return 1;
156             }
157              
158             sub get_start_day_of_month {
159 198     198 1 11661 my $self = shift;
160 198         452 return $self->start_dom;
161             }
162              
163             sub set_start_month_of_year {
164 147     147 1 6119 my ($self, $start_moy) = @_;
165 147 50       368 return 0 unless defined $start_moy;
166 147 100 66     1212 if ($start_moy =~ /^\d+$/ and $start_moy >= 1 and $start_moy <= 12) {
      100        
167 145         392 $self->start_moy($start_moy);
168             } else {
169 2         11 $self->set_error("Invalid start month of year, \"$start_moy\"");
170 2         22 return 0;
171             }
172 145         1425 return 1;
173             }
174              
175             sub get_start_month_of_year {
176 432     432 1 5623 my $self = shift;
177 432         934 return $self->start_moy;
178             }
179              
180             sub set_today_date {
181 15     15 1 895 my ($self, @today) = @_;
182 15 100       40 if (scalar @today) {
183 8         29 my @verified_date = $self->_date_to_array(@today);
184 8 100       28 if (@verified_date) {
185 7         30 $self->today_date(@verified_date);
186 7         91 return 1;
187             }
188 1         3 my $temp = join(":",@today);
189 1         6 $self->set_error("Today override failed validation, \"$temp\"");
190 1         11 return 0;
191             }
192             else {
193 7         706 $self->today_date(Today);
194 7         112 return 1;
195             }
196             }
197              
198             sub get_today_date {
199 719     719 1 2536 my $self = shift;
200 719         981 return @{$self->today_date};
  719         1579  
201             }
202              
203             sub set_sliding_window {
204 724     724 1 3808 my ($self, $sliding_window) = @_;
205 724 50       1660 return 0 unless defined $sliding_window;
206 724 100 100     2430 if ($sliding_window == 0 or $sliding_window == 1) {
207 723         2010 $self->sliding_window($sliding_window);
208 723         6629 return 1;
209             }
210             else {
211 1         6 $self->set_error("Invalid sliding window, \"$sliding_window\"");
212 1         11 return 0;
213             }
214             }
215              
216             sub get_sliding_window {
217 717     717 1 1772 my $self = shift;
218 717         1547 return $self->sliding_window;
219             }
220              
221             sub set_direction {
222 724     724 1 3519 my ($self,$direction) = @_;
223 724 50       1571 return 0 unless defined $direction;
224 724 100       2244 if ($direction =~ /^[\+-]$/) {
225 723         1920 $self->direction($direction);
226 723         6522 return 1;
227             }
228 1         6 $self->set_error("Invalid direction argument, \"$direction\"");
229 1         12 return 0;
230             }
231              
232             sub get_direction {
233 717     717 1 1835 my $self = shift;
234 717         1466 return $self->direction;
235             }
236              
237             sub set_error {
238 15     15 0 425 my ($self, $msg) = @_;
239 15         23 my @existing = @{$self->error};
  15         46  
240 15         179 push @existing, $msg;
241 15         41 $self->error(\@existing);
242             }
243              
244             sub get_error {
245 2     2 1 26 my $self = shift;
246 2         5 return $self->error;
247             }
248              
249             sub clear_error {
250 722     722 1 1437 my $self = shift;
251 722         2022 $self->error([]);
252             }
253              
254             ################################################################################
255             sub _set_default_parameters {
256 7     7   396 my $self = shift;
257 7         29 $self->set_intervals(1);
258 7         27 $self->set_span(1);
259 7         26 $self->set_start_day_of_week('MONDAY');
260 7         32 $self->set_start_day_of_month(1);
261 7         27 $self->set_start_month_of_year(1);
262 7         35 $self->_set_print_format('%04d-%02d-%02d');
263 7         25 $self->set_today_date();
264 7         24 $self->set_sliding_window(0);
265 7         26 $self->set_direction('-');
266 7         23 $self->clear_error();
267             }
268              
269             sub _set_passed_parameters {
270 7     7   870 my $self = shift;
271 7         19 my $hash = shift;
272 7 100       33 $self->set_type($hash->{type}) if exists $hash->{type};
273 7 50       30 $self->set_intervals($hash->{intervals}) if exists $hash->{intervals};
274 7 100       25 $self->set_span($hash->{span}) if exists $hash->{span};
275 7 100       37 $self->set_today_date($hash->{today_date}) if exists $hash->{today_date};
276 7 50       20 $self->set_direction($hash->{direction}) if exists $hash->{direction};
277             $self->set_start_day_of_week($hash->{start_day_of_week})
278 7 100       24 if exists $hash->{start_day_of_week};
279             $self->set_sliding_window($hash->{sliding_window})
280 7 50       25 if exists $hash->{sliding_window};
281             $self->set_start_day_of_month($hash->{start_day_of_month})
282 7 50       20 if exists $hash->{start_day_of_month};
283             $self->set_start_month_of_year($hash->{start_month_of_year})
284 7 50       23 if exists $hash->{start_month_of_year};
285             }
286              
287             sub _get_start_date {
288 715     715   1097 my $self = shift;
289 715         1342 my $direction = $self->get_direction;
290 715         6729 my @start = $self->_start_reference;
291 715         1513 my $span = $self->get_span;
292 715         6847 my $intervals = $self->get_intervals;
293 715         6590 my @delta = $self->_delta_per_period;
294 715 100       1685 if ($direction eq '-') {
295 358         732 @delta = _negate(@delta);
296             }
297 715         1059 my $map_factor;
298 715 100       1366 if ($self->get_sliding_window) {
299 238 100       2482 $map_factor = ($direction eq '+') ? $intervals
300             : ($span + $intervals - 1)
301             ;
302             } else {
303 477         4806 $map_factor = $span * $intervals;
304             }
305 715         1404 @delta = map { $_ * $map_factor } @delta;
  2145         3961  
306 715         1585 @start = $self->_add_delta_ymd(@start, @delta);
307 715         1578 return @start;
308             }
309              
310             sub _get_end_date {
311 715     715   1484 my $self = shift;
312 715         1294 my @start = @_;
313 715         1670 my @delta = $self->_delta_ymd;
314 715         1479 my @end = $self->_add_delta_ymd(@start,@delta);
315 715         1508 return @end;
316             }
317              
318             sub _get_last_date {
319 715     715   1407 my $self = shift;
320 715         1334 my @end = @_;
321 715         1446 @end = $self->_add_delta_ymd(@end,(0,0,-1));
322 715         1412 return @end;
323             }
324              
325             sub _start_reference {
326 716     716   1108 my $self = shift;
327 716         1365 my @start = $self->get_today_date;
328 716         7801 my $type = $self->get_type;
329 716 100       7483 if ($type eq 'YEAR') {
    100          
    100          
    100          
    50          
330 168         311 my $start_moy = $self->get_start_month_of_year;
331 168 100       1583 if ($start_moy > $start[1]) {
332 42         93 @start = $self->_add_delta_ymd(@start,(-1,0,0));
333             }
334 168         292 $start[1] = $start_moy;
335 168         266 $start[2] = 1;
336             } elsif ($type eq 'QUARTER') {
337 42         89 $start[1] -= ( ( $start[1] - 1 ) % 3 );
338 42         62 $start[2] = 1;
339             } elsif ($type eq 'MONTH') {
340 170         332 my $start_dom = $self->get_start_day_of_month;
341 170 100       1654 if ($start_dom > $start[2]) {
342 42         101 @start = $self->_add_delta_ymd(@start,(0,-1,0));
343             }
344 170         317 $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         581 my $start_dow = $self->get_start_day_of_week;
350 294         2983 my $today_dow = Day_of_Week(@start);
351 294         1279 @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       755 if ($today_dow < $start_dow) {
354 168         343 @start = $self->_add_delta_ymd(@start,(0,0,-7));
355             }
356             } elsif ($type eq 'DAY') {
357             ## No change
358             }
359 716         1716 return @start;
360             }
361              
362             sub _set_start_dow_name {
363 310     310   690 my ($self,$start_dow_name) = @_;
364 310         889 $self->start_dow_name($start_dow_name);
365             }
366              
367             sub _get_start_dow_name {
368 2     2   531 my $self = shift;
369 2         7 return $self->start_dow_name;
370             }
371              
372             sub _set_print_format {
373 9     9   792 my ($self, $format) = @_;
374             ## valid: %s, %d, '/', '-', ' ', ':'
375 9         16 my $validate = $format;
376 9         81 $validate =~ s/[\/\- :]//g;
377 9         56 $validate =~ s/%[0-9]*d//g;
378 9 100       30 if ($validate) {
379 1         5 $self->set_error("Suspect output format: \"$format\"");
380 1         12 return 0;
381             }
382 8         34 $self->print_format($format);
383 8         81 return 1;
384             }
385              
386             sub _get_print_format {
387 2144     2144   3350 my $self = shift;
388 2144         4328 return $self->print_format;
389             }
390              
391             sub _delta_ymd {
392 716     716   1005 my $self = shift;
393 716         1235 my $span = $self->get_span;
394 716         6987 my @single_delta = $self->_delta_per_period;
395 716         1279 my @total_delta = map { $span * $_ } @single_delta;
  2148         3602  
396 716         1491 return @total_delta;
397             }
398              
399             sub _delta_per_period {
400 1432     1432   2053 my $self = shift;
401 1432         2411 my $type = $self->get_type;
402 1432 100       15002 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   1075 my @negatives = map { -1 * $_ } @_;
  1077         2049  
411 359         796 return @negatives;
412             }
413              
414             sub _date_to_array {
415 10     10   1494 my ($self,@date) = @_;
416 10 100 66     76 if (scalar(@date) == 1 and $date[0] =~ /^(\d+)-(\d+)-(\d+)$/) {
417 7         43 @date = ($1,$2,$3);
418             }
419 10 100 33     203 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         43 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   4166 my ($self, @date) = @_;
434 2143         3780 my $format = $self->_get_print_format();
435 2143         22624 return sprintf $format, @date;
436             }
437              
438             sub _add_delta_ymd {
439 2693     2693   5980 my ($self,@date_info) = @_;
440 2693         4053 my @new_date = ();
441 2693         3898 eval {
442 2693         7058 @new_date = Add_Delta_YMD(@date_info);
443             };
444 2693 100       5870 if ($@) {
445 1         5 my $errstring = sprintf "Cannot calculate date diff: (%d,%d,%d) + (%d,%d,%d)", @date_info;
446 1         16 $self->set_error($errstring);
447             }
448 2693         5949 return @new_date;
449             }
450              
451             1;
452              
453             __END__