File Coverage

blib/lib/Date/Holidays/CA.pm
Criterion Covered Total %
statement 106 153 69.2
branch 12 34 35.2
condition 0 3 0.0
subroutine 25 41 60.9
pod 9 9 100.0
total 152 240 63.3


line stmt bran cond sub pod time code
1             # Date::Holidays::CA
2             #
3             # This module is free software! You can copy, modify, share and
4             # distribute it under the same license as Perl itself.
5             #
6             # Rick Scott
7             # rick@shadowspar.dyndns.org
8             #
9             # Sun Oct 25 14:32:20 EDT 2009
10              
11             =encoding utf8
12              
13             =head1 NAME
14              
15             Date::Holidays::CA - Holidays for Canadian locales
16              
17             =head1 SYNOPSIS
18              
19             # procedural approach
20            
21             use Date::Holidays::CA qw(:all);
22            
23             my ($year, $month, $day) = (localtime)[ 5, 4, 3 ];
24             $year += 1900;
25             $month += 1;
26            
27             print 'Woot!' if is_holiday($year, $month, $day, {province => 'BC'});
28            
29             my $calendar = holidays($year, {province => 'BC'});
30             print $calendar->('0701'); # "Canada Day/FĂȘte du Canada"
31            
32            
33             # object-oriented approach
34            
35             use DateTime;
36             use Date::Holidays::CA;
37            
38             my $dhc = Date::Holidays::CA->new({ province => 'QC' });
39            
40             print 'Woot!' if $dhc->is_holiday(DateTime->today);
41            
42             my $calendar = $dhc->holidays_dt(DateTime->today->year);
43             print join keys %$calendar, "\n"; # lists holiday names for QC
44              
45              
46              
47             =head1 DESCRIPTION
48              
49             Date::Holidays::CA determines public holidays for Canadian jurisdictions.
50             Its interface is a superset of that provided by Date::Holidays -- read
51             on for details.
52              
53             =cut
54              
55              
56             package Date::Holidays::CA;
57              
58 5     5   310305 use 5.006;
  5         17  
  5         178  
59 5     5   26 use strict;
  5         8  
  5         168  
60 5     5   26 use warnings;
  5         12  
  5         118  
61 5     5   24 use Carp;
  5         15  
  5         437  
62 5     5   9554 use DateTime;
  5         763342  
  5         164  
63 5     5   4823 use DateTime::Event::Easter;
  5         306774  
  5         16885  
