File Coverage

blib/lib/Date/Business.pm
Criterion Covered Total %
statement 33 170 19.4
branch 0 102 0.0
condition 0 46 0.0
subroutine 11 30 36.6
pod 0 19 0.0
total 44 367 11.9


line stmt bran cond sub pod time code
1             # $Id: Business.pm,v 1.1 1999/12/28 22:05:38 desimr Exp desimr $
2             #
3             # $Log: Business.pm,v $
4             #
5             # Revision 1.2 1999/11/25 01:15:31 desimr
6             # added support for Holidays
7             #
8             # Revision 1.1 1999/11/23 18:11:55 desimr
9             # Business date package
10             #
11             # (c) 1999 Morgan Stanley Dean Witter and Co.
12             # See LICENSE for terms of distribution.
13             #
14             # Author: Richard DeSimine
15             #
16             package Date::Business;
17              
18 1     1   6750 use strict;
  1         27  
  1         43  
19 1     1   762 use POSIX;
  1         9331  
  1         7  
20 1     1   5071 use Time::Local;
  1         4004  
  1         112  
21 1     1   13 use vars qw($VERSION @ISA @EXPORT);
  1         2  
  1         216  
22            
23             require Exporter;
24             require DynaLoader;
25            
26             @ISA = qw(Exporter DynaLoader);
27              
28             our $VERSION = '1.3'; # VERSION
29            
30             #RCS/CVS Version
31             my($RCSVERSION) = do {
32             my @r = (q$Revision: 1.1 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r
33             };
34              
35 1     1   11 use constant DAY => 86_400;
  1         5  
  1         111  
36 1     1   7 use constant WEEK => DAY * 7;
  1         2  
  1         67  
37 1     1   6 use constant E_SUNDAY => DAY * 3; # offset from Epoch Day Of Week
  1         3  
  1         59  
38 1     1   24 use constant THURSDAY => 4; # day of week
  1         5  
  1         59  
39 1     1   6 use constant FRIDAY => 5; # day of week
  1         2  
  1         82  
40 1     1   7 use constant SATURDAY => 6; # day of week
  1         2  
  1         126  
41 1     1   9 use constant SUNDAY => 0; # day of week
  1         2  
  1         2748  
42              
43             # create a new object with the specified date
44             # an offset in business days may be provided
45             sub new($;$$$) {
46 0     0 0   my($class) = shift;
47 0           my(%params) = @_;
48            
49 0           my($date) = $params{DATE}; # string or Date object
50 0           my($offset) = $params{OFFSET}; # business days
51            
52 0           bless my $self = {'val' => 0}, $class;
53 0 0         $self->{FORCE} = $params{FORCE} if (defined($params{FORCE}));
54 0 0         $self->{HOLIDAY} = $params{HOLIDAY} if (ref($params{HOLIDAY}) eq 'CODE');
55            
56             # is the date parameter another Date::Business object?
57 0 0         if (ref($date) eq __PACKAGE__) {
58 0           $self->{val} = $date->{val};
59 0 0 0       $self->{FORCE} ||= $date->{FORCE} if (defined($date->{FORCE}));
60 0 0 0       $self->{HOLIDAY} ||= $date->{HOLIDAY} if (ref($date->{HOLIDAY}) eq 'CODE');
61             } else {
62             # if not a Date::Business object is it a date string?
63 0 0 0       if (defined($date) && length($date) != 0) {
64 0           $self->{'val'} = image2value($date);
65             } else {
66             # else use current localtime
67 0           my($lt) = timegm(localtime());
68 0           $self->{'val'} = $lt - ($lt % DAY);
69             }
70             }
71            
72             # compute offset if specified
73 0 0         if (defined($offset)) {
74 0 0         $self->addb($offset) if ($offset > 0);
75 0 0         $self->subb(-$offset) if ($offset < 0);
76             } else {
77             # if the date was initialized with a weekend or holiday
78             # and the FORCE option is set, force it to the 'next'
79             # or 'prev' business day
80 0 0         if (defined($params{FORCE})) {
81 0 0 0       if ($self->day_of_week == SATURDAY || $self->day_of_week == SUNDAY ||
      0        
      0        
82             (ref($self->{HOLIDAY}) eq 'CODE' &&
83             $self->{HOLIDAY}->($self->image, $self->image))) {
84 0 0         $self->prevb if ($self->{FORCE} eq 'prev');
85 0 0         $self->nextb if ($self->{FORCE} eq 'next');
86             }
87             }
88             }
89 0           return $self;
90             }
91              
92             sub image2value($;$) {
93 0     0 0   my($image) = @_;
94              
95 0           $image =~ m/(....)(..)(..)/;
96 0           return timegm(0, 0, 0, $3, ($2-1), $1 - 1900);
97             }
98              
99             sub value($) {
100 0     0 0   my($self) = @_;
101 0           return $self->{'val'};
102             }
103              
104             sub image($) {
105 0     0 0   my($self) = @_;
106 0           return POSIX::strftime("%Y%m%d", gmtime($self->{'val'}));
107             }
108              
109             sub next(;$) {
110 0     0 0   my($self, $n) = @_;
111 0 0         $n = 1 if (!defined($n));
112 0           $self->{'val'} += DAY * $n;
113             }
114              
115             sub prev(;$) {
116 0     0 0   my($self, $n) = @_;
117 0 0         $n = 1 if (!defined($n));
118 0           $self->{'val'} -= (DAY * $n);
119             }
120              
121             sub datecmp($$) {
122 0     0 0   my($self, $other) = @_;
123              
124 0           return $self->{'val'} <=> $other->{'val'};
125             }
126              
127             sub eq($$) {
128 0     0 0   my($self, $other) = @_;
129              
130 0           return $self->{'val'} <=> $other->{'val'};
131             }
132              
133             sub gt($$) {
134 0     0 0   my($self, $other) = @_;
135 0           return $self->{'val'} > $other->{'val'};
136             }
137              
138             sub lt($$) {
139 0     0 0   my($self, $other) = @_;
140 0           return $self->{'val'} < $other->{'val'};
141             }
142              
143             sub add($$) {
144 0     0 0   my($self, $inc) = @_;
145 0           $self->{'val'} += $inc * DAY;
146             }
147              
148             sub sub($$) {
149 0     0 0   my($self, $inc) = @_;
150 0           $self->{'val'} -= $inc * DAY;
151             }
152              
153             sub diff($$) {
154 0     0 0   my($self, $other) = @_;
155              
156 0           return int(($self->{'val'} - $other->{'val'}) / DAY);
157             }
158              
159             sub day_of_week($$) {
160 0     0 0   my($self) = @_;
161 0           return (gmtime($self->{'val'}))[6];
162             }
163              
164              
165             # business date functions
166             sub nextb() {
167 0     0 0   my($self) = @_;
168 0           $self->addb(1);
169             }
170              
171             sub prevb() {
172 0     0 0   my($self) = @_;
173 0           $self->subb(1);
174             }
175              
176             # takes a reference to $self and a reference
177             # to an object of type Date::Business and returns
178             # the difference in business days
179             sub diffb($$;$$) {
180 0     0 0   my($self, $other, $force_self, $force_other) = @_;
181 0 0         return -1 if (!defined($other));
182 0           my($days, $o_val, $sval, $tmp, $dow);
183 0           my($sign) = 1;
184            
185 0   0       $force_self ||= 'prev';
186 0   0       $force_other ||= 'prev';
187              
188 0           $sval = $self->{val};
189 0           while ($force_self eq 'prev') {
190 0           $tmp = $sval;
191 0           $dow = (gmtime($sval))[6];
192 0 0         $sval -= 2 * DAY if ($dow == SUNDAY);
193 0 0         $sval -= 1 * DAY if ($dow == SATURDAY);
194             $sval -= 1 * DAY if (ref($self->{HOLIDAY}) eq 'CODE' &&
195 0 0 0       $self->{HOLIDAY}->(POSIX::strftime("%Y%m%d", gmtime($sval)),
196             POSIX::strftime("%Y%m%d", gmtime($sval))));
197 0 0         last if ($sval == $tmp);
198             }
199 0           while ($force_self eq 'next') {
200 0           $tmp = $sval;
201 0           $dow = (gmtime($sval))[6];
202 0 0         $sval += 1 * DAY if ($dow == SUNDAY);
203 0 0         $sval += 2 * DAY if ($dow == SATURDAY);
204             $sval += 1 * DAY if (ref($self->{HOLIDAY}) eq 'CODE' &&
205 0 0 0       $self->{HOLIDAY}->(POSIX::strftime("%Y%m%d", gmtime($sval)),
206             POSIX::strftime("%Y%m%d", gmtime($sval))));
207 0 0         last if ($sval == $tmp);
208             }
209            
210 0           $o_val = $other->{val};
211 0           while ($force_other eq 'prev') {
212 0           $tmp = $o_val;
213 0           $dow = (gmtime($o_val))[6];
214 0 0         $o_val -= 2 * DAY if ($dow == SUNDAY);
215 0 0         $o_val -= 1 * DAY if ($dow == SATURDAY);
216             $o_val -= 1 * DAY if (ref($other->{HOLIDAY}) eq 'CODE' &&
217 0 0 0       $other->{HOLIDAY}->(POSIX::strftime("%Y%m%d", gmtime($o_val)),
218             POSIX::strftime("%Y%m%d", gmtime($o_val))));
219 0 0         last if ($o_val == $tmp);
220             }
221 0           while ($force_other eq 'next') {
222 0           $tmp = $o_val;
223 0           $dow = (gmtime($o_val))[6];
224 0 0         $o_val += 1 * DAY if ($dow == SUNDAY);
225 0 0         $o_val += 2 * DAY if ($dow == SATURDAY);
226             $o_val += 1 * DAY if (ref($other->{HOLIDAY}) eq 'CODE' &&
227 0 0 0       $other->{HOLIDAY}->(POSIX::strftime("%Y%m%d", gmtime($o_val)),
228             POSIX::strftime("%Y%m%d", gmtime($o_val))));
229 0 0         last if ($o_val == $tmp);
230             }
231            
232 0 0         if ($sval < $o_val){
233 0           $sign = -1;
234             } else {
235 0           $tmp = $sval;
236 0           $sval = $o_val;
237 0           $o_val = $tmp;
238             }
239            
240 0           my($weeks) = int((($o_val - $sval)/WEEK)) * 5;
241 0           $days = ((($o_val + E_SUNDAY) / DAY) % 7) - ((($sval + E_SUNDAY)/ DAY) % 7);
242 0 0         $days += 5 if ($days < 0);
243              
244 0 0         if (ref($other->{HOLIDAY}) eq 'CODE') {
245 0           $days -= $self->{HOLIDAY}->(POSIX::strftime("%Y%m%d", gmtime($sval)),
246             POSIX::strftime("%Y%m%d", gmtime($o_val)));
247             }
248 0           return $sign * ($weeks + $days);
249             }
250              
251             # adds n business days
252             sub addb($$) {
253 0     0 0   my($self, $inc) = @_;
254              
255 0 0 0       return if ($inc == 0 || $inc < 0 && $self->subb(-$inc));
      0        
256              
257 0           my($start) = $self->{'val'};
258 0           my($weeks) = int($inc/5) * 7;
259 0           my($dow) = (($self->{'val'} + E_SUNDAY) / DAY) % 7;
260 0           my($days) = $inc % 5;
261 0 0         if ($dow > THURSDAY) {
262 0 0         $self->{'val'} -= 1 * DAY if ($dow == FRIDAY);
263 0 0         $self->{'val'} -= 2 * DAY if ($dow == SATURDAY);
264 0 0         $dow-- if ($days == 0);
265             }
266 0 0         $days += 2 if ($days + $dow > THURSDAY);
267 0           $self->{'val'} += ($weeks + $days) * DAY;
268              
269 0 0         if (ref($self->{HOLIDAY}) eq 'CODE') {
270 0           my($start_txt) = POSIX::strftime("%Y%m%d", gmtime($start + DAY));
271 0           my($numHolidays) = $self->{HOLIDAY}->($start_txt, $self->image);
272 0 0         $self->addb($numHolidays) if ($numHolidays);
273             }
274 0           return 1;
275             }
276              
277             # subs n business days
278             sub subb($$) {
279 0     0 0   my($self, $dec) = @_;
280              
281 0 0 0       return if ($dec == 0 || $dec < 0 && $self->addb(-$dec));
      0        
282              
283 0           my($start) = $self->{'val'};
284 0           my($weeks) = int($dec/5) * 7;
285 0           my($dow) = (($self->{'val'} + E_SUNDAY) / DAY) % 7;
286 0           my($days) = $dec % 5;
287 0 0         if ($dow > 4) {
288 0 0         $self->{'val'} += 2 * DAY if ($dow == FRIDAY);
289 0 0         $self->{'val'} += 1 * DAY if ($dow == SATURDAY);
290 0 0         $days += 2 if ($days);
291             } else {
292 0 0         $days += 2 if ($days > $dow);
293             }
294 0           $self->{'val'} -= ($weeks + $days) * DAY;
295              
296 0 0         if (ref($self->{HOLIDAY}) eq 'CODE') {
297 0           my($end_txt) = POSIX::strftime("%Y%m%d", gmtime($start - DAY));
298 0           my($numHolidays) = $self->{HOLIDAY}->($self->image, $end_txt);
299 0 0         $self->subb($numHolidays) if ($numHolidays);
300             }
301 0           return 1;
302             }
303             1;
304             __END__