File Coverage

blib/lib/Date/Calc/Endpoints.pm
Criterion Covered Total %
statement 246 254 96.8
branch 84 104 80.7
condition 22 33 66.6
subroutine 43 43 100.0
pod 20 23 86.9
total 415 457 90.8


line stmt bran cond sub pod time code
1             package Date::Calc::Endpoints;
2 6     6   66591 use base qw(Class::Accessor);
  6         7  
  6         2833  
3 6     6   8144 use strict;
  6         6  
  6         104  
4 6     6   24 use vars qw($VERSION);
  6         6  
  6         281  
5              
6 6         12032 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   2400 );
  6         37048  
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 55 my $class = shift;
25 5         12 my $self = bless {}, $class;
26 5         13 my %args = @_;
27 5         22 $self->_set_default_parameters();
28 5         35 $self->_set_passed_parameters(\%args);
29 5         15 return $self;
30             }
31              
32             sub get_dates {
33 714     714 1 3340 my $self = shift;
34 714         877 $self->clear_error;
35 714         3679 my %args = @_;
36 714 50       1240 if (scalar keys %args) {
37 0         0 $self->_set_passed_parameters(\%args);
38             }
39 714 50       1053 if (!$self->type) {
40 0         0 $self->set_error("Cannot get dates - no range type specified");
41 0         0 return ();
42             }
43 714         4496 my @start = $self->_get_start_date;
44 714 50       1067 unless (scalar @start) {
45 0         0 return ();
46             }
47 714         859 my @end = $self->_get_end_date(@start);
48 714 50       973 unless (scalar @end) {
49 0         0 return ();
50             }
51 714         813 my @last = $self->_get_last_date(@end);
52 714 50       917 unless (scalar @last) {
53 0         0 return ();
54             }
55              
56 714         879 my $start_date = $self->_array_to_date(@start);
57 714         837 my $end_date = $self->_array_to_date(@end);
58 714         801 my $last_date = $self->_array_to_date(@last);
59 714         1737 return ($start_date,$end_date,$last_date);
60             }
61              
62             sub set_type {
63 430     430 0 304973 my ($self, $type) = @_;
64 430 50       775 return 0 unless defined $type;
65 430         428 $type = uc($type);
66 430         1200 my %valid_types = ('DAY' => 1 , 'WEEK' => 1 , 'MONTH' => 1 , 'QUARTER' => 1 , 'YEAR' => 1);
67 430 100       676 unless ($valid_types{$type}) {
68 1         5 $self->set_error("Invalid type $type");
69 1         8 $self->type('');
70 1         6 return 0;
71             }
72 429         813 $self->type($type);
73 429         3091 return 1;
74             }
75              
76             sub get_type {
77 2153     2153 0 2883 my $self = shift;
78 2153         2551 return $self->type;
79             }
80              
81             sub set_intervals {
82 733     733 1 4971 my ($self, $intervals) = @_;
83 733 50       1157 return 0 unless defined $intervals;
84 733 100       1993 if ($intervals =~ /^(?:-)?\d+$/) {
85 732         1172 $self->intervals($intervals);
86 732         3803 return 1;
87             }
88             else {
89 1         4 $self->set_error("Invalid intervals, \"$intervals\"");
90 1         7 return 0;
91             }
92             }
93              
94             sub get_intervals {
95 726     726 1 3313 my $self = shift;
96 726         876 return $self->intervals;
97             }
98              
99             sub set_span {
100 729     729 1 2832 my ($self, $span) = @_;
101 729 50       1007 return 0 unless defined $span;
102 729 100 66     3287 if ($span =~ /^\d+$/ and $span > 0) {
103 728         1141 $self->span($span);
104 728         3591 return 1;
105             }
106             else {
107 1         4 $self->set_error("Invalid span, \"$span\"");
108 1         8 return 0;
109             }
110             }
111              
112             sub get_span {
113 1437     1437 1 2092 my $self = shift;
114 1437         1884 return $self->span;
115             }
116              
117             sub set_start_day_of_week {
118 311     311 1 214482 my ($self, $start_dow) = @_;
119 311 50       572 return 0 unless defined $start_dow;
120 311         327 $start_dow = uc($start_dow);
121 311         1102 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       528 if (exists $valid_dow{$start_dow}) {
131 310         606 $self->start_dow($valid_dow{$start_dow});
132 310         2176 $self->_set_start_dow_name($start_dow);
133 310         1736 return 1;
134             }
135             else {
136 1         4 $self->set_error("Invalid start day of week, \"$start_dow\"");
137 1         8 return 0;
138             }
139             }
140              
141             sub get_start_day_of_week {
142 301     301 1 1882 my $self = shift;
143 301         375 return $self->start_dow;
144             }
145              
146             sub set_start_day_of_month {
147 163     163 1 7765 my ($self, $start_dom) = @_;
148 163 50       265 return 0 unless defined $start_dom;
149 163 100 66     1188 if ($start_dom =~ /^\d+$/ and $start_dom >= 1 and $start_dom <= 28) {
      100        
150 161         271 $self->start_dom($start_dom);
151             } else {
152 2         9 $self->set_error("Invalid start day of month, \"$start_dom\"");
153 2         15 return 0;
154             }
155 161         919 return 1;
156             }
157              
158             sub get_start_day_of_month {
159 198     198 1 6824 my $self = shift;
160 198         265 return $self->start_dom;
161             }
162              
163             sub set_start_month_of_year {
164 147     147 1 3678 my ($self, $start_moy) = @_;
165 147 50       226 return 0 unless defined $start_moy;
166 147 100 66     1057 if ($start_moy =~ /^\d+$/ and $start_moy >= 1 and $start_moy <= 12) {
      100        
167 145         249 $self->start_moy($start_moy);
168             } else {
169 2         6 $self->set_error("Invalid start month of year, \"$start_moy\"");
170 2         13 return 0;
171             }
172 145         783 return 1;
173             }
174              
175             sub get_start_month_of_year {
176 432     432 1 3461 my $self = shift;
177 432         557 return $self->start_moy;
178             }
179              
180             sub set_today_date {
181 15     15 1 530 my ($self, @today) = @_;
182 15 100       42 return 0 unless @today;
183 8 50       39 if (scalar @today) {
184 8         25 my @verified_date = $self->_date_to_array(@today);
185 8 100       19 if (@verified_date) {
186 7         23 $self->today_date(@verified_date);
187 7         59 return 1;
188             }
189 1         3 my $temp = join(":",@today);
190 1         3 $self->set_error("Today override failed validation, \"$temp\"");
191 1         7 return 0;
192             }
193             else {
194 0         0 $self->today_date(Today);
195 0         0 return 1;
196             }
197             }
198              
199             sub get_today_date {
200 717     717 1 709 my $self = shift;
201 717         477 return @{$self->today_date};
  717         905  
202             }
203              
204             sub set_sliding_window {
205 724     724 1 2006 my ($self, $sliding_window) = @_;
206 724 50       969 return 0 unless defined $sliding_window;
207 724 100 100     1642 if ($sliding_window == 0 or $sliding_window == 1) {
208 723         1092 $self->sliding_window($sliding_window);
209 723         3458 return 1;
210             }
211             else {
212 1         5 $self->set_error("Invalid sliding window, \"$sliding_window\"");
213 1         8 return 0;
214             }
215             }
216              
217             sub get_sliding_window {
218 717     717 1 1081 my $self = shift;
219 717         894 return $self->sliding_window;
220             }
221              
222             sub set_direction {
223 724     724 1 2170 my ($self,$direction) = @_;
224 724 50       973 return 0 unless defined $direction;
225 724 100       1448 if ($direction =~ /^[\+-]$/) {
226 723         1036 $self->direction($direction);
227 723         3431 return 1;
228             }
229 1         4 $self->set_error("Invalid direction argument, \"$direction\"");
230 1         9 return 0;
231             }
232              
233             sub get_direction {
234 717     717 1 936 my $self = shift;
235 717         902 return $self->direction;
236             }
237              
238             sub set_error {
239 15     15 0 254 my ($self, $msg) = @_;
240 15         14 my @existing = @{$self->error};
  15         29  
241 15         109 push @existing, $msg;
242 15         25 $self->error(\@existing);
243             }
244              
245             sub get_error {
246 2     2 1 17 my $self = shift;
247 2         4 return $self->error;
248             }
249              
250             sub clear_error {
251 722     722 1 769 my $self = shift;
252 722         1314 $self->error([]);
253             }
254              
255             ################################################################################
256             sub _set_default_parameters {
257 7     7   273 my $self = shift;
258 7         23 $self->set_intervals(1);
259 7         20 $self->set_span(1);
260 7         20 $self->set_start_day_of_week('MONDAY');
261 7         18 $self->set_start_day_of_month(1);
262 7         16 $self->set_start_month_of_year(1);
263 7         35 $self->_set_print_format('%04d-%02d-%02d');
264 7         15 $self->set_today_date();
265 7         17 $self->set_sliding_window(0);
266 7         23 $self->set_direction('-');
267 7         16 $self->clear_error();
268             }
269              
270             sub _set_passed_parameters {
271 7     7   503 my $self = shift;
272 7         7 my $hash = shift;
273 7 100       25 $self->set_type($hash->{type}) if exists $hash->{type};
274 7 50       23 $self->set_intervals($hash->{intervals}) if exists $hash->{intervals};
275 7 100       27 $self->set_span($hash->{span}) if exists $hash->{span};
276 7 100       23 $self->set_today_date($hash->{today_date}) if exists $hash->{today_date};
277 7 50       22 $self->set_direction($hash->{direction}) if exists $hash->{direction};
278             $self->set_start_day_of_week($hash->{start_day_of_week})
279 7 100       18 if exists $hash->{start_day_of_week};
280             $self->set_sliding_window($hash->{sliding_window})
281 7 50       25 if exists $hash->{sliding_window};
282             $self->set_start_day_of_month($hash->{start_day_of_month})
283 7 50       15 if exists $hash->{start_day_of_month};
284             $self->set_start_month_of_year($hash->{start_month_of_year})
285 7 50       19 if exists $hash->{start_month_of_year};
286             }
287              
288             sub _get_start_date {
289 715     715   507 my $self = shift;
290 715         793 my $direction = $self->get_direction;
291 715         3607 my @start = $self->_start_reference;
292 715         932 my $span = $self->get_span;
293 715         3574 my $intervals = $self->get_intervals;
294 715         3396 my @delta = $self->_delta_per_period;
295 715 100       1111 if ($direction eq '-') {
296 358         407 @delta = _negate(@delta);
297             }
298 715         480 my $map_factor;
299 715 100       820 if ($self->get_sliding_window) {
300 238 100       1387 $map_factor = ($direction eq '+') ? $intervals
301             : ($span + $intervals - 1)
302             ;
303             } else {
304 477         2593 $map_factor = $span * $intervals;
305             }
306 715         797 @delta = map { $_ * $map_factor } @delta;
  2145         2257  
307 715         963 @start = $self->_add_delta_ymd(@start, @delta);
308 715         974 return @start;
309             }
310              
311             sub _get_end_date {
312 715     715   722 my $self = shift;
313 715         668 my @start = @_;
314 715         783 my @delta = $self->_delta_ymd;
315 715         871 my @end = $self->_add_delta_ymd(@start,@delta);
316 715         906 return @end;
317             }
318              
319             sub _get_last_date {
320 715     715   792 my $self = shift;
321 715         632 my @end = @_;
322 715         832 @end = $self->_add_delta_ymd(@end,(0,0,-1));
323 715         879 return @end;
324             }
325              
326             sub _start_reference {
327 716     716   527 my $self = shift;
328 716         830 my @start = $self->get_today_date;
329 716         4386 my $type = $self->get_type;
330 716 100       4198 if ($type eq 'YEAR') {
    100          
    100          
    100          
    50          
331 168         181 my $start_moy = $self->get_start_month_of_year;
332 168 100       844 if ($start_moy > $start[1]) {
333 42         60 @start = $self->_add_delta_ymd(@start,(-1,0,0));
334             }
335 168         148 $start[1] = $start_moy;
336 168         155 $start[2] = 1;
337             } elsif ($type eq 'QUARTER') {
338 42         54 $start[1] -= ( ( $start[1] - 1 ) % 3 );
339 42         36 $start[2] = 1;
340             } elsif ($type eq 'MONTH') {
341 170         195 my $start_dom = $self->get_start_day_of_month;
342 170 100       841 if ($start_dom > $start[2]) {
343 42         68 @start = $self->_add_delta_ymd(@start,(0,-1,0));
344             }
345 170         199 $start[2] = $start_dom;
346             } elsif ($type eq 'WEEK') {
347             ## Calculate the "Monday" of the current week, and add the number of days to get to
348             ## desired start date. If that start day-of-week is "after" the "current" day-of-week,
349             ## that start date will be in the future. Will need to subtract a week.
350 294         334 my $start_dow = $self->get_start_day_of_week;
351 294         1623 my $today_dow = Day_of_Week(@start);
352 294         927 @start = $self->_add_delta_ymd(Monday_of_Week(Week_of_Year(@start)),(0,0,$start_dow - 1));
353             ## NEED MORE HERE _ this is just "monday" at this point
354 294 100       517 if ($today_dow < $start_dow) {
355 168         259 @start = $self->_add_delta_ymd(@start,(0,0,-7));
356             }
357             } elsif ($type eq 'DAY') {
358             ## No change
359             }
360 716         1077 return @start;
361             }
362              
363             sub _set_start_dow_name {
364 310     310   253 my ($self,$start_dow_name) = @_;
365 310         440 $self->start_dow_name($start_dow_name);
366             }
367              
368             sub _get_start_dow_name {
369 2     2   256 my $self = shift;
370 2         4 return $self->start_dow_name;
371             }
372              
373             sub _set_print_format {
374 9     9   466 my ($self, $format) = @_;
375             ## valid: %s, %d, '/', '-', ' ', ':'
376 9         10 my $validate = $format;
377 9         92 $validate =~ s/[\/\- :]//g;
378 9         48 $validate =~ s/%[0-9]*d//g;
379 9 100       25 if ($validate) {
380 1         5 $self->set_error("Suspect output format: \"$format\"");
381 1         8 return 0;
382             }
383 8         25 $self->print_format($format);
384 8         60 return 1;
385             }
386              
387             sub _get_print_format {
388 2144     2144   1629 my $self = shift;
389 2144         2589 return $self->print_format;
390             }
391              
392             sub _delta_ymd {
393 716     716   473 my $self = shift;
394 716         718 my $span = $self->get_span;
395 716         3628 my @single_delta = $self->_delta_per_period;
396 716         692 my @total_delta = map { $span * $_ } @single_delta;
  2148         1969  
397 716         890 return @total_delta;
398             }
399              
400             sub _delta_per_period {
401 1432     1432   965 my $self = shift;
402 1432         1417 my $type = $self->get_type;
403 1432 100       8399 return $type eq 'YEAR' ? (1,0,0)
    100          
    100          
    100          
404             : $type eq 'QUARTER' ? (0,3,0)
405             : $type eq 'MONTH' ? (0,1,0)
406             : $type eq 'WEEK' ? (0,0,7)
407             : (0,0,1)
408             }
409              
410             sub _negate {
411 359     359   655 my @negatives = map { -1 * $_ } @_;
  1077         1140  
412 359         558 return @negatives;
413             }
414              
415             sub _date_to_array {
416 10     10   949 my ($self,@date) = @_;
417 10 100 66     97 if (scalar(@date) == 1 and $date[0] =~ /^(\d+)-(\d+)-(\d+)$/) {
418 7         31 @date = ($1,$2,$3);
419             }
420 10 100 33     201 if ((scalar(@date) == 3) and
      33        
      33        
      66        
421             ($date[0] =~ /^\d+$/) and
422             ($date[1] =~ /^\d+$/) and
423             ($date[2] =~ /^\d+$/) and
424             (check_date(@date))) {
425 9         30 return (@date);
426             }
427             else {
428 1         7 $self->set_error("Invalid \"today\": " . join("-",@date));
429             }
430 1         9 return ();
431             }
432              
433             sub _array_to_date {
434 2143     2143   2115 my ($self, @date) = @_;
435 2143         2031 my $format = $self->_get_print_format();
436 2143         12551 return sprintf $format, @date;
437             }
438              
439             sub _add_delta_ymd {
440 2693     2693   3116 my ($self,@date_info) = @_;
441 2693         1859 my @new_date = ();
442 2693         1897 eval {
443 2693         4806 @new_date = Add_Delta_YMD(@date_info);
444             };
445 2693 100       3650 if ($@) {
446 1         4 my $errstring = sprintf "Cannot calculate date diff: (%d,%d,%d) + (%d,%d,%d)", @date_info;
447 1         3 $self->set_error($errstring);
448             }
449 2693         3732 return @new_date;
450             }
451              
452             1;
453              
454             __END__