Initial commit of OpenSPARC T2 design and verification files.
[OpenSPARC-T2-DV] / tools / perl-5.8.0 / lib / site_perl / 5.8.0 / sun4-solaris / Date / Calendar / Year.pm
CommitLineData
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
12package Date::Calendar::Year;
13
14use strict;
15use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION );
16
17require 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
29use Bit::Vector;
30use Carp::Clan qw(^Date::);
31use Date::Calc::Object qw(:ALL);
32
33sub 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
43sub empty_period
44{
45 carp("dates interval is empty") if ($^W);
46}
47
48sub _invalid_
49{
50 my($item,$name) = @_;
51
52 croak("date '$item' for day '$name' is invalid");
53}
54
55sub _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
63sub _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
71sub _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
86sub _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
101sub 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
121sub 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
141sub 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
157sub 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
279sub vec_full # full holidays
280{
281 my($self) = @_;
282
283 return ${$self}{'FULL'};
284}
285
286sub vec_half # half holidays
287{
288 my($self) = @_;
289
290 return ${$self}{'HALF'};
291}
292
293sub vec_work # work space
294{
295 my($self) = @_;
296
297 return ${$self}{'WORK'};
298}
299
300sub val_days
301{
302 my($self) = @_;
303
304 return ${$self}{'DAYS'};
305}
306
307sub val_base
308{
309 my($self) = @_;
310
311 return ${$self}{'BASE'};
312}
313
314sub val_year
315{
316 my($self) = @_;
317
318 return ${$self}{'YEAR'};
319}
320
321sub year # as a shortcut and to enable shift_year
322{
323 my($self) = @_;
324
325 return ${$self}{'YEAR'};
326}
327
328sub 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
385sub 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
410sub _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
427sub _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
457sub 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
479sub _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
551sub 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
640sub 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
648sub 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
656sub 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
6641;
665
666__END__
667