File Coverage

blib/lib/Date/Holidays/CA.pm
Criterion Covered Total %
statement 109 154 70.7
branch 12 34 35.2
condition 0 3 0.0
subroutine 27 42 64.2
pod 9 9 100.0
total 157 242 64.8


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              
12              
13 5     5   776597 use strict;
  5         39  
  5         110  
14 5     5   22 use warnings;
  5         9  
  5         191  
15             our $VERSION = '0.04'; # TRIAL VERSION
16              
17             # ABSTRACT: Date::Holidays::CA determines public holidays for Canadian jurisdictions
18              
19             use 5.006;
20 5     5   85 use Carp;
  5         17  
21 5     5   24 use DateTime;
  5         8  
  5         253  
22 5     5   2588 use DateTime::Event::Easter;
  5         1586198  
  5         187  
23 5     5   2595  
  5         193372  
  5         13049  
24              
25             require Exporter;
26              
27             our @ISA = qw(Exporter);
28              
29             our %EXPORT_TAGS = ( 'all' => [ qw(
30             is_holiday
31             is_ca_holiday
32             is_holiday_dt
33             holidays
34             ca_holidays
35             holidays_dt
36             ) ] );
37              
38             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
39             our @EXPORT = qw();
40              
41              
42              
43             my $class = shift;
44             my $args_ref = shift;
45 30     30 1 116886  
46 30         59 my $self = {
47             province => 'CA',
48 30         77 language => 'EN/FR',
49             };
50              
51             bless $self, $class;
52             $self->set($args_ref);
53 30         44 return $self;
54 30         84 }
55 27         45  
56              
57              
58              
59             croak 'Wrong number of arguments to get()' if scalar @_ != 2;
60             my $self = shift;
61             my $field = shift;
62 4 100   4 1 85  
63 3         6 if (exists $self->{$field}) {
64 3         3 return $self->{$field};
65             }
66 3 100       12  
67 2         13 croak "No such field $field";
68             }
69              
70 1         9  
71              
72             croak 'Wrong number of arguments to set()' if scalar @_ != 2;
73             my $self = shift;
74             my $args_ref = shift;
75              
76 32 50   32 1 130 while (my ($field, $value) = each %{$args_ref}) {
77 32         43 my $new_value;
78 32         40  
79             if ($new_value = _validate($field, $value)) {
80 32         42 $self->{$field} = $new_value;
  74         191  
81 47         51 }
82             }
83 47 50       84  
84 42         82 return 1;
85             }
86              
87              
88 27         46  
89             return ( is_ca_holiday(@_) ? 1 : 0 );
90             }
91              
92              
93              
94 0 0   0 1 0 my $self;
95             $self = shift if (ref $_[0]); # invoked in OO style
96              
97             my $year = shift;
98             my $month = shift;
99             my $day = shift;
100 0     0 1 0 my $options = shift;
101 0 0       0  
102             _assert_valid_date($year, $month, $day);
103 0         0  
104 0         0 unless (defined $self) {
105 0         0 $self = Date::Holidays::CA->new($options);
106 0         0 }
107              
108 0         0 my $calendar = $self->_generate_calendar($year);
109              
110 0 0       0 # assumption: there is only one holiday for any given day.
111 0         0 while (my ($holiday_name, $holiday_dt) = each %$calendar) {
112             if ($month == $holiday_dt->month and $day == $holiday_dt->day) {
113             return $holiday_name;
114 0         0 }
115             }
116              
117 0         0 return;
118 0 0 0     0 }
119 0         0  
120              
121              
122             my ($self, $dt, $options);
123 0         0  
124             my @args = map {
125             ref $_ eq 'DateTime' ? ($_->year, $_->month, $_->day) : $_
126             } @_;
127              
128             return is_holiday(@args);
129 0     0 1 0 }
130              
131              
132 0 0       0  
  0         0  
133             my $calendar = holidays_dt(@_);
134              
135 0         0 my %holidays = map {
136             $calendar->{$_}->strftime('%m%d') => $_
137             } keys %$calendar;
138              
139             return \%holidays;
140             }
141 19     19 1 108  
142              
143              
144 19         75 return holidays(@_);
  228         5575  
