File Coverage

blib/lib/Date/Holidays/NYSE.pm
Criterion Covered Total %
statement 57 58 98.2
branch 43 44 97.7
condition 145 150 96.6
subroutine 9 9 100.0
pod 4 4 100.0
total 258 265 97.3


line stmt bran cond sub pod time code
1             package Date::Holidays::NYSE;
2 21     21   2594085 use strict;
  21         60  
  21         821  
3 21     21   108 use warnings;
  21         42  
  21         1214  
4 21     21   127 use base qw{Exporter};
  21         44  
  21         3287  
5 21     21   11451 use POSIX (); #strftime to calculate wday
  21         186704  
  21         845  
6 21     21   9255 use Date::Calc 5.0 (); #to calculate Easter and thus Good Friday
  21         197731  
  21         17248  
7              
8             our @EXPORT_OK = qw(is_holiday holidays is_nyse_holiday nyse_holidays);
9             our $VERSION = '0.04';
10              
11             =head1 NAME
12              
13             Date::Holidays::NYSE - Date::Holidays Adapter for New York Stock Exchange (NYSE) holidays
14              
15             =head1 SYNOPSIS
16              
17             use Date::Holidays::NYSE qw{is_holiday};
18             my $holiday_name = is_holiday($year, $month, $day);
19              
20             =head1 DESCRIPTION
21              
22             Date::Holidays Adapter for New York Stock Exchange (NYSE) holidays
23              
24             Per https://www.nyse.com/markets/hours-calendars these are the NYSE holidays.
25              
26             New Years Day (not observed on 12/31)
27             Martin Luther King, Jr. Day
28             Washington's Birthday
29             Good Friday (falls between March 20 and April 23)
30             Memorial Day
31             Juneteenth National Independence Day (first observed in 2022)
32             Independence Day
33             Labor Day
34             Thanksgiving Day
35             Christmas Day
36              
37             =head1 METHODS
38              
39             =head2 is_holiday
40              
41             Returns a holiday name or undef given three arguments (year, month, day).
42              
43             my ($year, $month, $day) = (2023, 4, 7);
44             use Date::Holidays::NYSE qw{is_holiday};
45             my $holiday_name = is_holiday($year, $month, $day);
46             if (defined $holiday_name) {
47             print "Holiday: $holiday_name\n"; #Good Friday
48             } else {
49             print "Not a NYSE Holiday\n";
50             }
51              
52             =cut
53              
54             sub is_holiday {
55 8232     8232 1 3731855 my $year = shift;
56 8232         12216 my $month = shift;
57 8232         11085 my $day = shift;
58 8232         89014 my $wday = POSIX::strftime(qq{%w}, 0, 0, 0, $day, $month-1, $year-1900); #12:00 am #0=Sun, 1=Mon, 6=Sat
59 8232         14464 my $is_good_friday = 0;
60 8232 100 100     22144 if ($wday == 5 and (($month == 3 and $day >= 20) or ($month == 4 and $day <= 23))) { #Check Fridays in March and April for Good Friday
      100        
61             # Good Friday is the Friday before Easter Sunday.
62             # Easter can happen on any day from March 22 to April 25.
63             # thus Good Friday can happen on any day from March 20 to April 23.
64 128         601 my ($year_easter , $month_easter , $day_easter ) = Date::Calc::Easter_Sunday($year);
65 128         565 my ($year_good_friday, $month_good_friday, $day_good_friday) = Date::Calc::Add_Delta_Days($year_easter, $month_easter, $day_easter, -2);
66 128 100 66     660 if ($year == $year_good_friday and $month == $month_good_friday and $day == $day_good_friday) {
      100        
67 40         111 $is_good_friday = 1;
68             }
69             }
70              
71             #Ref: https://www.nyse.com/markets/hours-calendars
72             #Ref: https://web.archive.org/web/20101203013357/http://www.nyse.com/about/newsevents/1176373643795.html
73             #Friday 12/31 - New Years' Day (January 1) in 2011 falls on a Saturday. The rules of the applicable exchanges state that when a holiday falls on a Saturday, we observe the preceding Friday unless the Friday is the end of a monthly or yearly accounting period. In this case, Friday, December 31, 2010 is the end of both a monthly and yearly accounting period; therefore the exchanges will be open that day and the following Monday.
74 8232 100 100     175222 if ($month == 1 and $day == 1 and $wday >= 1 and $wday <= 5) { #New Year's Day on a Weekday
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    50 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 66        
    100 100        
    100 100        
    100 100        
    100 66        
    100 100        
      100        
      100        
      66        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      66        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
75 34         207 return q{New Year's Day};
76             } elsif ($month == 1 and $day == 2 and $wday == 1) { #Monday after New Year's Day
77 6         41 return q{New Year's Day Observed};
78             } elsif ($month == 1 and $day >= 15 and $day <= 21 and $wday == 1) { #Third Monday in January
79 40         202 return 'Martin Luther King, Jr. Day';
80             } elsif ($month == 2 and $day >= 15 and $day <= 21 and $wday == 1) { #Third Monday in February
81 40         188 return q{Washington's Birthday};
82             } elsif ($is_good_friday) { #Good Friday between March 20 and April 23
83 40         168 return 'Good Friday';
84             } elsif ($month == 5 and $day >= 25 and $day <= 31 and $wday == 1) { #Last Monday in May
85 40         173 return 'Memorial Day';
86             } elsif ($year >= 2022 and $month == 6 and $day == 18 and $wday == 5) { #Juneteenth on a Friday
87 0         0 return 'Juneteenth National Independence Day Observed';
88             } elsif ($year >= 2022 and $month == 6 and $day == 19 and $wday >= 1 and $wday <= 5) { #Juneteenth on a weekday
89 12         53 return 'Juneteenth National Independence Day';
90             } elsif ($year >= 2022 and $month == 6 and $day == 20 and $wday == 1) { #Juneteenth on a Monday
91 2         12 return 'Juneteenth National Independence Day Observed';
92             } elsif ($month == 7 and $day == 3 and $wday == 5) { #Friday before July 4
93 8         34 return 'Independence Day Observed';
94             } elsif ($month == 7 and $day == 4 and $wday >= 1 and $wday <= 5) { #July 4 on a weekday
95 28         119 return 'Independence Day';
96             } elsif ($month == 7 and $day == 5 and $wday == 1) { #Monday after July 4
97 4         17 return 'Independence Day Observed';
98             } elsif ($month == 9 and $day >= 1 and $day <= 7 and $wday == 1) { #First Monday in September
99 40         207 return 'Labor Day';
100             } elsif ($month == 11 and $day >= 22 and $day <= 28 and $wday == 4) { #Fourth Thursday in November.
101 40         228 return 'Thanksgiving Day';
102             } elsif ($month == 12 and $day == 24 and $wday == 5) { #Friday before December 25
103 4         23 return 'Christmas Day Observed';
104             } elsif ($month == 12 and $day == 25 and $wday >= 1 and $wday <= 5) { #December 25 on a weekday
105 30         186 return 'Christmas Day';
106             } elsif ($month == 12 and $day == 26 and $wday == 1) { #Monday after December 25
107 6         29 return 'Christmas Day Observed';
108             } elsif ($year == 2025 and $month == 1 and $day == 9) { #Day of Mourning for Jimmy Carter
109 2         13 return 'Day of Mourning for President Jimmy Carter';
110             } else {
111 7856         18339 return undef;
112             }
113             }
114              
115             =head2 is_nyse_holiday
116              
117             Wrapper around is_holiday function per the API specification. See L
118              
119             =cut
120              
121 4     4 1 5037 sub is_nyse_holiday {return is_holiday(@_)};
122              
123             =head2 holidays
124              
125             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.
126              
127             use Date::Holidays::US qw{holidays};
128             my $year = 2023;
129             my $holidays_href = holidays($year);
130             foreach my $key (sort keys %$holidays_href) { #e.g. "0101", "0619","0704"
131             my ($month, $day) = $key =~ m/\A([0-9]{2})([0-9]{2})\Z/;
132             my $name = $holidays_href->{$key};
133             print "Year: $year, Month: $month, Day: $day, Name: $name\n";
134             }
135              
136             =cut
137              
138             sub holidays {
139 22     22 1 106 my $year = shift;
140 22         82 my %holidays = ();
141 22         710 my $time = POSIX::mktime(0, 0, 0, 1, 0, $year-1900); #Jan 1st
142 22         61 while (1) {
143 8060         186259 my ($year_calculated, $month, $day) = split /-/, POSIX::strftime("%Y-%m-%d", POSIX::gmtime($time));
144 8060 100       184915 last if $year_calculated > $year;
145 8038         12941 my $date = $month . $day;
146 8038         15089 my $name = is_holiday($year, $month, $day);
147 8038 100       16574 $holidays{$date} = $name if defined($name);
148 8038         12340 $time += 86400; #Note: Not all US days have 24 hours but we are using UTC for the date component
149             }
150 22         263 return \%holidays;
151             }
152              
153             =head2 nyse_holidays
154              
155             Wrapper around holidays function per the API specification. See L
156              
157             =cut
158              
159 2     2 1 10 sub nyse_holidays {return holidays(@_)};
160              
161             =head1 SEE ALSO
162              
163             L, L
164              
165             =head1 AUTHOR
166              
167             Michael R. Davis, MRDVT
168              
169             =head1 COPYRIGHT AND LICENSE
170              
171             Copyright (C) 2023 by Michael R. Davis
172              
173             MIT License
174              
175             =cut
176              
177             1;