File Coverage

blib/lib/Date/Holidays/US.pm
Criterion Covered Total %
statement 51 63 80.9
branch 56 68 82.3
condition 236 312 75.6
subroutine 8 8 100.0
pod 4 4 100.0
total 355 455 78.0


line stmt bran cond sub pod time code
1             package Date::Holidays::US;
2 7     7   479752 use strict;
  7         68  
  7         191  
3 7     7   30 use warnings;
  7         14  
  7         186  
4 7     7   32 use base qw{Exporter};
  7         10  
  7         1096  
5 7     7   3027 use POSIX; #strftime to calculate wday
  7         41428  
  7         37  
6              
7             our @EXPORT_OK = qw(is_holiday holidays is_us_holiday us_holidays);
8              
9             our $VERSION = '0.03';
10              
11             =head1 NAME
12              
13             Date::Holidays::US - Date::Holidays Adapter for US Federal holidays
14              
15             =head1 SYNOPSIS
16              
17             use Date::Holidays::US qw{is_holiday};
18             my $holiday_name = is_holiday($year, $month, $day);
19              
20             =head1 DESCRIPTION
21              
22             Date::Holidays Adapter for US Federal holidays back to 1880 with updates from 2022.
23              
24             =head1 METHODS
25              
26             =head2 is_holiday
27              
28             Returns a holiday name or undef given three arguments (year, month, day).
29              
30             my ($year, $month, $day) = (2022, 6, 19);
31             use Date::Holidays::US qw{is_holiday};
32             my $holiday_name = is_holiday($year, $month, $day);
33             if (defined $holiday_name) {
34             print "Holiday: $holiday_name\n";
35             } else {
36             print "Not a US Holiday\n";
37             }
38              
39             =cut
40              
41             sub is_holiday {
42 1886     1886 1 3152 my $year = shift;
43 1886         2284 my $month = shift;
44 1886         2196 my $day = shift;
45 1886         39734 my $wday = POSIX::strftime(qq{%w}, 0, 0, 0, $day, $month-1, $year-1900); #12:00 am
46              
47             #Ref: https://sgp.fas.org/crs/misc/R41990.pdf
48              
49             #5 U.S. Code § 6103 - Holidays
50             #The history of federal holidays in the United States dates back to June 28, 1870
51 1886 100 66     98422 if ($year > 1870 and $month == 1 and $day == 1) {
    50 100        
    100 66        
    100 100        
    50 66        
    50 100        
    50 100        
    100 100        
    50 100        
    50 66        
    100 100        
    100 100        
    100 66        
    100 100        
    50 100        
    100 66        
    100 66        
    100 66        
    100 33        
    50 66        
    50 66        
    100 33        
    50 33        
    100 100        
    100 100        
    100 100        
    50 100        
    100 66        
    100 66        
    100 33        
    100 66        
    50 66        
      33        
      33        
      100        
      100        
      66        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      66        
      66        
      100        
      66        
      100        
      100        
      66        
      66        
      100        
      100        
      100        
      100        
      100        
      100        
      33        
      33        
      33        
      33        
      33        
      33        
      0        
      66        
      66        
      66        
      33        
      33        
      33        
      0        
      66        
      66        
      66        
      33        
      33        
      66        
      100        
      100        
      66        
      100        
      66        
      100        
      66        
      66        
      100        
      100        
      100        
      66        
      100        
      100        
      66        
      100        
      66        
      100        
      100        
      66        
      100        
      66        
52 12         75 return q{New Year's Day}; #January 1
53             } elsif ($year > 1909 and $month == 1 and $day == 2 and $wday == 1) { #Executive Order 1076 (May 22, 1909)
54 0         0 return q{New Year's Day Observed}; #Monday afer January 1
55              
56             #observed for the first time on January 20, 1986. - Pub.L. 98–399, 98 Stat. 1475, enacted November 2, 1983
57             } elsif ($year >= 1986 and $month == 1 and $day >= 15 and $day <= 21 and $wday == 1) {
58 8         36 return 'Birthday of Martin Luther King, Jr.' #the third Monday in January
59              
60             #Inauguration Day - Really only DC and few
61             } elsif ($year >= 1965 and $month == 1 and $day == 20 and $year % 4 == 1) { #5 U.S. Code 6103(c)
62 1         5 return 'Inauguration Day' #January 20 of each fourth year after 1965
63             } elsif ($year >= 1965 and $month == 1 and $day == 21 and $year % 4 == 1 and $wday == 1) { #5 U.S. Code 6103(c)
64 0         0 return 'Inauguration Day Observed' #When January 20 ... falls on Sunday, the next succeeding day ... observance
65              
66             # Washington's Birthday was celebrated on February 22 from 1879 until 1970.
67             # in 1968 the Uniform Monday Holiday Act moved it to the third Monday in February
68             # The Act was signed into law on June 1, 1968, and took effect on January 1, 1971.
69             } elsif ($year >= 1879 and $year < 1971 and $month == 2 and $day == 22) {
70 0         0 return q{Washington's Birthday}; #February 22 from 1879 until 1970
71             } elsif ($year > 1909 and $year < 1971 and $month == 2 and $day == 23 and $wday == 1) { #Executive Order 1076 (May 22, 1909)
72 0         0 return q{Washington's Birthday Observed}; #February 23 when Monday
73             } elsif ($year >= 1971 and $month == 2 and $day >= 15 and $day <= 21 and $wday == 1) { #Uniform Monday Holiday Act (June 28, 1968)
74 8         34 return q{Washington's Birthday}; #the third Monday in February
75              
76             # Memorial Day/Decoration Day
77             } elsif ($year >= 1888 and $year < 1971 and $month == 5 and $day == 30) {
78 0         0 return 'Decoration Day'; #May 30
79             } elsif ($year >= 1909 and $year < 1971 and $month == 6 and $day == 1 and $wday == 1) { #Executive Order 1076 (May 22, 1909)
80 0         0 return 'Decoration Day Observed'; #June 1st
81             } elsif ($year >= 1971 and $month == 5 and $day >= 25 and $day <= 31 and $wday == 1) { #Uniform Monday Holiday Act (June 28, 1968)
82 8         35 return 'Memorial Day'; #the last Monday in May
83              
84             #The day was first recognized as a federal holiday in June 2021, when President
85             #Joe Biden signed the Juneteenth National Independence Day Act into law.
86             } elsif ($year >= 2021 and $month == 6 and $day == 18 and $wday == 5) { #Executive Order 11582 (Feb. 11, 1971) "or any other calendar day designated as a holiday by Federal statute"
87 1         5 return 'Juneteenth National Independence Day Observed'; #Friday before June 19
88             } elsif ($year >= 2021 and $month == 6 and $day == 19) { #Juneteenth National Independence Day Act (June 17, 2021)
89 7         28 return 'Juneteenth National Independence Day'; #June 19
90             } elsif ($year >= 2021 and $month == 6 and $day == 20 and $wday == 1) { #Executive Order 11582 (Feb. 11, 1971)
91 4         78 return 'Juneteenth National Independence Day Observed'; #Monday afer June 19
92              
93             #Independence Day
94              
95             } elsif ($year >= 1971 and $month == 7 and $day == 3 and $wday == 5) { #Executive Order 11582 (Feb. 11, 1971)
96 0         0 return 'Independence Day Observed'; #Friday before July 4
97             } elsif ($year >= 1870 and $month == 7 and $day == 4) {
98 7         28 return 'Independence Day'; #July 4
99             } elsif ($year >= 1909 and $month == 7 and $day == 5 and $wday == 1) { #Executive Order 1076 (May 22, 1909)
100 1         5 return 'Independence Day Observed'; #Monday afer July 4
101              
102             ## Labor Day
103             # By 1894, thirty U.S. states were already officially celebrating Labor Day. In that year,
104             # Congress passed a bill recognizing the first Monday of September as Labor Day and making
105             # it an official federal holiday. President Grover Cleveland signed the bill into law on
106             # June 28.[15][4] The federal law, however, only made it a holiday for federal workers.
107              
108             } elsif ($year >= 1894 and $month == 9 and $day >= 1 and $day <= 7 and $wday == 1) {
109 8         32 return 'Labor Day'; #the first Monday in September
110              
111             ##Columbus Day
112             } elsif ($year >= 1971 and $month == 10 and $day >= 8 and $day <= 14 and $wday == 1) { #Uniform Monday Holiday Act
113 8         229 return 'Columbus Day'; #the second Monday in October
114              
115             ##Veterans Day (>1954)/Armistice Day (<1954)
116             #November 11 (1938 to 1970 and >1978)
117             #fourth Monday in October (1971 to 1977)
118              
119             } elsif ($year >= 1938 and $year < 1954 and $month == 11 and $day == 11) {
120 0         0 return 'Armistice Day'; #November 11
121             } elsif ($year >= 1945 and $year < 1954 and $month == 11 and $day == 12 and $wday == 1) { #Executive Order 9636 (October 3, 1945)
122 0         0 return 'Armistice Day Observed'; #Monday afer November 11
123              
124             } elsif ($year >= 1954 and $year < 1971 and $month == 11 and $day == 11) {
125 1         7 return 'Veterans Day'; #November 11
126             } elsif ($year >= 1954 and $year < 1971 and $month == 11 and $day == 12 and $wday == 1) { #Executive Order 9636 (October 3, 1945)
127 0         0 return 'Veterans Day Observed'; #Monday afer November 11
128              
129             } elsif ($year >= 1971 and $year < 1978 and $month == 10 and $day >= 22 and $day <= 28 and $wday == 1) {
130 7         39 return 'Veterans Day'; #fourth Monday in October
131              
132             } elsif ($year >= 1978 and $month == 11 and $day == 10 and $wday == 5) { #Executive Order 11582 (Feb. 11, 1971)
133 1         12 return 'Veterans Day Observed'; #Friday before November 11
134             } elsif ($year >= 1978 and $month == 11 and $day == 11) {
135 17         113 return 'Veterans Day'; #November 11
136             } elsif ($year >= 1978 and $month == 11 and $day == 12 and $wday == 1) { #Executive Order 11582 (Feb. 11, 1971)
137 0         0 return 'Veterans Day Observed'; #Monday afer November 11
138              
139             ##Thanksgiving Day
140             } elsif ($year >= 1870 and $month == 11 and $day >= 22 and $day <= 28 and $wday == 4) {
141 8         33 return 'Thanksgiving Day'; #the fourth Thursday in November.
142              
143             ##Christmas Day
144             } elsif ($year >= 1971 and $month == 12 and $day == 24 and $wday == 5) { #Executive Order 11582 (Feb. 11, 1971)
145 1         6 return 'Christmas Day Observed'; #Friday before December 25
146             } elsif ($year >= 1870 and $month == 12 and $day == 25) {
147 7         30 return 'Christmas Day'; #December 25
148             } elsif ($year >= 1909 and $month == 12 and $day == 26 and $wday == 1) { #Executive Order 1076 (May 22, 1909)
149 4         16 return 'Christmas Day Observed'; #Monday afer December 25
150              
151             } elsif ($year >= 1971 and $month == 12 and $day == 31 and $wday == 5) { #Executive Order 11582 (Feb. 11, 1971)
152 0         0 return q{New Year's Day Observed}; #Friday before January 1
153              
154             } else {
155 1767         4504 return undef;
156             }
157             }
158              
159             =head2 is_us_holiday
160              
161             Wrapper around is_holiday function per the API specification. See L
162              
163             =cut
164              
165 4     4 1 918 sub is_us_holiday {return is_holiday(@_)};
166              
167             =head2 holidays
168              
169             Returns a hash reference containing all of the holidays in the specified year. The keys for the returned hash reference are the dates where 2-digit month and 2-digit day are concatenated.
170              
171             use Date::Holidays::US qw{holidays};
172             my $year = 2022;
173             my $holidays_href = holidays($year);
174             foreach my $key (sort keys %$holidays_href) { #e.g. "0101", "0619","0704"
175             my ($month, $day) = $key =~ m/\A([0-9]{2})([0-9]{2})\Z/;
176             my $name = $holidays_href->{$key};
177             print "Year: $year, Month: $month, Day: $day, Name: $name\n";
178             }
179              
180             =cut
181              
182             sub holidays {
183 5     5 1 11 my $year = shift;
184 5         12 my %holidays = ();
185 5         159 my $time = POSIX::mktime(0, 0, 0, 1, 0, $year-1900); #Jan 1st
186 5         15 while (1) {
187 1830         35115 my ($year_calculated, $month, $day) = split /-/, POSIX::strftime("%Y-%m-%d", POSIX::gmtime($time));
188 1830 100       64648 last if $year_calculated > $year;
189 1825         3196 my $date = $month . $day;
190 1825         3842 my $name = is_holiday($year, $month, $day);
191 1825 100       3510 $holidays{$date} = $name if defined($name);
192 1825         2425 $time += 86400; #Note: Not all US days have 24 hours but we are using UTC for the date component
193             }
194 5         38 return \%holidays;
195             }
196              
197             =head2 us_holidays
198              
199             Wrapper around holidays function per the API specification. See L
200              
201             =cut
202              
203 2     2 1 10 sub us_holidays {return holidays(@_)};
204              
205             =head1 TODO
206              
207             Add Federal Holidays for President mark of respect holidays (e.g. 2007-01-02 for Gerald R. Ford, the thirty-eighth President of the United States)
208              
209             =head1 SEE ALSO
210              
211             L, L
212              
213             =head1 AUTHOR
214              
215             Michael R. Davis, MRDVT
216              
217             =head1 COPYRIGHT AND LICENSE
218              
219             Copyright (C) 2022 by Michael R. Davis
220              
221             MIT License
222              
223             =cut
224              
225             1;