145             }
146              
147 19         910  
148              
149             my $self;
150             $self = shift if (ref $_[0]); # invoked in OO style
151              
152             my $year = shift;
153 0     0 1 0 my $args_ref = shift;
154              
155             unless (defined $self) {
156             $self = Date::Holidays::CA->new($args_ref);
157             }
158              
159 19     19 1 37 return $self->_generate_calendar($year);
160 19 50       42 }
161              
162 19         28  
163 19         21  
164             ### internal functions
165 19 50       38  
166 0         0 my @VALID_PROVINCES = qw{ CA AB BC MB NB NL NS NT NU ON PE QC SK YT };
167             my @VALID_LANGUAGES = qw{ EN/FR FR/EN EN FR };
168             my %VALUES_FOR = (
169 19         50 'PROVINCE' => \@VALID_PROVINCES,
170             'LANGUAGE' => \@VALID_LANGUAGES,
171             );
172              
173              
174             # _validate($field, $value)
175             #
176             # accepts: field name ( 'province' | 'language' )
177             # possible value for that field
178             # returns: if $value is a valid value for $field, canonicalize and return
179             # it (eg, upcase it).
180             # if $value isn't valid, throw an exception.
181              
182              
183             my $field = shift;
184             my $value = shift;
185              
186             my @valid_values = @{ $VALUES_FOR{uc($field)} };
187             croak "No such field $field" unless @valid_values;
188              
189             foreach my $valid_value (@valid_values) {
190             return uc($value) if uc($value) eq $valid_value;
191             }
192              
193             croak "$value is not a recognized setting for $field";
194 47     47   56 }
195 47         61  
196              
197 47         45 # _assert_valid_date
  47         156  
