File Coverage

blib/lib/Date/Holidays/USExtended.pm
Criterion Covered Total %
statement 51 53 96.2
branch 8 12 66.6
condition 2 6 33.3
subroutine 11 11 100.0
pod 4 4 100.0
total 76 86 88.3


line stmt bran cond sub pod time code
1             package Date::Holidays::USExtended;
2             our $AUTHORITY = 'cpan:GENE';
3              
4             # ABSTRACT: Provides an extended set of United States holidays
5              
6 2     2   664846 use warnings;
  2         4  
  2         148  
7 2     2   10 use strict;
  2         3  
  2         44  
8              
9 2     2   858 use utf8;
  2         632  
  2         10  
10 2     2   915 use Date::Easter qw(easter);
  2         6228  
  2         166  
11 2     2   1804 use DateTime ();
  2         1390858  
  2         141  
12 2     2   26 use Exporter qw(import);
  2         5  
  2         2018  
13              
14             our @EXPORT = qw(is_holiday holidays);
15              
16             our $VERSION = '0.0300';
17              
18              
19             sub new {
20 1     1 1 926 my $self = shift;
21 1         5 bless \$self => $self;
22             }
23              
24              
25             sub is_holiday {
26 372     372 1 25538 my ($self, $year, $month, $day) = @_;
27 372 50 33     4429 return undef unless $year && $month && $day;
      33        
28 372         1208 my $holidays = $self->holidays($year);
29 372         1318 my $str = sprintf '%02d%02d', $month, $day;
30 372 100       3396 return $holidays->{$str} ? $holidays->{$str} : undef;
31             }
32              
33              
34             sub us_holidays {
35 375     375 1 11361 my ($self, $year) = @_;
36 375 50       951 unless ($year) {
37 0         0 $year = (localtime)[5];
38 0         0 $year += 1900;
39             }
40 375         1033 my %dom = (
41             memorial => _nth_day_of_month(-1, 1, $year, 5),
42             mothers => _nth_day_of_month(2, 7, $year, 5),
43             fathers => _nth_day_of_month(3, 7, $year, 6),
44             labor => _nth_day_of_month(1, 1, $year, 9),
45             columbus => _nth_day_of_month(2, 1, $year, 10),
46             thanksgiving => _nth_day_of_month(4, 4, $year, 11),
47             );
48             my %holidays = (
49             1 => {
50             1 => "New Year's Day",
51             15 => 'Martin Luther King Jr.',
52             },
53             2 => {
54             14 => "Valentine's Day",
55             19 => "President's Day",
56             },
57             3 => {
58             17 => "St. Patrick's Day",
59             },
60             4 => {
61             },
62             5 => {
63             5 => 'Cinco de Mayo',
64             $dom{mothers} => "Mother's Day",
65             $dom{memorial} => 'Memorial Day',
66             },
67             6 => {
68             14 => 'Flag Day',
69             $dom{fathers} => "Father's Day",
70             19 => 'Juneteenth',
71             },
72             7 => {
73             4 => 'Independence Day',
74             },
75             8 => {
76             },
77             9 => {
78             $dom{labor} => 'Labor Day',
79             },
80             10 => {
81             $dom{columbus} => "Columbus; Indigenous Peoples' Day",
82             31 => 'Halloween'
83             },
84             11 => {
85             11 => "Veteran's Day",
86 375         17189 $dom{thanksgiving} => 'Thanksgiving',
87             },
88             12 => {
89             24 => 'Christmas Eve',
90             25 => 'Christmas',
91             31 => "New Year's Eve",
92             },
93             );
94 375         1717 my ($month, $day) = easter($year);
95 375         10681 $holidays{$month}->{$day} = 'Easter';
96 375         1644 return \%holidays;
97             }
98              
99              
100             sub holidays {
101 373     373 1 3018 my ($self, $year) = @_;
102 373         1223 my $holidays = $self->us_holidays($year);
103 373         752 my %rtn;
104 373         2740 for my $month (sort { $a <=> $b } keys %$holidays) {
  11162         18844  
105 4476         6690 for my $day (sort { $a <=> $b } keys %{ $holidays->{$month} }) {
  4936         10862  
  4476         12540  
106             $rtn{ sprintf '%02d%02d', $month, $day } = $holidays->{$month}->{$day}
107 7833 50       31005 if $holidays->{$month}->{$day};
108             }
109             }
110 373         4325 return \%rtn;
111             }
112              
113             # https://stackoverflow.com/questions/18908238/perl-datetime-module-calculating-first-second-third-fouth-last-sunday-monda
114             # Here $nth is 1, 2, 3... for first, second, third, etc.
115             # Or -1, -2, -3... for last, next-to-last, etc.
116             # $dow is 1-7 for Monday-Sunday. $month is 1-12
117             sub _nth_day_of_month {
118 2250     2250   32121 my ($nth, $dow, $year, $month) = @_;
119              
120 2250         3830 my ($date, $delta);
121 2250 100       4825 if ($nth > 0) {
122             # For 1st etc. we want the last day of that week (i.e. 7, 14, 21, 28, "35")
123 1875         6105 $date = DateTime->new(year => $year, month => $month, day => 1);
124 1875         687924 $delta = $nth * 7 - 1;
125             } else {
126             # For last etc. we want the last day of the month (minus a week if next-to-last, etc)
127 375         1765 $date = DateTime->last_day_of_month(year => $year, month => $month);
128 375         148900 $delta = 7 * ($nth + 1); # $nth is negative
129             }
130              
131             # Back up to the first $dow on or before $date + $delta
132 2250         7748 $date->add(days => $delta - ($date->day_of_week + $delta - $dow) % 7);
133              
134             # If we're not in the right month, then that month doesn't have the specified date
135 2250 50       2728220 return (($date->month == $month) ? $date->day : undef);
136             }
137              
138             1;
139              
140             __END__
141              
142             =pod
143              
144             =encoding utf-8
145              
146             =head1 NAME
147              
148             Date::Holidays::USExtended - Provides an extended set of United States holidays
149              
150             =head1 VERSION
151              
152             version 0.0300
153              
154             =head1 SYNOPSIS
155              
156             # Using with the Date::Holidays module:
157             use Date::Holidays ();
158             my $dh = Date::Holidays->new(countrycode => 'USExtended', nocheck => 1);
159             print $dh->is_holiday(year => 2024, month => 1, day => 1), "\n";
160             my $h = $dh->holidays;
161              
162             # Using the Date::Holidays::USExtended module directly:
163             use Date::Holidays::USExtended ();
164             $dh = Date::Holidays::USExtended->new;
165             print $dh->is_holiday(2024, 1, 1), "\n";
166             $h = $dh->holidays;
167             $h = $dh->us_holidays(2032);
168              
169             =head1 DESCRIPTION
170              
171             C<Date::Holidays::USExtended> provides an extended set of United States holidays.
172              
173             =head1 METHODS
174              
175             =head2 new
176              
177             $dh = Date::Holidays::USExtended->new;
178              
179             Return a new C<Date::Holidays::USExtended> object.
180              
181             =head2 is_holiday
182              
183             $holiday = $dh->is_holiday($year, $month, $day);
184              
185             Takes three arguments:
186              
187             year: four digits
188             month: between 1-12
189             day: between 1-31
190              
191             Returns the name of the holiday, if one exists on that day.
192              
193             =head2 us_holidays
194              
195             $holidays = $dh->us_holidays;
196             $holidays = $dh->us_holidays($year);
197              
198             Returns a hash reference of holiday names, where the keys are by month
199             and day.
200              
201             =head2 holidays
202              
203             $holidays = $dh->holidays;
204             $holidays = $dh->holidays($year);
205              
206             Returns a hash reference of holiday names, where the keys are 4 digit
207             strings month and day.
208              
209             =head1 SEE ALSO
210              
211             L<Date::Holidays>
212              
213             L<Date::Holidays::Adapter>
214              
215             L<Date::Holidays::Adapter::USA>
216              
217             =head1 AUTHOR
218              
219             Gene Boggs <gene.boggs@gmail.com>
220              
221             =head1 COPYRIGHT AND LICENSE
222              
223             This software is Copyright (c) 2024 by Gene Boggs.
224              
225             This is free software, licensed under:
226              
227             The Artistic License 2.0 (GPL Compatible)
228              
229             =cut