Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | |
2 | ############################################################################### | |
3 | ## ## | |
4 | ## Copyright (c) 2000 - 2002 by Steffen Beyer. ## | |
5 | ## All rights reserved. ## | |
6 | ## ## | |
7 | ## This package is free software; you can redistribute it ## | |
8 | ## and/or modify it under the same terms as Perl itself. ## | |
9 | ## ## | |
10 | ############################################################################### | |
11 | ||
12 | package Date::Calendar::Year; | |
13 | ||
14 | use strict; | |
15 | use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION ); | |
16 | ||
17 | require Exporter; | |
18 | ||
19 | @ISA = qw(Exporter); | |
20 | ||
21 | @EXPORT = qw(); | |
22 | ||
23 | @EXPORT_OK = qw( check_year empty_period ); | |
24 | ||
25 | %EXPORT_TAGS = (all => [@EXPORT_OK]); | |
26 | ||
27 | $VERSION = '5.3'; | |
28 | ||
29 | use Bit::Vector; | |
30 | use Carp::Clan qw(^Date::); | |
31 | use Date::Calc::Object qw(:ALL); | |
32 | ||
33 | sub check_year | |
34 | { | |
35 | my($year) = shift_year(\@_); | |
36 | ||
37 | if (($year < 1583) || ($year > 2299)) | |
38 | { | |
39 | croak("given year ($year) out of range [1583..2299]"); | |
40 | } | |
41 | } | |
42 | ||
43 | sub empty_period | |
44 | { | |
45 | carp("dates interval is empty") if ($^W); | |
46 | } | |
47 | ||
48 | sub _invalid_ | |
49 | { | |
50 | my($item,$name) = @_; | |
51 | ||
52 | croak("date '$item' for day '$name' is invalid"); | |
53 | } | |
54 | ||
55 | sub _check_init_date_ | |
56 | { | |
57 | my($item,$name,$year,$yy,$mm,$dd) = @_; | |
58 | ||
59 | &_invalid_($item,$name) | |
60 | unless (($year == $yy) && (check_date($yy,$mm,$dd))); | |
61 | } | |
62 | ||
63 | sub _check_callback_date_ | |
64 | { | |
65 | my($name,$year,$yy,$mm,$dd) = @_; | |
66 | ||
67 | croak("callback function for day '$name' returned invalid date") | |
68 | unless (($year == $yy) && (check_date($yy,$mm,$dd))); | |
69 | } | |
70 | ||
71 | sub _set_date_ | |
72 | { | |
73 | my($self,$name,$yy,$mm,$dd,$flag) = @_; | |
74 | my($index); | |
75 | ||
76 | $flag ||= ''; | |
77 | $index = $self->date2index($yy,$mm,$dd); | |
78 | if ($flag ne '#') | |
79 | { | |
80 | if ($flag eq ':') { ${$self}{'HALF'}->Bit_On( $index ); } | |
81 | else { ${$self}{'FULL'}->Bit_On( $index ); } | |
82 | } | |
83 | $self->{'TAGS'}{$index}{$name} = 1; | |
84 | } | |
85 | ||
86 | sub _set_fixed_date_ | |
87 | { | |
88 | my($self) = shift; | |
89 | my($item) = shift; | |
90 | my($name) = shift; | |
91 | my($year) = shift; | |
92 | ||
93 | if ($_[1] =~ /^[a-zA-Z]+$/) | |
94 | { | |
95 | &_invalid_($item,$name) unless ($_[1] = Decode_Month($_[1])); | |
96 | } | |
97 | &_check_init_date_($item,$name,$year,@_); | |
98 | &_set_date_($self,$name,@_); | |
99 | } | |
100 | ||
101 | sub date2index | |
102 | { | |
103 | my($self) = shift; | |
104 | my($yy,$mm,$dd) = shift_date(\@_); | |
105 | my($year,$index); | |
106 | ||
107 | $year = ${$self}{'YEAR'}; | |
108 | if ($yy != $year) | |
109 | { | |
110 | croak("given year ($yy) != object's year ($year)"); | |
111 | } | |
112 | if ((check_date($yy,$mm,$dd)) && | |
113 | (($index = (Date_to_Days($yy,$mm,$dd) - ${$self}{'BASE'})) >= 0) && | |
114 | ($index < ${$self}{'DAYS'})) | |
115 | { | |
116 | return $index; | |
117 | } | |
118 | else { croak("invalid date ($yy,$mm,$dd)"); } | |
119 | } | |
120 | ||
121 | sub index2date | |
122 | { | |
123 | my($self,$index) = @_; | |
124 | my($year,$yy,$mm,$dd); | |
125 | ||
126 | $year = ${$self}{'YEAR'}; | |
127 | $yy = $year; | |
128 | $mm = 1; | |
129 | $dd = 1; | |
130 | if (($index == 0) || | |
131 | (($index > 0) && | |
132 | ($index < ${$self}{'DAYS'}) && | |
133 | (($yy,$mm,$dd) = Add_Delta_Days($year,1,1, $index)) && | |
134 | ($yy == $year))) | |
135 | { | |
136 | return Date::Calc->new($yy,$mm,$dd); | |
137 | } | |
138 | else { croak("invalid index ($index)"); } | |
139 | } | |
140 | ||
141 | sub new | |
142 | { | |
143 | my($class) = shift; | |
144 | my($year) = shift_year(\@_); | |
145 | my($profile) = shift; | |
146 | my($language) = shift || 0; | |
147 | my($self); | |
148 | ||
149 | &check_year($year); | |
150 | $self = { }; | |
151 | $class = ref($class) || $class || 'Date::Calendar::Year'; | |
152 | bless($self, $class); | |
153 | $self->init($year,$profile,$language); | |
154 | return $self; | |
155 | } | |
156 | ||
157 | sub init | |
158 | { | |
159 | my($self) = shift; | |
160 | my($year) = shift_year(\@_); | |
161 | my($profile) = shift; | |
162 | my($language) = shift || 0; | |
163 | my($days,$dow,$lang,$name,$item,$flag,$temp,$n); | |
164 | my(@easter,@date); | |
165 | ||
166 | &check_year($year); | |
167 | croak("given profile is not a HASH ref") unless (ref($profile) eq 'HASH'); | |
168 | $days = Days_in_Year($year,12); | |
169 | ${$self}{'YEAR'} = $year; | |
170 | ${$self}{'DAYS'} = $days; | |
171 | ${$self}{'BASE'} = Date_to_Days($year,1,1); | |
172 | ${$self}{'TAGS'} = { }; | |
173 | ${$self}{'HALF'} = Bit::Vector->new($days); | |
174 | ${$self}{'FULL'} = Bit::Vector->new($days); | |
175 | ${$self}{'WORK'} = Bit::Vector->new($days); | |
176 | $dow = Day_of_Week($year,1,1); | |
177 | $dow = 7 - $dow if ($dow != 7); | |
178 | $dow--; | |
179 | while ($dow < $days) | |
180 | { | |
181 | ${$self}{'FULL'}->Bit_On( $dow ); # Saturday | |
182 | ${$self}{'FULL'}->Bit_On( $dow ) if (++$dow < $days); # Sunday | |
183 | $dow += 6; | |
184 | } | |
185 | @easter = Easter_Sunday($year); | |
186 | if ($language =~ /^\d+$/) | |
187 | { | |
188 | if (($language > 0) and ($language <= Languages())) | |
189 | { $lang = Language($language); } | |
190 | else | |
191 | { $lang = Language(1); } | |
192 | } | |
193 | else | |
194 | { | |
195 | if ($language = Decode_Language($language)) | |
196 | { $lang = Language($language); } | |
197 | else | |
198 | { $lang = Language(1); } | |
199 | } | |
200 | foreach $name (keys %{$profile}) | |
201 | { | |
202 | @date = (); | |
203 | $item = ${$profile}{$name}; | |
204 | if (ref($item)) | |
205 | { | |
206 | if (ref($item) eq 'CODE') | |
207 | { | |
208 | if (@date = &$item($year,$name)) | |
209 | { | |
210 | &_check_callback_date_($name,$year,@date); | |
211 | &_set_date_($self,$name,@date); | |
212 | } | |
213 | } | |
214 | else { croak("value for day '$name' is not a CODE ref"); } | |
215 | } | |
216 | elsif ($item =~ /^ ([#:]?) ([+-]\d+) $/x) | |
217 | { | |
218 | $flag = $1; | |
219 | $temp = $2; | |
220 | if ($temp == 0) { @date = @easter; } | |
221 | else { @date = Add_Delta_Days(@easter, $temp); } | |
222 | &_check_init_date_($item,$name,$year,@date); | |
223 | &_set_date_($self,$name,@date,$flag); | |
224 | } | |
225 | elsif (($item =~ /^ ([#:]?) (\d+) \. (\d+) \.? $/x) || | |
226 | ($item =~ /^ ([#:]?) (\d+) \.? ([a-zA-Z]+) \.? $/x) || | |
227 | ($item =~ /^ ([#:]?) (\d+) - (\d+|[a-zA-Z]+) -? $/x)) | |
228 | { | |
229 | $flag = $1; | |
230 | @date = ($year,$3,$2); | |
231 | &_set_fixed_date_($self,$item,$name,$year,@date,$flag); | |
232 | } | |
233 | elsif (($item =~ /^ ([#:]?) (\d+) \/ (\d+) $/x) || | |
234 | ($item =~ /^ ([#:]?) ([a-zA-Z]+) \/? (\d+) $/x)) | |
235 | { | |
236 | $flag = $1; | |
237 | @date = ($year,$2,$3); | |
238 | &_set_fixed_date_($self,$item,$name,$year,@date,$flag); | |
239 | } | |
240 | elsif (($item =~ /^ ([#:]?) ([1-5]) ([a-zA-Z]+) (\d+) $/x) || | |
241 | ($item =~ /^ ([#:]?) ([1-5]) \/ ([1-7]|[a-zA-Z]+) \/ (\d+|[a-zA-Z]+) $/x)) | |
242 | { | |
243 | $flag = $1; | |
244 | $n = $2; | |
245 | $dow = $3; | |
246 | $temp = $4; | |
247 | if ($dow =~ /^[a-zA-Z]+$/) | |
248 | { | |
249 | &_invalid_($item,$name) unless ($dow = Decode_Day_of_Week($dow)); | |
250 | } | |
251 | if ($temp =~ /^[a-zA-Z]+$/) | |
252 | { | |
253 | &_invalid_($item,$name) unless ($temp = Decode_Month($temp)); | |
254 | } | |
255 | else | |
256 | { | |
257 | &_invalid_($item,$name) unless (($temp > 0) && ($temp < 13)); | |
258 | } | |
259 | unless (@date = Nth_Weekday_of_Month_Year($year,$temp,$dow,$n)) | |
260 | { | |
261 | if ($n == 5) | |
262 | { | |
263 | &_invalid_($item,$name) | |
264 | unless (@date = Nth_Weekday_of_Month_Year($year,$temp,$dow,4)); | |
265 | } | |
266 | else { &_invalid_($item,$name); } | |
267 | } | |
268 | &_set_date_($self,$name,@date,$flag); | |
269 | } | |
270 | else | |
271 | { | |
272 | croak("unrecognized date '$item' for day '$name'"); | |
273 | } | |
274 | } | |
275 | ${$self}{'HALF'}->AndNot( ${$self}{'HALF'}, ${$self}{'FULL'} ); | |
276 | Language($lang); | |
277 | } | |
278 | ||
279 | sub vec_full # full holidays | |
280 | { | |
281 | my($self) = @_; | |
282 | ||
283 | return ${$self}{'FULL'}; | |
284 | } | |
285 | ||
286 | sub vec_half # half holidays | |
287 | { | |
288 | my($self) = @_; | |
289 | ||
290 | return ${$self}{'HALF'}; | |
291 | } | |
292 | ||
293 | sub vec_work # work space | |
294 | { | |
295 | my($self) = @_; | |
296 | ||
297 | return ${$self}{'WORK'}; | |
298 | } | |
299 | ||
300 | sub val_days | |
301 | { | |
302 | my($self) = @_; | |
303 | ||
304 | return ${$self}{'DAYS'}; | |
305 | } | |
306 | ||
307 | sub val_base | |
308 | { | |
309 | my($self) = @_; | |
310 | ||
311 | return ${$self}{'BASE'}; | |
312 | } | |
313 | ||
314 | sub val_year | |
315 | { | |
316 | my($self) = @_; | |
317 | ||
318 | return ${$self}{'YEAR'}; | |
319 | } | |
320 | ||
321 | sub year # as a shortcut and to enable shift_year | |
322 | { | |
323 | my($self) = @_; | |
324 | ||
325 | return ${$self}{'YEAR'}; | |
326 | } | |
327 | ||
328 | sub labels | |
329 | { | |
330 | my($self) = shift; | |
331 | my(@date); | |
332 | my($index); | |
333 | my(%result); | |
334 | ||
335 | if (@_) | |
336 | { | |
337 | @date = shift_date(\@_); | |
338 | $index = $self->date2index(@date); | |
339 | if (defined $self->{'TAGS'}{$index}) | |
340 | { | |
341 | if (defined wantarray and wantarray) | |
342 | { | |
343 | return | |
344 | ( | |
345 | Day_of_Week_to_Text(Day_of_Week(@date)), | |
346 | keys(%{$self->{'TAGS'}{$index}}) | |
347 | ); | |
348 | } | |
349 | else | |
350 | { | |
351 | return 1 + scalar( keys(%{$self->{'TAGS'}{$index}}) ); | |
352 | } | |
353 | } | |
354 | else | |
355 | { | |
356 | if (defined wantarray and wantarray) | |
357 | { | |
358 | return( Day_of_Week_to_Text(Day_of_Week(@date)) ); | |
359 | } | |
360 | else | |
361 | { | |
362 | return 1; | |
363 | } | |
364 | } | |
365 | } | |
366 | else | |
367 | { | |
368 | local($_); | |
369 | %result = (); | |
370 | foreach $index (keys %{$self->{'TAGS'}}) | |
371 | { | |
372 | grep( $result{$_} = 0, keys(%{$self->{'TAGS'}{$index}}) ); | |
373 | } | |
374 | if (defined wantarray and wantarray) | |
375 | { | |
376 | return( keys %result ); | |
377 | } | |
378 | else | |
379 | { | |
380 | return scalar( keys %result ); | |
381 | } | |
382 | } | |
383 | } | |
384 | ||
385 | sub search | |
386 | { | |
387 | my($self,$pattern) = @_; | |
388 | my($index,$label,$upper); | |
389 | my(@result); | |
390 | ||
391 | local($_); | |
392 | @result = (); | |
393 | $pattern = ISO_UC($pattern); | |
394 | foreach $index (keys %{$self->{'TAGS'}}) | |
395 | { | |
396 | LABEL: | |
397 | foreach $label (keys %{$self->{'TAGS'}{$index}}) | |
398 | { | |
399 | $upper = ISO_UC($label); | |
400 | if (index($upper,$pattern) >= $[) | |
401 | { | |
402 | push( @result, $index ); | |
403 | last LABEL; | |
404 | } | |
405 | } | |
406 | } | |
407 | return( map( $self->index2date($_), sort {$a<=>$b} @result ) ); | |
408 | } | |
409 | ||
410 | sub _interval_workdays_ | |
411 | { | |
412 | my($self,$lower,$upper) = @_; | |
413 | my($work,$full,$half,$days); | |
414 | ||
415 | $work = ${$self}{'WORK'}; | |
416 | $full = ${$self}{'FULL'}; | |
417 | $half = ${$self}{'HALF'}; | |
418 | $work->Empty(); | |
419 | $work->Interval_Fill($lower,$upper); | |
420 | $work->AndNot($work,$full); | |
421 | $days = $work->Norm(); | |
422 | $work->And($work,$half); | |
423 | $days -= $work->Norm() * 0.5; | |
424 | return $days; | |
425 | } | |
426 | ||
427 | sub _delta_workdays_ | |
428 | { | |
429 | my($self,$lower_index,$upper_index,$include_lower,$include_upper) = @_; | |
430 | my($days); | |
431 | ||
432 | $days = ${$self}{'DAYS'}; | |
433 | if (($lower_index < 0) || ($lower_index >= $days)) | |
434 | { | |
435 | croak("invalid lower index ($lower_index)"); | |
436 | } | |
437 | if (($upper_index < 0) || ($upper_index >= $days)) | |
438 | { | |
439 | croak("invalid upper index ($upper_index)"); | |
440 | } | |
441 | if ($lower_index > $upper_index) | |
442 | { | |
443 | croak("lower index ($lower_index) > upper index ($upper_index)"); | |
444 | } | |
445 | $lower_index++ unless ($include_lower); | |
446 | $upper_index-- unless ($include_upper); | |
447 | if (($upper_index < 0) || | |
448 | ($lower_index >= $days) || | |
449 | ($lower_index > $upper_index)) | |
450 | { | |
451 | &empty_period(); | |
452 | return 0; | |
453 | } | |
454 | return $self->_interval_workdays_($lower_index,$upper_index); | |
455 | } | |
456 | ||
457 | sub delta_workdays | |
458 | { | |
459 | my($self) = shift; | |
460 | my($yy1,$mm1,$dd1) = shift_date(\@_); | |
461 | my($yy2,$mm2,$dd2) = shift_date(\@_); | |
462 | my($including1,$including2) = (shift,shift); | |
463 | my($index1,$index2); | |
464 | ||
465 | $index1 = $self->date2index($yy1,$mm1,$dd1); | |
466 | $index2 = $self->date2index($yy2,$mm2,$dd2); | |
467 | if ($index1 > $index2) | |
468 | { | |
469 | return -$self->_delta_workdays_( | |
470 | $index2,$index1,$including2,$including1); | |
471 | } | |
472 | else | |
473 | { | |
474 | return $self->_delta_workdays_( | |
475 | $index1,$index2,$including1,$including2); | |
476 | } | |
477 | } | |
478 | ||
479 | sub _move_forward_ | |
480 | { | |
481 | my($self,$index,$rest,$sign) = @_; | |
482 | my($limit,$year,$full,$half,$loop,$min,$max); | |
483 | ||
484 | if ($sign == 0) | |
485 | { | |
486 | return( $self->index2date($index), $rest, 0 ); | |
487 | } | |
488 | $limit = ${$self}{'DAYS'} - 1; | |
489 | $year = ${$self}{'YEAR'}; | |
490 | $full = ${$self}{'FULL'}; | |
491 | $half = ${$self}{'HALF'}; | |
492 | $loop = 1; | |
493 | if ($sign > 0) | |
494 | { | |
495 | $rest = -$rest if ($rest < 0); | |
496 | while ($loop) | |
497 | { | |
498 | $loop = 0; | |
499 | if ($full->bit_test($index) && | |
500 | (($min,$max) = $full->Interval_Scan_inc($index)) && | |
501 | ($min == $index)) | |
502 | { | |
503 | if ($max >= $limit) | |
504 | { | |
505 | return( Date::Calc->new(++$year,1,1), $rest, +1 ); | |
506 | } | |
507 | else { $index = $max + 1; } | |
508 | } | |
509 | if ($half->bit_test($index)) | |
510 | { | |
511 | if ($rest >= 0.5) { $rest -= 0.5; $index++; $loop = 1; } | |
512 | } | |
513 | elsif ($rest >= 1.0) { $rest -= 1.0; $index++; $loop = 1; } | |
514 | if ($loop && ($index > $limit)) | |
515 | { | |
516 | return( Date::Calc->new(++$year,1,1), $rest, +1 ); | |
517 | } | |
518 | } | |
519 | return( $self->index2date($index), $rest, 0 ); | |
520 | } | |
521 | else # ($sign < 0) | |
522 | { | |
523 | $rest = -$rest if ($rest > 0); | |
524 | while ($loop) | |
525 | { | |
526 | $loop = 0; | |
527 | if ($full->bit_test($index) && | |
528 | (($min,$max) = $full->Interval_Scan_dec($index)) && | |
529 | ($max == $index)) | |
530 | { | |
531 | if ($min <= 0) | |
532 | { | |
533 | return( Date::Calc->new(--$year,12,31), $rest, -1 ); | |
534 | } | |
535 | else { $index = $min - 1; } | |
536 | } | |
537 | if ($half->bit_test($index)) | |
538 | { | |
539 | if ($rest <= -0.5) { $rest += 0.5; $index--; $loop = 1; } | |
540 | } | |
541 | elsif ($rest <= -1.0) { $rest += 1.0; $index--; $loop = 1; } | |
542 | if ($loop && ($index < 0)) | |
543 | { | |
544 | return( Date::Calc->new(--$year,12,31), $rest, -1 ); | |
545 | } | |
546 | } | |
547 | return( $self->index2date($index), $rest, 0 ); | |
548 | } | |
549 | } | |
550 | ||
551 | sub add_delta_workdays | |
552 | { | |
553 | my($self) = shift; | |
554 | my($yy,$mm,$dd) = shift_date(\@_); | |
555 | my($days) = shift; | |
556 | my($sign) = shift; | |
557 | my($index,$full,$half,$limit,$diff,$guess); | |
558 | ||
559 | $index = $self->date2index($yy,$mm,$dd); # check date | |
560 | if ($sign == 0) | |
561 | { | |
562 | return( Date::Calc->new($yy,$mm,$dd), $days, 0 ); | |
563 | } | |
564 | $days = -$days if ($days < 0); | |
565 | if ($days < 2) # other values possible for fine-tuning optimal speed | |
566 | { | |
567 | return( $self->_move_forward_($index,$days,$sign) ); | |
568 | } | |
569 | # else sufficiently large distance | |
570 | $full = ${$self}{'FULL'}; | |
571 | $half = ${$self}{'HALF'}; | |
572 | if ($sign > 0) | |
573 | { | |
574 | # First, check against whole rest of year: | |
575 | $limit = ${$self}{'DAYS'} - 1; | |
576 | $diff = $self->_interval_workdays_($index,$limit); | |
577 | if ($days >= $diff) | |
578 | { | |
579 | $days -= $diff; | |
580 | return( Date::Calc->new(++$yy,1,1), $days, +1 ); | |
581 | } | |
582 | # else ($days < $diff) | |
583 | # Now calculate proportional jump (approximatively): | |
584 | $guess = $index + int($days * ($limit-$index+1) / $diff); | |
585 | $guess = $limit if ($guess > $limit); | |
586 | if ($index + 2 > $guess) # again, other values possible for fine-tuning | |
587 | { | |
588 | return( $self->_move_forward_($index,$days,+1) ); | |
589 | } | |
590 | # else sufficiently long jump | |
591 | $diff = $self->_interval_workdays_($index,$guess-1); | |
592 | while ($days < $diff) # reverse gear (jumped too far) | |
593 | { | |
594 | $guess--; | |
595 | unless ($full->bit_test($guess)) | |
596 | { | |
597 | if ($half->bit_test($guess)) { $diff -= 0.5; } | |
598 | else { $diff -= 1.0; } | |
599 | } | |
600 | } | |
601 | # Now move in original direction: | |
602 | $days -= $diff; | |
603 | return( $self->_move_forward_($guess,$days,+1) ); | |
604 | } | |
605 | else # ($sign < 0) | |
606 | { | |
607 | # First, check against whole rest of year: | |
608 | $limit = 0; | |
609 | $diff = $self->_interval_workdays_($limit,$index); | |
610 | if ($days >= $diff) | |
611 | { | |
612 | $days -= $diff; | |
613 | return( Date::Calc->new(--$yy,12,31), -$days, -1 ); | |
614 | } | |
615 | # else ($days < $diff) | |
616 | # Now calculate proportional jump (approximatively): | |
617 | $guess = $index - int($days * ($index+1) / $diff); | |
618 | $guess = $limit if ($guess < $limit); | |
619 | if ($guess > $index - 2) # again, other values possible for fine-tuning | |
620 | { | |
621 | return( $self->_move_forward_($index,-$days,-1) ); | |
622 | } | |
623 | # else sufficiently long jump | |
624 | $diff = $self->_interval_workdays_($guess+1,$index); | |
625 | while ($days < $diff) # reverse gear (jumped too far) | |
626 | { | |
627 | $guess++; | |
628 | unless ($full->bit_test($guess)) | |
629 | { | |
630 | if ($half->bit_test($guess)) { $diff -= 0.5; } | |
631 | else { $diff -= 1.0; } | |
632 | } | |
633 | } | |
634 | # Now move in original direction: | |
635 | $days -= $diff; | |
636 | return( $self->_move_forward_($guess,-$days,-1) ); | |
637 | } | |
638 | } | |
639 | ||
640 | sub is_full | |
641 | { | |
642 | my($self) = shift; | |
643 | my(@date) = shift_date(\@_); | |
644 | ||
645 | return $self->vec_full->bit_test( $self->date2index(@date) ); | |
646 | } | |
647 | ||
648 | sub is_half | |
649 | { | |
650 | my($self) = shift; | |
651 | my(@date) = shift_date(\@_); | |
652 | ||
653 | return $self->vec_half->bit_test( $self->date2index(@date) ); | |
654 | } | |
655 | ||
656 | sub is_work | |
657 | { | |
658 | my($self) = shift; | |
659 | my(@date) = shift_date(\@_); | |
660 | ||
661 | return $self->vec_work->bit_test( $self->date2index(@date) ); | |
662 | } | |
663 | ||
664 | 1; | |
665 | ||
666 | __END__ | |
667 |