198 45 50       91 #
199             # accepts: numeric year, month, day
200 45         66 # returns: nothing
201 134 100       250 #
202             # throw an exception on invalid dates; otherwise, do nothing.
203              
204 3         22 my ($year, $month, $day) = @_;
205              
206             # DateTime does date validation when a DT object is created.
207             my $dt = DateTime->new(
208             year => $year, month => $month, day => $day,
209             );
210             }
211              
212              
213             # format: each holiday is listed as a triplet:
214             # * function that returns a DateTime object for that holiday
215             # * english name
216 0     0   0 # * french name
217             # listing the names each time makes for a verbose list with a lot of
218             # repetition; unfortunately different provinces sometimes call different
219 0         0 # holidays different things.
220              
221             my %HOLIDAYS_FOR = (
222             CA => [
223             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
224             \&_good_friday, 'Good Friday', 'Vendredi Saint',
225             \&_easter_monday, 'Easter Monday', 'Lundi de Pâques',
226             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
227             \&_canada_day, 'Canada Day', 'Fête du Canada',
228             \&_civic_holiday, 'Civic Holiday', 'Fête Civique',
229             \&_labour_day, 'Labour Day', 'Fête du Travail',
230             \&_truth_reconciliation_day, 'National Day for Truth and Reconciliation', 'Journée nationale de la vérité et de la réconciliation',
231             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
232             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
233             \&_christmas_day, 'Christmas Day', 'Noël',
234             \&_boxing_day, 'Boxing Day', 'Lendemain de Noël',
235             ],
236              
237             AB => [
238             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
239             \&_family_day, 'Family Day', 'Jour de la Famille',
240             \&_good_friday, 'Good Friday', 'Vendredi Saint',
241             \&_easter_monday, 'Easter Monday', 'Lundi de Pâques',
242             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
243             \&_canada_day, 'Canada Day', 'Fête du Canada',
244             \&_civic_holiday, 'Alberta Heritage Day', 'Jour d\'Héritage d\'Alberta',
245             \&_labour_day, 'Labour Day', 'Fête du Travail',
246             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
247             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
248             \&_christmas_day, 'Christmas Day', 'Noël',
249             \&_boxing_day, 'Boxing Day', 'Lendemain de Noël',
250             ],
251              
252             BC => [
253             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
254             \&_family_day, 'Family Day', 'Jour de la Famille',
255             \&_good_friday, 'Good Friday', 'Vendredi Saint',
256             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
257             \&_canada_day, 'Canada Day', 'Fête du Canada',
258             \&_civic_holiday, 'BC Day', 'Fête de la Colombie-Britannique',
259             \&_labour_day, 'Labour Day', 'Fête du Travail',
260             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
261             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
262             \&_christmas_day, 'Christmas Day', 'Noël',
263             ],
264              
265             MB => [
266             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
267             \&_family_day, 'Louis Riel Day', 'Jour de Louis Riel',
268             \&_good_friday, 'Good Friday', 'Vendredi Saint',
269             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
270             \&_canada_day, 'Canada Day', 'Fête du Canada',
271             \&_labour_day, 'Labour Day', 'Fête du Travail',
272             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
273             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
274             \&_christmas_day, 'Christmas Day', 'Noël',
275             ],
276              
277             NB => [
278             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
279             \&_family_day, 'Family Day', 'Le jour de la Famille',
280             \&_good_friday, 'Good Friday', 'Vendredi Saint',
281             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
282             \&_canada_day, 'Canada Day', 'Fête du Canada',
283             \&_civic_holiday, 'New Brunswick Day', 'Fête du Nouveau-Brunswick',
284             \&_labour_day, 'Labour Day', 'Fête du Travail',
285             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
286             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
287             \&_christmas_day, 'Christmas Day', 'Noël',
288             \&_boxing_day, 'Boxing Day', 'Lendemain de Noël',
289             ],
290              
291             NL => [
292             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
293             \&_st_patricks_day, 'St Patrick\'s Day', 'La Saint-Patrick',
294             \&_good_friday, 'Good Friday', 'Vendredi Saint',
295             \&_st_georges_day, 'St George\'s Day', 'La Saint-Georges',
296             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
297             \&_nl_discovery_day, 'June Day', 'Jour de juin',
298             \&_canada_day, 'Memorial Day', 'Fête du Canada',
299             \&_orangemens_day, 'Orangemen\'s Day', 'Fête des Orangistes',
300             \&_labour_day, 'Labour Day', 'Fête du Travail',
301             \&_truth_reconciliation_day, 'National Day for Truth and Reconciliation', 'Journée nationale de la vérité et de la réconciliation',
302             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
303             \&_remembrance_day, 'Armistice Day (Remembrance Day)', 'Jour du Souvenir',
304             \&_christmas_day, 'Christmas Day', 'Noël',
305             \&_boxing_day, 'Boxing Day', 'Lendemain de Noël',
306             ],
307              
308             NS => [
309             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
310             \&_family_day, 'Nova Scotia Heritage Day', 'Le jour de la Famille',
311             \&_good_friday, 'Good Friday', 'Vendredi Saint',
312             \&_canada_day, 'Canada Day', 'Fête du Canada',
313             \&_labour_day, 'Labour Day', 'Fête du Travail',
314             \&_christmas_day, 'Christmas Day', 'Noël',
315             ],
316              
317             NT => [
318             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
319             \&_good_friday, 'Good Friday', 'Vendredi Saint',
320             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
321             \&_national_aboriginal_day, 'National Aboriginal Day', 'Journée Nationale des Autochtones',
322             \&_canada_day, 'Canada Day', 'Fête du Canada',
323             \&_civic_holiday, 'Civic Holiday', 'Fête Civique',
324             \&_labour_day, 'Labour Day', 'Fête du Travail',
325             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
326             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
327             \&_christmas_day, 'Christmas Day', 'Noël',
328             \&_boxing_day, 'Boxing Day', 'Lendemain de Noël',
329             ],
330              
331             NU => [
332             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
333             \&_good_friday, 'Good Friday', 'Vendredi Saint',
334             \&_easter_monday, 'Easter Monday', 'Lundi de Pâques',
335             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
336             \&_canada_day, 'Canada Day', 'Fête du Canada',
337             \&_nunavut_day, 'Nunavut Day', 'Jour du Nunavut',
338             \&_civic_holiday, 'Civic Holiday', 'Congé Statutaire',
339             \&_labour_day, 'Labour Day', 'Fête du Travail',
340             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
341             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
342             \&_christmas_day, 'Christmas Day', 'Noël',
343             \&_boxing_day, 'Boxing Day', 'Lendemain de Noël',
344             ],
345              
346             ON => [
347             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
348             \&_family_day, 'Family Day', 'Jour de la Famille',
349             \&_good_friday, 'Good Friday', 'Vendredi Saint',
350             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
351             \&_canada_day, 'Canada Day', 'Fête du Canada',
352             \&_civic_holiday, 'Civic Holiday', 'Congé Statutaire',
353             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
354             \&_christmas_day, 'Christmas Day', 'Noël',
355             \&_boxing_day, 'Boxing Day', 'Lendemain de Noël',
356             ],
357              
358             PE => [
359             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
360             \&_family_day, 'Islander Day', 'Fête des Insulaires',
361             \&_good_friday, 'Good Friday', 'Vendredi Saint',
362             \&_canada_day, 'Canada Day', 'Fête du Canada',
363             \&_labour_day, 'Labour Day', 'Fête du Travail',
364             \&_truth_reconciliation_day, 'National Day for Truth and Reconciliation', 'Journée nationale de la vérité et de la réconciliation',
365             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
366             \&_christmas_day, 'Christmas Day', 'Noël',
367             ],
368              
369             QC => [
370             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
371             \&_good_friday, 'Good Friday', 'Vendredi Saint',
372             \&_victoria_day, 'National Patriot\'s Day', 'Journée Nationale des Patriotes / Fête de la Reine',
373             \&_st_john_baptiste_day, 'Saint-Jean-Baptiste Day', 'La Saint-Jean',
374             \&_canada_day, 'Canada Day', 'Fête du Canada',
375             \&_labour_day, 'Labour Day', 'Fête du Travail',
376             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
377             \&_christmas_day, 'Christmas Day', 'Noël',
378             ],
379              
380             SK => [
381             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
382             \&_family_day, 'Family Day', 'Jour de la Famille',
383             \&_good_friday, 'Good Friday', 'Vendredi Saint',
384             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
385             \&_canada_day, 'Canada Day', 'Fête du Canada',
386             \&_civic_holiday, 'Saskatchewan Day', 'Fête de la Saskatchewan',
387             \&_labour_day, 'Labour Day', 'Fête du Travail',
388             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
389             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
390             \&_christmas_day, 'Christmas Day', 'Noël',
391             ],
392              
393             YT => [
394             \&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
395             \&_good_friday, 'Good Friday', 'Vendredi Saint',
396             \&_victoria_day, 'Victoria Day', 'Fête de la Reine',
397             \&_national_aboriginal_day, 'National Aboriginal Day', 'Journée Nationale des Autochtones',
398             \&_canada_day, 'Canada Day', 'Fête du Canada',
399             \&_yt_discovery_day, 'Discovery Day', 'Jour du découverte',
400             \&_labour_day, 'Labour Day', 'Fête du Travail',
401             \&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
402             \&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
403             \&_christmas_day, 'Christmas Day', 'Noël',
404             ],
405             );
406              
407              
408             # _generate_calendar
409             #
410             # accepts: numeric year
411             # returns: hashref (string $holiday_name => DateTime $holiday_dt)
412             #
413             # generate a holiday calendar for the specified year -- a hash mapping
414             # holiday names to datetime objects.
415             my $self = shift;
416             my $year = shift;
417             my $calendar = {};
418              
419             my @holiday_list = @{ $HOLIDAYS_FOR{$self->{'province'}} };
420              
421             while(@holiday_list) {
422             my $holiday_dt = (shift @holiday_list)->($year); # fn invokation
423             my $name_en = shift @holiday_list;
424             my $name_fr = shift @holiday_list;
425              
426             my $holiday_name =
427             $self->{'language'} eq 'EN' ? $name_en
428 19     19   22 : $self->{'language'} eq 'FR' ? $name_fr
429 19         29 : $self->{'language'} eq 'EN/FR' ? "$name_en/$name_fr"
430 19         28 : $self->{'language'} eq 'FR/EN' ? "$name_fr/$name_en"
431             : "$name_en/$name_fr"; # sane default, should never get here
432 19         21  
  19         111  
433             $calendar->{$holiday_name} = $holiday_dt;
434 19         35 }
435 228         446  
436 228         109143 return $calendar;
437 228         293 }
438              
439             ### toolkit functions
440              
441             # _nth_monday
442             #
443 228 0       435 # accepts: year, month, ordinal of which monday to find
    0          
    0          
    50          
