Date-Holidays-CA
view release on metacpan or search on metacpan
lib/Date/Holidays/CA.pm view on Meta::CPAN
sub get {
croak 'Wrong number of arguments to get()' if scalar @_ != 2;
my $self = shift;
my $field = shift;
if (exists $self->{$field}) {
return $self->{$field};
}
croak "No such field $field";
}
sub set {
croak 'Wrong number of arguments to set()' if scalar @_ != 2;
my $self = shift;
my $args_ref = shift;
while (my ($field, $value) = each %{$args_ref}) {
my $new_value;
if ($new_value = _validate($field, $value)) {
$self->{$field} = $new_value;
}
}
return 1;
}
sub is_holiday {
return ( is_ca_holiday(@_) ? 1 : 0 );
}
sub is_ca_holiday {
my $self;
$self = shift if (ref $_[0]); # invoked in OO style
my $year = shift;
my $month = shift;
my $day = shift;
my $options = shift;
if (defined $options and defined $options->{province} ) {
$self->{province} = $options->{province}
}
_assert_valid_date($year, $month, $day);
unless (defined $self) {
$self = Date::Holidays::CA->new($options);
}
my $calendar = $self->_generate_calendar($year);
# assumption: there is only one holiday for any given day.
while (my ($holiday_name, $holiday_dt) = each %$calendar) {
if ($month == $holiday_dt->month and $day == $holiday_dt->day) {
return $holiday_name;
}
}
return;
}
sub is_holiday_dt {
my ($self, $dt, $options);
my @args = map {
ref $_ eq 'DateTime' ? ($_->year, $_->month, $_->day) : $_
} @_;
return is_holiday(@args);
}
sub holidays {
my $calendar = holidays_dt(@_);
my %holidays = map {
$calendar->{$_}->strftime('%m%d') => $_
} keys %$calendar;
return \%holidays;
}
sub ca_holidays {
return holidays(@_);
}
sub holidays_dt {
my $self;
$self = shift if (ref $_[0]); # invoked in OO style
my $year = shift;
my $args_ref = shift;
unless (defined $self) {
$self = Date::Holidays::CA->new($args_ref);
}
return $self->_generate_calendar($year);
}
### internal functions
my @VALID_PROVINCES = qw{ CA AB BC MB NB NL NS NT NU ON PE QC SK YT };
my @VALID_LANGUAGES = qw{ EN/FR FR/EN EN FR };
my %VALUES_FOR = (
'PROVINCE' => \@VALID_PROVINCES,
'LANGUAGE' => \@VALID_LANGUAGES,
);
# _validate($field, $value)
#
# accepts: field name ( 'province' | 'language' )
# possible value for that field
# returns: if $value is a valid value for $field, canonicalize and return
# it (eg, upcase it).
# if $value isn't valid, throw an exception.
sub _validate {
my $field = shift;
my $value = shift;
my @valid_values = @{ $VALUES_FOR{uc($field)} };
croak "No such field $field" unless @valid_values;
foreach my $valid_value (@valid_values) {
return uc($value) if uc($value) eq $valid_value;
}
croak "$value is not a recognized setting for $field";
}
# _assert_valid_date
#
# accepts: numeric year, month, day
# returns: nothing
#
# throw an exception on invalid dates; otherwise, do nothing.
sub _assert_valid_date {
my ($year, $month, $day) = @_;
# DateTime does date validation when a DT object is created.
my $dt = DateTime->new(
year => $year, month => $month, day => $day,
);
}
# format: each holiday is listed as a triplet:
# * function that returns a DateTime object for that holiday
# * english name
# * french name
# listing the names each time makes for a verbose list with a lot of
lib/Date/Holidays/CA.pm view on Meta::CPAN
\&_family_day, 'Family Day', 'Jour de la Famille',
\&_good_friday, 'Good Friday', 'Vendredi Saint',
\&_victoria_day, 'Victoria Day', 'Fête de la Reine',
\&_canada_day, 'Canada Day', 'Fête du Canada',
\&_civic_holiday, 'Civic Holiday', 'Congé Statutaire',
\&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
\&_christmas_day, 'Christmas Day', 'Noël',
\&_boxing_day, 'Boxing Day', 'Lendemain de Noël',
],
PE => [
\&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
\&_family_day, 'Islander Day', 'Fête des Insulaires',
\&_good_friday, 'Good Friday', 'Vendredi Saint',
\&_canada_day, 'Canada Day', 'Fête du Canada',
\&_labour_day, 'Labour Day', 'Fête du Travail',
\&_truth_reconciliation_day, 'National Day for Truth and Reconciliation', 'Journée nationale de la vérité et de la réconciliation',
\&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
\&_christmas_day, 'Christmas Day', 'Noël',
],
QC => [
\&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
\&_good_friday, 'Good Friday', 'Vendredi Saint',
\&_victoria_day, 'National Patriot\'s Day', 'Journée Nationale des Patriotes / Fête de la Reine',
\&_st_john_baptiste_day, 'Saint-Jean-Baptiste Day', 'La Saint-Jean',
\&_canada_day, 'Canada Day', 'Fête du Canada',
\&_labour_day, 'Labour Day', 'Fête du Travail',
\&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
\&_christmas_day, 'Christmas Day', 'Noël',
],
SK => [
\&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
\&_family_day, 'Family Day', 'Jour de la Famille',
\&_good_friday, 'Good Friday', 'Vendredi Saint',
\&_victoria_day, 'Victoria Day', 'Fête de la Reine',
\&_canada_day, 'Canada Day', 'Fête du Canada',
\&_civic_holiday, 'Saskatchewan Day', 'Fête de la Saskatchewan',
\&_labour_day, 'Labour Day', 'Fête du Travail',
\&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
\&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
\&_christmas_day, 'Christmas Day', 'Noël',
],
YT => [
\&_new_years_day, 'New Year\'s Day', 'Jour de l\'An',
\&_good_friday, 'Good Friday', 'Vendredi Saint',
\&_victoria_day, 'Victoria Day', 'Fête de la Reine',
\&_national_aboriginal_day, 'National Aboriginal Day', 'Journée Nationale des Autochtones',
\&_canada_day, 'Canada Day', 'Fête du Canada',
\&_yt_discovery_day, 'Discovery Day', 'Jour du découverte',
\&_labour_day, 'Labour Day', 'Fête du Travail',
\&_thanksgiving_day, 'Thanksgiving Day', 'Action de Grâce',
\&_remembrance_day, 'Remembrance Day', 'Jour du Souvenir',
\&_christmas_day, 'Christmas Day', 'Noël',
],
);
# _generate_calendar
#
# accepts: numeric year
# returns: hashref (string $holiday_name => DateTime $holiday_dt)
#
# generate a holiday calendar for the specified year -- a hash mapping
# holiday names to datetime objects.
sub _generate_calendar {
my $self = shift;
my $year = shift;
my $calendar = {};
my @holiday_list = @{ $HOLIDAYS_FOR{$self->{'province'}} };
while(@holiday_list) {
my $holiday_dt = (shift @holiday_list)->($year); # fn invokation
my $name_en = shift @holiday_list;
my $name_fr = shift @holiday_list;
my $holiday_name =
$self->{'language'} eq 'EN' ? $name_en
: $self->{'language'} eq 'FR' ? $name_fr
: $self->{'language'} eq 'EN/FR' ? "$name_en/$name_fr"
: $self->{'language'} eq 'FR/EN' ? "$name_fr/$name_en"
: "$name_en/$name_fr"; # sane default, should never get here
$calendar->{$holiday_name} = $holiday_dt;
}
return $calendar;
}
### toolkit functions
# _nth_monday
#
# accepts: year, month, ordinal of which monday to find
# returns: numeric date of the requested monday
#
# find the day of week for the first day of the month,
# calculate the number of day to skip forward to hit the first monday,
# then skip forward the requisite number of weeks.
#
# in general, the number of days we need to skip forward from the
# first of the month is (target_dow - first_of_month_dow) % 7
sub _nth_monday {
my $year = shift;
my $month = shift;
my $n = shift;
my $first_of_month = DateTime->new(
year => $year,
month => $month,
day => 1,
);
my $date_of_first_monday = 1 + ( (1 - $first_of_month->dow()) % 7);
return $date_of_first_monday + 7 * ($n - 1);
}
# _nearest_monday
#
# accepts: year, month, day for a given date
# returns: day of the nearest monday to that date
sub _nearest_monday {
my $year = shift;
my $month = shift;
my $day = shift;
my $dt = DateTime->new(year => $year, month => $month, day => $day);
my $delta_days = ((4 - $dt->dow) % 7) - 3;
return $day + $delta_days;
}
# _round_to_monday
#
# accepts: year, month, day for a given date
# returns: day unless day is a Sat/Sun, then the next Mon
sub _round_to_monday {
my $year = shift;
my $month = shift;
my $day = shift;
my $dt = DateTime->new(year => $year, month => $month, day => $day);
lib/Date/Holidays/CA.pm view on Meta::CPAN
year => $year,
month => 11,
day => 11,
);
}
sub _christmas_day {
my $year = shift;
# Christmas Day is December 25th but the Holiday is normally
# observed on the Monday if it occurs on a week end day
return DateTime->new(
year => $year,
month => 12,
day => _round_to_monday($year, 12, 25)
);
}
sub _boxing_day {
my $year = shift;
# Normally the day after the Christmas Holiday, except if
# Christmas is on Friday, then our holiday is on Monday
my $result = _christmas_day($year)->add(days=>1);
return DateTime->new(
year => $year,
month => 12,
day => _round_to_monday($year, 12, $result->day())
);
}
1; # all's well
__END__
=pod
=encoding UTF-8
=head1 NAME
Date::Holidays::CA - Date::Holidays::CA determines public holidays for Canadian jurisdictions
=head1 VERSION
version 0.07
=head1 SYNOPSIS
# procedural approach
use Date::Holidays::CA qw(:all);
my ($year, $month, $day) = (localtime)[ 5, 4, 3 ];
$year += 1900;
$month += 1;
print 'Woot!' if is_holiday($year, $month, $day, {province => 'BC'});
my $calendar = holidays($year, {province => 'BC'});
#returns a hash reference
print $calendar->{'0701'}; # "Canada Day/Fête du Canada"
# object-oriented approach
use DateTime;
use Date::Holidays::CA;
my $dhc = Date::Holidays::CA->new({ province => 'QC' });
print 'Woot!' if $dhc->is_holiday(DateTime->today);
my $calendar = $dhc->holidays_dt(DateTime->today->year);
print join keys %$calendar, "\n"; # lists holiday names for QC
=head1 DESCRIPTION
Date::Holidays::CA determines public holidays for Canadian jurisdictions.
Its interface is a superset of that provided by Date::Holidays -- read
on for details.
=head1 NAME
Date::Holidays::CA - Holidays for Canadian locales
=head1 FUNCTIONS / METHODS
=head2 Class Methods
=head3 new()
Create a new Date::Holidays::CA object. Parameters should be given as
a hashref of key-value pairs.
my $dhc = Date::Holidays::CA->new(); # defaults
my $dhc = Date::Holidays::CA->new({
province => 'ON', language => 'EN'
});
Two parameters can be specified: B<province> and B<language>.
=head4 Province
=over
=item * CA
Canadian Federal holidays (the default).
=item * AB
Alberta
=item * BC
British Columbia
=item * MB
Manitoba
=item * NB
New Brunswick
=item * NL
Newfoundland & Labrador
=item * NS
Nova Scotia
lib/Date/Holidays/CA.pm view on Meta::CPAN
French text only.
=back
=head2 Object Methods
=head3 get()
Retrieve fields of a Date::Holidays::CA object.
$prov = $dhc->('province');
=head3 set()
Alter fields of a Date::Holidays::CA object. Specify parameters just
as with new().
$dhc->set({province => 'QC', language => 'FR/EN'});
=head2 Combination Methods
These methods are callable in either object-oriented or procedural style.
=head3 is_holiday()
For a given year, month (1-12) and day (1-31), return 1 if the given
day is a holiday; 0 if not. When using procedural calling style, an
additional hashref of options can be specified.
$holiday_p = is_holiday($year, $month, $day);
$holiday_p = is_holiday($year, $month, $day, {
province => 'BC', language => 'EN'
});
$holiday_p = $dhc->is_holiday($year, $month, $day);
=head3 is_ca_holiday()
Similar to C<is_holiday>. Return the name of the holiday occurring on
the specified date if there is one; C<undef> if there isn't.
print $dhc->is_ca_holiday(2001, 1, 1); # "New Year's Day"
=head3 is_holiday_dt()
As is_holiday, but accepts a DateTime object in place of a numeric year,
month, and day.
$holiday_p = is_holiday($dt, {province => 'SK', language => 'EN'});
$holiday_p = $dhc->is_holiday($dt);
=head3 holidays()
For the given year, return a hashref containing all the holidays for
that year. The keys are the date of the holiday in C<mmdd> format
(eg '1225' for December 25); the values are the holiday names.
my $calendar = holidays($year, {province => 'MB', language => 'EN'});
#returns a hash reference
print $calendar->{'0701'}; # "Canada Day"
my $calendar = $dhc->holidays($year);
#returns a hash reference
print $calendar->{'1111'}; # "Remembrance Day"
=head3 ca_holidays()
Same as C<holidays()>.
=head3 holidays_dt()
Similar to C<holidays()>, after a fashion: returns a hashref with the
holiday names as the keys and DateTime objects as the values.
my $calendar = $dhc->holidays_dt($year);
=head1 SPECIFICATIONS
The following holidays are recognized:
=over
=item I<New Year's Day>
January 1.
=item I<Islander Day>
PE. Originally added in 2009 as the second Monday in February, this
holiday will be revised to the third Monday in February starting in
2010. I<This module shows Islander Day as falling on the third Monday>
-- see the I<KNOWN BUGS> section.
=item I<Family Day / Louis Riel Day>
The Third Monday of February is Family Day in AB, SK, and ON, and
Louis Riel Day in MB.
=item I<St. Patrick's Day>
NL. Nearest Monday to March 17.
=item I<Good Friday>
The Friday falling before Easter Sunday.
=item I<Easter Monday>
CA, QC. The Monday following Easter Sunday.
=item I<St. Patrick's Day>
NL. Nearest Monday to April 23.
=item I<Victoria Day>
Monday falling on or before May 24.
=item I<National Aboriginal Day>
NT. June 21.
=item I<Saint-Jean-Baptiste Day>
QC. June 24.
=item I<Discovery Day>
There are actually two holidays named "Discovery Day". Newfoundland observes
Discovery Day on the Monday nearest June 24, and the Yukon observes Discovery
Day on the third Monday of August.
=item I<Canada Day>
July 1.
( run in 3.163 seconds using v1.01-cache-2.11-cpan-364913b4093 )