64              
65              
66             require Exporter;
67              
68             our @ISA = qw(Exporter);
69              
70             our %EXPORT_TAGS = ( 'all' => [ qw(
71             is_holiday
72             is_ca_holiday
73             is_holiday_dt
74             holidays
75             ca_holidays
76             holidays_dt
77             ) ] );
78              
79             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
80             our @EXPORT = qw();
81              
82             our $VERSION = '0.03';
83              
84              
85             =head1 FUNCTIONS / METHODS
86              
87             =head2 Class Methods
88              
89             =head3 new()
90              
91             Create a new Date::Holidays::CA object. Parameters should be given as
92             a hashref of key-value pairs.
93              
94             my $dhc = Date::Holidays::CA->new(); # defaults
95              
96             my $dhc = Date::Holidays::CA->new({
97             province => 'ON', language => 'EN'
98             });
99              
100             Two parameters can be specified: B and B.
101              
102             =head4 Province
103              
104             =over
105              
106             =item * CA
107              
108             Canadian Federal holidays (the default).
109              
110             =item * AB
111              
112             Alberta
113              
114             =item * BC
115              
116             British Columbia
117              
118             =item * MB
119              
120             Manitoba
121              
122             =item * NB
123              
124             New Brunswick
125              
126             =item * NL
127              
128             Newfoundland & Labrador
129              
130             =item * NS
131              
132             Nova Scotia
133              
134             =item * NT
135              
136             Northwest Territories
137              
138             =item * NU
139              
140             Nunavut
141              
142             =item * ON
143              
144             Ontario
145              
146             =item * PE
147              
148             Prince Edward Island
149              
150             =item * QC
151              
152             Quebec
153              
154             =item * SK
155              
156             Saskatchewan
157              
158             =item * YT
159              
160             Yukon Territory
161              
162             =back
163              
164             =head4 Language
165              
166             =over
167              
168             =item * EN/FR
169              
170             English text followed by French text.
171              
172             =item * FR/EN
173              
174             French text followed by English text.
175              
176             =item * EN
177              
178             English text only.
179              
180             =item * FR
181              
182             French text only.
183              
184             =back
185              
186             =cut
187              
188            
189             sub new {
190 30     30 1 93436 my $class = shift;
191 30         46 my $args_ref = shift;
192            
193 30         108 my $self = {
194             province => 'CA',
195             language => 'EN/FR',
196             };
197              
198 30         76 bless $self, $class;
199 30         86 $self->set($args_ref);
200 27         54 return $self;
201             }
202              
203              
204              
205             =head2 Object Methods
206              
207             =head3 get()
208              
209             Retrieve fields of a Date::Holidays::CA object.
210              
211             $prov = $dhc->('province');
212              
213             =cut
214              
215             sub get {
216 4 100   4 1 98 croak 'Wrong number of arguments to get()' if scalar @_ != 2;
217 3         6 my $self = shift;
218 3         7 my $field = shift;
219            
220 3 100       21 if (exists $self->{$field}) {
221 2         11 return $self->{$field};
222             }
223              
224 1         10 croak "No such field $field";
225             }
226              
227              
228             =head3 set()
229              
230             Alter fields of a Date::Holidays::CA object. Specify parameters just
231             as with new().
232              
233             $dhc->set({province => 'QC', language => 'FR/EN'});
234              
235             =cut
236              
237             sub set {
238 32 50   32 1 143 croak 'Wrong number of arguments to set()' if scalar @_ != 2;
239 32         45 my $self = shift;
240 32         40 my $args_ref = shift;
241            
242 32         44 while (my ($field, $value) = each %{$args_ref}) {
  74         245  
243 47         43 my $new_value;
244              
245 47 50       88 if ($new_value = _validate($field, $value)) {
246 42         147 $self->{$field} = $new_value;
247             }
248             }
249              
250 27         50 return 1;
251             }
252              
253              
254             =head2 Combination Methods
255              
256             These methods are callable in either object-oriented or procedural style.
257              
258             =head3 is_holiday()
259              
260             For a given year, month (1-12) and day (1-31), return 1 if the given
261             day is a holiday; 0 if not. When using procedural calling style, an
262             additional hashref of options can be specified.
263              
264             $holiday_p = is_holiday($year, $month, $day);
265              
266             $holiday_p = is_holiday($year, $month, $day, {
267             province => 'BC', language => 'EN'
268             });
269              
270             $holiday_p = $dhc->is_holiday($year, $month, $day);
271              
272             =cut
273              
274             sub is_holiday {
275 0 0   0 1 0 return ( is_ca_holiday(@_) ? 1 : 0 );
276             }
277              
278              
279             =head3 is_ca_holiday()
280              
281             Similar to C. Return the name of the holiday occurring on
282             the specified date if there is one; C if there isn't.
283              
284             print $dhc->is_ca_holiday(2001, 1, 1); # "New Year's Day"
285              
286             =cut
287              
288             sub is_ca_holiday {
289 0     0 1 0 my $self;
290 0 0       0 $self = shift if (ref $_[0]); # invoked in OO style
291              
292 0         0 my $year = shift;
293 0         0 my $month = shift;
294 0         0 my $day = shift;
295 0         0 my $options = shift;
296              
297 0         0 _assert_valid_date($year, $month, $day);
298              
299 0 0       0 unless (defined $self) {
300 0         0 $self = Date::Holidays::CA->new($options);
301             }
302              
303 0         0 my $calendar = $self->_generate_calendar($year);
304              
305             # assumption: there is only one holiday for any given day.
306 0         0 while (my ($holiday_name, $holiday_dt) = each %$calendar) {
307 0 0 0     0 if ($month == $holiday_dt->month and $day == $holiday_dt->day) {
308 0         0 return $holiday_name;
309             }
310             }
311              
312 0         0 return;
313             }
314              
315              
316             =head3 is_holiday_dt()
317              
318             As is_holiday, but accepts a DateTime object in place of a numeric year,
319             month, and day.
320              
321             $holiday_p = is_holiday($dt, {province => 'SK', language => 'EN'});
322              
323             $holiday_p = $dhc->is_holiday($dt);
324              
325             =cut
326              
327             sub is_holiday_dt {
328 0     0 1 0 my ($self, $dt, $options);
329              
330 0 0       0 my @args = map {
331 0         0 ref $_ eq 'DateTime' ? ($_->year, $_->month, $_->day) : $_
332             } @_;
333              
334 0         0 return is_holiday(@args);
335             }
336              
337              
338             =head3 holidays()
339              
340             For the given year, return a hashref containing all the holidays for
341             that year. The keys are the date of the holiday in C format
342             (eg '1225' for December 25); the values are the holiday names.
343              
344             my $calendar = holidays($year, {province => 'MB', language => 'EN'});
345             print $calendar->('0701'); # "Canada Day"
346            
347             my $calendar = $dhc->holidays($year);
348             print $calendar->('1111'); # "Remembrance Day"
349              
350             =cut
351              
352             sub holidays {
353 19     19 1 92 my $calendar = holidays_dt(@_);
354              
355 190         5103 my %holidays = map {
356 19         65 $calendar->{$_}->strftime('%m%d') => $_
357             } keys %$calendar;
358              
359 19         976 return \%holidays;
360             }
361              
362              
363             =head3 ca_holidays()
364              
365             Same as C.
366              
367             =cut
368              
369             sub ca_holidays {
370 0     0 1 0 return holidays(@_);
371             }
372              
373              
374             =head3 holidays_dt()
375              
376             Similar to C, after a fashion: returns a hashref with the
377             holiday names as the keys and DateTime objects as the values.
378              
379             my $calendar = $dhc->holidays_dt($year);
380              
381             =cut
382              
383             sub holidays_dt {
384 19     19 1 19 my $self;
385 19 50       49 $self = shift if (ref $_[0]); # invoked in OO style
386              
387 19         28 my $year = shift;
388 19         20 my $args_ref = shift;
389              
390 19 50       37 unless (defined $self) {
391 0         0 $self = Date::Holidays::CA->new($args_ref);
392             }
393              
394 19         61 return $self->_generate_calendar($year);
395             }
396              
397              
398              
399             ### internal functions
400              
401             my @VALID_PROVINCES = qw{ CA AB BC MB NB NL NS NT NU ON PE QC SK YT };
402             my @VALID_LANGUAGES = qw{ EN/FR FR/EN EN FR };
403             my %VALUES_FOR = (
404             'PROVINCE' => \@VALID_PROVINCES,
405             'LANGUAGE' => \@VALID_LANGUAGES,
406             );
407              
408              
409             # _validate($field, $value)
410             #
411             # accepts: field name ( 'province' | 'language' )
412             # possible value for that field
413             # returns: if $value is a valid value for $field, canonicalize and return
414             # it (eg, upcase it).
415             # if $value isn't valid, throw an exception.
416              
417              
418             sub _validate {
419 47     47   52 my $field = shift;
420 47         58 my $value = shift;
421              
422 47         44 my @valid_values = @{ $VALUES_FOR{uc($field)} };
  47         202  
423 45 50       96 croak "No such field $field" unless @valid_values;
424              
425 45         67 foreach my $valid_value (@valid_values) {
426 134 100       370 return uc($value) if uc($value) eq $valid_value;
427             }
428              
429 3         27 croak "$value is not a recognized setting for $field";
430             }
431              
432              
433             # _assert_valid_date
434             #
435             # accepts: numeric year, month, day
436             # returns: nothing
437             #
438             # throw an exception on invalid dates; otherwise, do nothing.
439              
440             sub _assert_valid_date {
441 0     0   0 my ($year, $month, $day) = @_;
442              
443             # DateTime does date validation when a DT object is created.
444 0         0 my $dt = DateTime->new(
445             year => $year, month => $month, day => $day,
446             );
447             }
448              
449              
450             # format: each holiday is listed as a triplet:
451             # * function that returns a DateTime object for that holiday
452             # * english name
453             # * french name
454             # listing the names each time makes for a verbose list with a lot of
455             # repetition; unfortunately different provinces sometimes call different
456             # holidays different things.
457              
458             my %HOLIDAYS_FOR = (
459             CA => [
460             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
461             \&_good_friday, 'Good Friday', 'Vendredi Saint',
462             \&_easter_monday, 'Easter Monday', 'Lundi de PĂąques',
463             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
464             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
465             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
466             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
467             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
468             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
469             \&_boxing_day, 'Boxing Day', 'Lendemain de NoĂ«l',
470             ],
471            
472             AB => [
473             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
474             \&_family_day, 'Family Day', 'Jour de la Famille',
475             \&_good_friday, 'Good Friday', 'Vendredi Saint',
476             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
477             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
478             \&_civic_holiday, 'Alberta Heritage Day', 'Jour d\'HĂ©ritage d\'Alberta',
479             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
480             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
481             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
482             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
483             ],
484            
485             BC => [
486             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
487             \&_good_friday, 'Good Friday', 'Vendredi Saint',
488             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
489             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
490             \&_civic_holiday, 'BC Day', 'FĂȘte de la Colombie-Britannique',
491             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
492             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
493             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
494             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
495             ],
496            
497             MB => [
498             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
499             \&_family_day, 'Louis Riel Day', 'Jour de Louis Riel',
500             \&_good_friday, 'Good Friday', 'Vendredi Saint',
501             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
502             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
503             \&_civic_holiday, 'Civic Holiday', 'CongĂ© Statutaire',
504             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
505             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
506             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
507             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
508             ],
509            
510             NB => [
511             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
512             \&_good_friday, 'Good Friday', 'Vendredi Saint',
513             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
514             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
515             \&_civic_holiday, 'New Brunswick Day', 'FĂȘte du Nouveau-Brunswick',
516             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
517             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
518             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
519             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
520             ],
521            
522             NL => [
523             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
524             \&_st_patricks_day, 'St Patrick\'s Day', 'La Saint-Patrick',
525             \&_good_friday, 'Good Friday', 'Vendredi Saint',
526             \&_st_georges_day, 'St George\'s Day', 'La Saint-Georges',
527             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
528             \&_nl_discovery_day, 'Discovery Day', 'Jour de la DĂ©couverte',
529             \&_canada_day, 'Memorial Day', 'FĂȘte du Canada',
530             \&_orangemens_day, 'Orangemen\'s Day', 'FĂȘte des Orangistes',
531             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
532             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
533             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
534             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
535             \&_boxing_day, 'Boxing Day', 'Lendemain de NoĂ«l',
536             ],
537            
538             NS => [
539             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
540             \&_good_friday, 'Good Friday', 'Vendredi Saint',
541             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
542             \&_civic_holiday, 'Natal Day', 'Jour de la Fondation',
543             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
544             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
545             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
546             ],
547            
548             NT => [
549             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
550             \&_good_friday, 'Good Friday', 'Vendredi Saint',
551             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
552             \&_national_aboriginal_day, 'National Aboriginal Day', 'JournĂ©e Nationale des Autochtones',
553             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
554             \&_civic_holiday, 'Civic Holiday', 'CongĂ© Statutaire',
555             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
556             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
557             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
558             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
559             \&_boxing_day, 'Boxing Day', 'Lendemain de NoĂ«l',
560             ],
561            
562             NU => [
563             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
564             \&_good_friday, 'Good Friday', 'Vendredi Saint',
565             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
566             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
567             \&_civic_holiday, 'Civic Holiday', 'CongĂ© Statutaire',
568             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
569             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
570             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
571             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
572             \&_boxing_day, 'Boxing Day', 'Lendemain de NoĂ«l',
573             ],
574            
575             ON => [
576             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
577             \&_family_day, 'Family Day', 'Jour de la Famille',
578             \&_good_friday, 'Good Friday', 'Vendredi Saint',
579             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
580             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
581             \&_civic_holiday, 'Civic Holiday', 'CongĂ© Statutaire',
582             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
583             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
584             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
585             \&_boxing_day, 'Boxing Day', 'Lendemain de NoĂ«l',
586             ],
587            
588             PE => [
589             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
590             \&_family_day, 'Islander Day', 'FĂȘte des Insulaires',
591             \&_good_friday, 'Good Friday', 'Vendredi Saint',
592             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
593             \&_civic_holiday, 'Natal Day', 'Jour de la Fondation',
594             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
595             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
596             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
597             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
598             \&_boxing_day, 'Boxing Day', 'Lendemain de NoĂ«l',
599             ],
600            
601             QC => [
602             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
603             \&_good_friday, 'Good Friday', 'Vendredi Saint',
604             \&_easter_monday, 'Easter Monday', 'Lundi de PĂąques',
605             \&_victoria_day, 'Victoria Day', 'JournĂ©e Nationale des Patriotes / FĂȘte de la Reine',
606             \&_st_john_baptiste_day, 'Saint-Jean-Baptiste Day', 'La Saint-Jean',
607             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
608             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
609             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
610             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
611             ],
612            
613             SK => [
614             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
615             \&_family_day, 'Family Day', 'Jour de la Famille',
616             \&_good_friday, 'Good Friday', 'Vendredi Saint',
617             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
618             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
619             \&_civic_holiday, 'Saskatchewan Day', 'FĂȘte de la Saskatchewan',
620             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
621             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
622             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
623             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
624             ],
625            
626             YT => [
627             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
628             \&_good_friday, 'Good Friday', 'Vendredi Saint',
629             \&_victoria_day, 'Victoria Day', 'FĂȘte de la Reine',
630             \&_canada_day, 'Canada Day', 'FĂȘte du Canada',
631             \&_yt_discovery_day, 'Discovery Day', 'Jour du dĂ©couverte',
632             \&_labour_day, 'Labour Day', 'FĂȘte du Travail',
633             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de GrĂące',
634             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
635             \&_christmas_day, 'Christmas Day', 'NoĂ«l',
636             ],
637             );
638              
639              
640             # _generate_calendar
641             #
642             # accepts: numeric year
643             # returns: hashref (string $holiday_name => DateTime $holiday_dt)
644             #
645             # generate a holiday calendar for the specified year -- a hash mapping
646             # holiday names to datetime objects.
647             sub _generate_calendar {
648 19     19   23 my $self = shift;
649 19         18 my $year = shift;
650 19         23 my $calendar = {};
651              
652 19         22 my @holiday_list = @{ $HOLIDAYS_FOR{$self->{'province'}} };
  19         124  
653              
654 19         40 while(@holiday_list) {
655 190         389 my $holiday_dt = (shift @holiday_list)->($year); # fn invokation
656 190         52263 my $name_en = shift @holiday_list;
657 190         243 my $name_fr = shift @holiday_list;
658              
659 190 0       399 my $holiday_name =
    0          
    0          
    50          
660             $self->{'language'} eq 'EN' ? $name_en
661             : $self->{'language'} eq 'FR' ? $name_fr
662             : $self->{'language'} eq 'EN/FR' ? "$name_en/$name_fr"
663             : $self->{'language'} eq 'FR/EN' ? "$name_fr/$name_en"
664             : "$name_en/$name_fr"; # sane default, should never get here
665              
666 190         580 $calendar->{$holiday_name} = $holiday_dt;
667             }
668              
669 19         76 return $calendar;
670             }
671              
672             ### toolkit functions
673              
674             # _nth_monday
675             #
676             # accepts: year, month, ordinal of which monday to find
677             # returns: numeric date of the requested monday
678             #
679             # find the day of week for the first day of the month,
680             # calculate the number of day to skip forward to hit the first monday,
681             # then skip forward the requisite number of weeks.
682             #
683             # in general, the number of days we need to skip forward from the
684             # first of the month is (target_dow - first_of_month_dow) % 7
685              
686             sub _nth_monday {
687 45     45   4373 my $year = shift;
688 45         55 my $month = shift;
689 45         48 my $n = shift;
690              
691 45         147 my $first_of_month = DateTime->new(
692             year => $year,
693             month => $month,
694             day => 1,
695             );
696              
697 45         7713 my $date_of_first_monday = 1 + ( (1 - $first_of_month->dow()) % 7);
698              
699 45         462 return $date_of_first_monday + 7 * ($n - 1);
700             }
701              
702             # _nearest_monday
703             #
704             # accepts: year, month, day for a given date
705             # returns: day of the nearest monday to that date
706              
707             sub _nearest_monday {
708 7     7   4292 my $year = shift;
709 7         13 my $month = shift;
710 7         8 my $day = shift;
711              
712 7         35 my $dt = DateTime->new(year => $year, month => $month, day => $day);
713            
714 7         1542 my $delta_days = ((4 - $dt->dow) % 7) - 3;
715              
716 7         93 return $day + $delta_days;
717             }
718              
719             ### holiday date calculating functions
720             #
721             # these all take one parameter ($year) and return a DateTime object
722             # specifying the day of the holiday for that year.
723              
724             sub _new_years_day {
725 19     19   29 my $year = shift;
726              
727 19         75 return DateTime->new(
728             year => $year,
729             month => 1,
730             day => 1,
731             );
732             }
733              
734             sub _family_day {
735 0     0   0 my $year = shift;
736              
737 0         0 return DateTime->new(
738             year => $year,
739             month => 2,
740             day => _nth_monday($year, 2, 3),
741             );
742             }
743              
744             sub _st_patricks_day {
745 0     0   0 my $year = shift;
746              
747 0         0 return DateTime->new(
748             year => $year,
749             month => 2,
750             day => _nearest_monday($year, 3, 17),
751             );
752             }
753              
754             sub _good_friday {
755 19     19   22 my $year = shift;
756              
757 19         54 my $dt = DateTime->new( year => $year, month => 1, day => 1 );
758 19         2953 my $event = DateTime::Event::Easter->new(day => 'Good Friday');
759 19         2235 return $event->following($dt);
760             }
761              
762             sub _easter_sunday {
763 0     0   0 my $year = shift;
764            
765 0         0 my $dt = DateTime->new( year => $year, month => 1, day => 1 );
766 0         0 my $event = DateTime::Event::Easter->new(day => 'Easter Sunday');
767 0         0 return $event->following($dt);
768             }
769              
770             sub _easter_monday {
771 19     19   24 my $year = shift;
772              
773 19         218 my $dt = DateTime->new( year => $year, month => 1, day => 1 );
774 19         3078 my $event = DateTime::Event::Easter->new(day => +1);
775 19         1859 return $event->following($dt);
776             }
777              
778             sub _st_georges_day {
779 0     0   0 my $year = shift;
780              
781 0         0 return DateTime->new(
782             year => $year,
783             month => 4,
784             day => _nearest_monday($year, 4, 23),
785             );
786             }
787              
788             sub _victoria_day {
789 19     19   21 my $year = shift;
790              
791 19         62 my $may_24 = DateTime->new(
792             year => $year,
793             month => 5,
794             day => 24,
795             );
796              
797 19         3187 return DateTime->new(
798             year => $year,
799             month => 5,
800             day => 25 - $may_24->dow()
801             );
802             }
803              
804             sub _national_aboriginal_day {
805 0     0   0 my $year = shift;
806              
807 0         0 return DateTime->new(
808             year => $year,
809             month => 6,
810             day => 21,
811             );
812             }
813              
814             sub _st_john_baptiste_day {
815 0     0   0 my $year = shift;
816              
817 0         0 return DateTime->new(
818             year => $year,
819             month => 6,
820             day => 24,
821             );
822             }
823              
824             sub _nl_discovery_day {
825 0     0   0 my $year = shift;
826              
827 0         0 return DateTime->new(
828             year => $year,
829             month => 6,
830             day => _nearest_monday($year, 6, 24),
831             );
832             }
833              
834             sub _canada_day {
835 19     19   26 my $year = shift;
836              
837 19         63 return DateTime->new(
838             year => $year,
839             month => 7,
840             day => 1,
841             );
842             }
843              
844             sub _nunavut_day {
845 0     0   0 my $year = shift;
846              
847 0         0 return DateTime->new(
848             year => $year,
849             month => 7,
850             day => 9,
851             );
852             }
853              
854             sub _orangemens_day {
855 0     0   0 my $year = shift;
856              
857 0         0 return DateTime->new(
858             year => $year,
859             month => 7,
860             day => _nearest_monday($year, 7, 12),
861             );
862             }
863              
864             sub _civic_holiday {
865 0     0   0 my $year = shift;
866              
867 0         0 return DateTime->new(
868             year => $year,
869             month => 8,
870             day => _nth_monday($year, 8, 1),
871             );
872             }
873              
874             sub _yt_discovery_day {
875 0     0   0 my $year = shift;
876              
877 0         0 return DateTime->new(
878             year => $year,
879             month => 8,
880             day => _nth_monday($year, 8, 3),
881             );
882             }
883              
884             sub _labour_day {
885 19     19   25 my $year = shift;
886              
887 19         45 return DateTime->new(
888             year => $year,
889             month => 9,
890             day => _nth_monday($year, 9, 1),
891             );
892             }
893              
894             sub _thanksgiving_day {
895 19     19   27 my $year = shift;
896              
897 19         39 return DateTime->new(
898             year => $year,
899             month => 10,
900             day => _nth_monday($year, 10, 2),
901             );
902             }
903              
904             sub _remembrance_day {
905 19     19   20 my $year = shift;
906              
907 19         55 return DateTime->new(
908             year => $year,
909             month => 11,
910             day => 11,
911             );
912             }
913              
914             sub _christmas_day {
915 19     19   26 my $year = shift;
916              
917 19         58 return DateTime->new(
918             year => $year,
919             month => 12,
920             day => 25,
921             );
922             }
923              
924             sub _boxing_day {
925 19     19   28 my $year = shift;
926              
927 19         57 return DateTime->new(
928             year => $year,
929             month => 12,
930             day => 26,
931             );
932             }
933              
934              
935             1; # all's well
936              
937             __END__