444             # returns: numeric date of the requested monday
445             #
446 228         628 # find the day of week for the first day of the month,
447             # calculate the number of day to skip forward to hit the first monday,
448             # then skip forward the requisite number of weeks.
449 19         60 #
450             # in general, the number of days we need to skip forward from the
451             # first of the month is (target_dow - first_of_month_dow) % 7
452              
453             my $year = shift;
454             my $month = shift;
455             my $n = shift;
456              
457             my $first_of_month = DateTime->new(
458             year => $year,
459             month => $month,
460             day => 1,
461             );
462              
463             my $date_of_first_monday = 1 + ( (1 - $first_of_month->dow()) % 7);
464              
465             return $date_of_first_monday + 7 * ($n - 1);
466             }
467 64     64   3461  
468 64         91 # _nearest_monday
469 64         66 #
470             # accepts: year, month, day for a given date
471 64         157 # returns: day of the nearest monday to that date
472              
473             my $year = shift;
474             my $month = shift;
475             my $day = shift;
476              
477 64         12752 my $dt = DateTime->new(year => $year, month => $month, day => $day);
478              
479 64         444 my $delta_days = ((4 - $dt->dow) % 7) - 3;
480              
481             return $day + $delta_days;
482             }
483              
484             ### holiday date calculating functions
485             #
486             # these all take one parameter ($year) and return a DateTime object
487             # specifying the day of the holiday for that year.
488 7     7   3014  
489 7         16 my $year = shift;
490 7         7  
491             return DateTime->new(
492 7         17 year => $year,
493             month => 1,
494 7         1428 day => 1,
495             );
496 7         52 }
497              
498             my $year = shift;
499              
500             return DateTime->new(
501             year => $year,
502             month => 2,
503             day => _nth_monday($year, 2, 3),
504             );
505 19     19   25 }
506              
507 19         77 my $year = shift;
508              
509             return DateTime->new(
510             year => $year,
511             month => 2,
512             day => _nearest_monday($year, 3, 17),
513             );
514             }
515 0     0   0  
516             my $year = shift;
517 0         0  
518             my $dt = DateTime->new( year => $year, month => 1, day => 1 );
519             my $event = DateTime::Event::Easter->new(day => 'Good Friday');
520             return $event->following($dt);
521             }
522              
523             my $year = shift;
524              
525 0     0   0 my $dt = DateTime->new( year => $year, month => 1, day => 1 );
526             my $event = DateTime::Event::Easter->new(day => 'Easter Sunday');
527 0         0 return $event->following($dt);
528             }
529              
530             my $year = shift;
531              
532             my $dt = DateTime->new( year => $year, month => 1, day => 1 );
533             my $event = DateTime::Event::Easter->new(day => +1);
534             return $event->following($dt);
535 19     19   23 }
536              
537 19         52 my $year = shift;
538 19         3963  
539 19         2902 return DateTime->new(
540             year => $year,
541             month => 4,
542             day => _nearest_monday($year, 4, 23),
543 0     0   0 );
544             }
545 0         0  
546 0         0 my $year = shift;
547 0         0  
548             my $may_24 = DateTime->new(
549             year => $year,
550             month => 5,
551 19     19   28 day => 24,
552             );
553 19         52  
554 19         3937 return DateTime->new(
555 19         2627 year => $year,
556             month => 5,
557             day => 25 - $may_24->dow()
558             );
559 0     0   0 }
560              
561 0         0 my $year = shift;
562              
563             return DateTime->new(
564             year => $year,
565             month => 6,
566             day => 21,
567             );
568             }
569 19     19   29  
570             my $year = shift;
571 19         52  
572             return DateTime->new(
573             year => $year,
574             month => 6,
575             day => 24,
576             );
577 19         3997 }
578              
579             my $year = shift;
580              
581             return DateTime->new(
582             year => $year,
583             month => 6,
584             day => _nearest_monday($year, 6, 24),
585 0     0   0 );
586             }
587 0         0  
588             my $year = shift;
589              
590             return DateTime->new(
591             year => $year,
592             month => 7,
593             day => 1,
594             );
595 0     0   0 }
596              
597 0         0 my $year = shift;
598              
599             return DateTime->new(
600             year => $year,
601             month => 7,
602             day => 9,
603             );
604             }
605 0     0   0  
606             my $year = shift;
607 0         0  
608             return DateTime->new(
609             year => $year,
610             month => 7,
611             day => _nearest_monday($year, 7, 12),
612             );
613             }
614              
615 19     19   26 my $year = shift;
616              
617 19         45 return DateTime->new(
618             year => $year,
619             month => 8,
620             day => _nth_monday($year, 8, 1),
621             );
622             }
623              
624             my $year = shift;
625 0     0   0  
626             return DateTime->new(
627 0         0 year => $year,
628             month => 8,
629             day => _nth_monday($year, 8, 3),
630             );
631             }
632              
633             my $year = shift;
634              
635 0     0   0 return DateTime->new(
636             year => $year,
637 0         0 month => 9,
638             day => _nth_monday($year, 9, 1),
639             );
640             }
641              
642             my $year = shift;
643              
644             return DateTime->new(
645 19     19   25 year => $year,
646             month => 9,
647 19         47 day => 30,
648             );
649             }
650              
651             my $year = shift;
652              
653             return DateTime->new(
654             year => $year,
655 0     0   0 month => 10,
656             day => _nth_monday($year, 10, 2),
657 0         0 );
658             }
659              
660             my $year = shift;
661              
662             return DateTime->new(
663             year => $year,
664             month => 11,
665 19     19   26 day => 11,
666             );
667 19         36 }
668              
669             my $year = shift;
670              
671             return DateTime->new(
672             year => $year,
673             month => 12,
674             day => 25,
675 19     19   27 );
676             }
677 19         49  
678             my $year = shift;
679              
680             return DateTime->new(
681             year => $year,
682             month => 12,
683             day => 26,
684             );
685 19     19   24 }
686              
687 19         44  
688             1; # all's well
689              
690              
691             =pod
692              
693             =encoding UTF-8
694              
695 19     19   25 =head1 NAME
696              
697 19         44 Date::Holidays::CA - Date::Holidays::CA determines public holidays for Canadian jurisdictions
698              
699             =head1 VERSION
700              
701             version 0.04
702              
703             =head1 SYNOPSIS
704              
705 19     19   27 # procedural approach
706              
707 19         44 use Date::Holidays::CA qw(:all);
708              
709             my ($year, $month, $day) = (localtime)[ 5, 4, 3 ];
710             $year += 1900;
711             $month += 1;
712              
713             print 'Woot!' if is_holiday($year, $month, $day, {province => 'BC'});
714              
715 19     19   23 my $calendar = holidays($year, {province => 'BC'});
716             print $calendar->('0701'); # "Canada Day/Fête du Canada"
717 19         54  
718              
719             # object-oriented approach
720              
721             use DateTime;
722             use Date::Holidays::CA;
723              
724             my $dhc = Date::Holidays::CA->new({ province => 'QC' });
725              
726             print 'Woot!' if $dhc->is_holiday(DateTime->today);
727              
728             my $calendar = $dhc->holidays_dt(DateTime->today->year);
729             print join keys %$calendar, "\n"; # lists holiday names for QC
730              
731             =head1 DESCRIPTION
732              
733             Date::Holidays::CA determines public holidays for Canadian jurisdictions.
734             Its interface is a superset of that provided by Date::Holidays -- read
735             on for details.
736              
737             =head1 NAME
738              
739             Date::Holidays::CA - Holidays for Canadian locales
740              
741             =head1 FUNCTIONS / METHODS
742              
743             =head2 Class Methods
744              
745             =head3 new()
746              
747             Create a new Date::Holidays::CA object. Parameters should be given as
748             a hashref of key-value pairs.
749              
750             my $dhc = Date::Holidays::CA->new(); # defaults
751              
752             my $dhc = Date::Holidays::CA->new({
753             province => 'ON', language => 'EN'
754             });
755              
756             Two parameters can be specified: B<province> and B<language>.
757              
758             =head4 Province
759              
760             =over
761              
762             =item * CA
763              
764             Canadian Federal holidays (the default).
765              
766             =item * AB
767              
768             Alberta
769              
770             =item * BC
771              
772             British Columbia
773              
774             =item * MB
775              
776             Manitoba
777              
778             =item * NB
779              
780             New Brunswick
781              
782             =item * NL
783              
784             Newfoundland & Labrador
785              
786             =item * NS
787              
788             Nova Scotia
789              
790             =item * NT
791              
792             Northwest Territories
793              
794             =item * NU
795              
796             Nunavut
797              
798             =item * ON
799              
800             Ontario
801              
802             =item * PE
803              
804             Prince Edward Island
805              
806             =item * QC
807              
808             Quebec
809              
810             =item * SK
811              
812             Saskatchewan
813              
814             =item * YT
815              
816             Yukon Territory
817              
818             =back
819              
820             =head4 Language
821              
822             =over
823              
824             =item * EN/FR
825              
826             English text followed by French text.
827              
828             =item * FR/EN
829              
830             French text followed by English text.
831              
832             =item * EN
833              
834             English text only.
835              
836             =item * FR
837              
838             French text only.
839              
840             =back
841              
842             =head2 Object Methods
843              
844             =head3 get()
845              
846             Retrieve fields of a Date::Holidays::CA object.
847              
848             $prov = $dhc->('province');
849              
850             =head3 set()
851              
852             Alter fields of a Date::Holidays::CA object. Specify parameters just
853             as with new().
854              
855             $dhc->set({province => 'QC', language => 'FR/EN'});
856              
857             =head2 Combination Methods
858              
859             These methods are callable in either object-oriented or procedural style.
860              
861             =head3 is_holiday()
862              
863             For a given year, month (1-12) and day (1-31), return 1 if the given
864             day is a holiday; 0 if not. When using procedural calling style, an
865             additional hashref of options can be specified.
866              
867             $holiday_p = is_holiday($year, $month, $day);
868              
869             $holiday_p = is_holiday($year, $month, $day, {
870             province => 'BC', language => 'EN'
871             });
872              
873             $holiday_p = $dhc->is_holiday($year, $month, $day);
874              
875             =head3 is_ca_holiday()
876              
877             Similar to C<is_holiday>. Return the name of the holiday occurring on
878             the specified date if there is one; C<undef> if there isn't.
879              
880             print $dhc->is_ca_holiday(2001, 1, 1); # "New Year's Day"
881              
882             =head3 is_holiday_dt()
883              
884             As is_holiday, but accepts a DateTime object in place of a numeric year,
885             month, and day.
886              
887             $holiday_p = is_holiday($dt, {province => 'SK', language => 'EN'});
888              
889             $holiday_p = $dhc->is_holiday($dt);
890              
891             =head3 holidays()
892              
893             For the given year, return a hashref containing all the holidays for
894             that year. The keys are the date of the holiday in C<mmdd> format
895             (eg '1225' for December 25); the values are the holiday names.
896              
897             my $calendar = holidays($year, {province => 'MB', language => 'EN'});
898             print $calendar->('0701'); # "Canada Day"
899              
900             my $calendar = $dhc->holidays($year);
901             print $calendar->('1111'); # "Remembrance Day"
902              
903             =head3 ca_holidays()
904              
905             Same as C<holidays()>.
906              
907             =head3 holidays_dt()
908              
909             Similar to C<holidays()>, after a fashion: returns a hashref with the
910             holiday names as the keys and DateTime objects as the values.
911              
912             my $calendar = $dhc->holidays_dt($year);
913              
914             =head1 SPECIFICATIONS
915              
916             The following holidays are recognized:
917              
918             =over
919              
920             =item I<New Year's Day>
921              
922             January 1.
923              
924             =item I<Islander Day>
925              
926             PE. Originally added in 2009 as the second Monday in February, this
927             holiday will be revised to the third Monday in February starting in
928             2010. I<This module shows Islander Day as falling on the third Monday>
929             -- see the I<KNOWN BUGS> section.
930              
931             =item I<Family Day / Louis Riel Day>
932              
933             The Third Monday of February is Family Day in AB, SK, and ON, and
934             Louis Riel Day in MB.
935              
936             =item I<St. Patrick's Day>
937              
938             NL. Nearest Monday to March 17.
939              
940             =item I<Good Friday>
941              
942             The Friday falling before Easter Sunday.
943              
944             =item I<Easter Monday>
945              
946             CA, QC. The Monday following Easter Sunday.
947              
948             =item I<St. Patrick's Day>
949              
950             NL. Nearest Monday to April 23.
951              
952             =item I<Victoria Day>
953              
954             Monday falling on or before May 24.
955              
956             =item I<National Aboriginal Day>
957              
958             NT. June 21.
959              
960             =item I<Saint-Jean-Baptiste Day>
961              
962             QC. June 24.
963              
964             =item I<Discovery Day>
965              
966             There are actually two holidays named "Discovery Day". Newfoundland observes
967             Discovery Day on the Monday nearest June 24, and the Yukon observes Discovery
968             Day on the third Monday of August.
969              
970             =item I<Canada Day>
971              
972             July 1.
973              
974             =item I<Nunavut Day>
975              
976             NU. July 9.
977              
978             =item I<Orangemen's Day>
979              
980             NL. Monday nearest July 12.
981              
982             =item I<Civic Holiday>
983              
984             AB, BC, MB, NB, NS, NT, NU, ON, PE, SK (that is to say, not CA, NL, QC, or YT).
985             First Monday of August.
986              
987             Different provinces call this holiday different things -- eg "BC Day" in
988             British Columbia, "Alberta Heritage Day" in Alberta, "Natal Day" in
989             Nova Scotia and PEI, and so forth.
990              
991             =item I<Labour Day>
992              
993             First Monday of September.
994              
995             =item I<Thanksgiving Day>
996              
997             Second Monday of October.
998              
999             =item I<Remembrance Day>
1000              
1001             All but ON and QC. November 11.
1002              
1003             =item I<Christmas Day>
1004              
1005             December 25.
1006              
1007             =item I<Boxing Day>
1008              
1009             CA, NL, NT, NU, ON, PE. December 26.
1010              
1011             =item I<National Day for Truth and Reconciliation>
1012              
1013             CA and PE. September 30.
1014              
1015             =back
1016              
1017             =head1 REFERENCES
1018              
1019             L<http://en.wikipedia.org/wiki/Public_holidays_in_Canada>
1020              
1021             L<http://www.craigmarlatt.com/canada/symbols_facts&lists/holidays.html>
1022              
1023             L<http://www.craigmarlatt.com/canada/symbols_facts&lists/august_holiday.html>
1024              
1025             L<http://geonames.nrcan.gc.ca/info/prov_abr_e.php> (Provincial abbreviations)
1026              
1027             A grillion government web pages listing official statutory holidays, all of
1028             which seem to have gone offline or moved.
1029              
1030             L<http://www.gov.mb.ca/labour/standards/doc,louis-riel_day,factsheet.html> (MB's Louis Riel Day)
1031              
1032             L<http://www.theguardian.pe.ca/index.cfm?sid=244766&sc=98> (PEI's Islander Day)
1033              
1034             =head1 KNOWN BUGS
1035              
1036             B<Historical holidays are not supported>; the current set of holidays
1037             will be projected into the past or future. For instance, Louis Riel Day
1038             was added as a Manitoba holiday in 2008, but if you use this module to
1039             generate a holiday list for 2007, Louis Riel Day will be present.
1040             Also, PEI's Islander Day was first observed on the second Monday of 2009,
1041             but will subsequently be observed on the third Monday of the month; this
1042             module will always show it as occurring on the third Monday.
1043             This will be addressed if there is demand to do so.
1044              
1045             B<Several lesser holidays are not yet implemented>:
1046              
1047             =over
1048              
1049             =item I<Calgary Stampede>
1050              
1051             I am told that the morning of the Stampede Parade is commonly given as
1052             a half-day holiday by employers within the city of Calgary, but I
1053             haven't been able to verify this, nor does there seem to be a way to
1054             mathematically calculate when parade day will be scheduled.
1055              
1056             =item I<St Johns Regatta Day>
1057              
1058             Regatta Day is a municipal holiday in St Johns, NL, and it is scheduled for
1059             the first Wednesday in August. However, if the weather on Quidi Vidi Lake
1060             does not look promising on Regatta morning, the event I<(and the attendant
1061             holiday)> are postponed until the next suitable day.
1062              
1063             How to programatically determine the day of this holiday has not yet been
1064             satisfactorily ascertained. L<Acme::Test::Weather|Acme::Test::Weather>
1065             has been considered.
1066              
1067             =item I<Gold Cup and Saucer Day (PEI)>
1068              
1069             Some few employees apparently get the day of the Gold Cup and Saucer
1070             harness race as a holiday, but I haven't been able to independently
1071             verify this.
1072              
1073             =item I<Construction Holiday (Quebec)>
1074              
1075             In Quebec, the vast majority of the construction industry gets the last
1076             full two weeks of July off, and it's also a popular time for other folks
1077             to book vacation. Since this technically only applies to a single
1078             industry, I haven't added it to this module, but I will if there is
1079             sufficient demand.
1080              
1081             =back
1082              
1083             =head1 HELP WANTED
1084              
1085             As you can see from the I<KNOWN BUGS> section above, our holiday structure
1086             can be fairly baroque. Different provinces and cities get different
1087             holidays; sometimes these are paid statutory holidays that are
1088             included in Employment Standards legislation; other times they are
1089             unofficial holidays that are given by convention and codified only in
1090             collective agreements and municipal by-laws. Thus, it's hard to know
1091             what's commonly considered "a holiday" in the various regions of the
1092             country without actually having lived and worked there.
1093              
1094             I only have direct experience with British Columbia and Ontario; my
1095             impression of what folks in other provinces consider to be a holiday
1096             is based on research on the WWW. I've tried to define a holiday as
1097             any day when "the majority of the workforce either get the day off
1098             (paid or unpaid) or receive pay in lieu." If the holidays list in this
1099             module doesn't accurately reflect the application of that definition
1100             to your region of Canada, I'd like to hear about it.
1101              
1102             Assistance with French translations of the holiday names and this
1103             documentation is most welcome. My French isn't all that great,
1104             but I'm happy to learn. =)
1105              
1106             Finally, I'd appreciate an email from any users of this module.
1107             I'm curious to know who has picked it up, and any feedback you might
1108             have will shape its future development.
1109              
1110             =head1 CAVEATS
1111              
1112             For reasons outlined in the two sections above, please be forewarned
1113             that what days are considered holidays may change with versions of
1114             the module.
1115              
1116             =head1 SEE ALSO
1117              
1118             =over
1119              
1120             =item Date::Holidays
1121              
1122             =item DateTime
1123              
1124             =item DateTime::Event::Easter
1125              
1126             =back
1127              
1128             =head1 AUTHOR
1129              
1130             Rick Scott <rick@cpan.org>
1131              
1132             =head1 COPYRIGHT AND LICENSE
1133              
1134             This software is copyright (c) 2006-2022 by Rick Scott.
1135              
1136             This is free software; you can redistribute it and/or modify it under
1137             the same terms as the Perl 5 programming language system itself.
1138              
1139             =cut